@memberjunction/ng-dashboards 5.21.0 → 5.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +364 -362
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +2 -2
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +313 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +2792 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts +382 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +2683 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -0
- package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +191 -197
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/models/model-management.component.js +9 -8
- package/dist/AI/components/models/model-management.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-management.component.js +305 -299
- package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
- package/dist/AI/components/system/system-configuration.component.js +319 -313
- package/dist/AI/components/system/system-configuration.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +240 -0
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -0
- package/dist/AI/components/vectors/vector-management-resource.component.js +1767 -0
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -0
- package/dist/AI/index.d.ts +3 -0
- package/dist/AI/index.d.ts.map +1 -1
- package/dist/AI/index.js +6 -0
- package/dist/AI/index.js.map +1 -1
- package/dist/AI/services/ai-instrumentation.service.d.ts +50 -7
- package/dist/AI/services/ai-instrumentation.service.d.ts.map +1 -1
- package/dist/AI/services/ai-instrumentation.service.js +161 -193
- package/dist/AI/services/ai-instrumentation.service.js.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.js +10 -12
- package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-create-dialog.component.js +13 -19
- package/dist/APIKeys/api-key-create-dialog.component.js.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js +12 -14
- package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
- package/dist/APIKeys/api-scopes-panel.component.js +61 -68
- package/dist/APIKeys/api-scopes-panel.component.js.map +1 -1
- package/dist/APIKeys/api-usage-panel.component.js +10 -11
- package/dist/APIKeys/api-usage-panel.component.js.map +1 -1
- package/dist/Actions/components/actions-list-view.component.js +82 -96
- package/dist/Actions/components/actions-list-view.component.js.map +1 -1
- package/dist/Actions/components/actions-overview.component.js +130 -134
- package/dist/Actions/components/actions-overview.component.js.map +1 -1
- package/dist/Actions/components/categories-list-view.component.d.ts.map +1 -1
- package/dist/Actions/components/categories-list-view.component.js +40 -46
- package/dist/Actions/components/categories-list-view.component.js.map +1 -1
- package/dist/Actions/components/code-management.component.js +2 -2
- package/dist/Actions/components/code-management.component.js.map +1 -1
- package/dist/Actions/components/entity-integration.component.js +2 -2
- package/dist/Actions/components/entity-integration.component.js.map +1 -1
- package/dist/Actions/components/execution-monitoring.component.js +127 -132
- package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
- package/dist/Actions/components/executions-list-view.component.js +2 -2
- package/dist/Actions/components/executions-list-view.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-card.component.js +11 -17
- package/dist/Actions/components/explorer/action-card.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-explorer.component.js +5 -11
- package/dist/Actions/components/explorer/action-explorer.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-list-item.component.js +8 -10
- package/dist/Actions/components/explorer/action-list-item.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-toolbar.component.js +112 -133
- package/dist/Actions/components/explorer/action-toolbar.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-tree-panel.component.js +63 -83
- package/dist/Actions/components/explorer/action-tree-panel.component.js.map +1 -1
- package/dist/Actions/components/explorer/new-action-panel.component.js +17 -21
- package/dist/Actions/components/explorer/new-action-panel.component.js.map +1 -1
- package/dist/Actions/components/explorer/new-category-panel.component.js +17 -21
- package/dist/Actions/components/explorer/new-category-panel.component.js.map +1 -1
- package/dist/Actions/components/scheduled-actions.component.js +2 -2
- package/dist/Actions/components/scheduled-actions.component.js.map +1 -1
- package/dist/Actions/components/security-permissions.component.js +2 -2
- package/dist/Actions/components/security-permissions.component.js.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.d.ts +13 -5
- package/dist/ComponentStudio/component-studio-dashboard.component.d.ts.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.js +168 -145
- package/dist/ComponentStudio/component-studio-dashboard.component.js.map +1 -1
- package/dist/ComponentStudio/components/artifact-load-dialog.component.d.ts +4 -5
- package/dist/ComponentStudio/components/artifact-load-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/artifact-load-dialog.component.js +197 -200
- package/dist/ComponentStudio/components/artifact-load-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.d.ts +5 -7
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +142 -148
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/browser/component-browser.component.js +153 -166
- package/dist/ComponentStudio/components/browser/component-browser.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/code-editor-panel.component.js +15 -20
- package/dist/ComponentStudio/components/editors/code-editor-panel.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js +16 -21
- package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/requirements-editor.component.js +18 -23
- package/dist/ComponentStudio/components/editors/requirements-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/spec-editor.component.js +25 -30
- package/dist/ComponentStudio/components/editors/spec-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js +10 -11
- package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js +24 -35
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/text-import-dialog.component.js +15 -17
- package/dist/ComponentStudio/components/text-import-dialog.component.js.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.js +7 -6
- package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +6 -5
- package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-types-resource.component.js +7 -6
- package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts +11 -0
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js +57 -0
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js +9 -9
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js.map +1 -1
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +10 -2
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +35 -11
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-resource.component.d.ts +1 -0
- package/dist/DataExplorer/data-explorer-resource.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-resource.component.js +8 -4
- package/dist/DataExplorer/data-explorer-resource.component.js.map +1 -1
- package/dist/Home/home-dashboard.component.d.ts +181 -1
- package/dist/Home/home-dashboard.component.d.ts.map +1 -1
- package/dist/Home/home-dashboard.component.js +1704 -182
- package/dist/Home/home-dashboard.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +4 -4
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +246 -259
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -1
- package/dist/Integration/components/widgets/integration-card.component.js +7 -9
- package/dist/Integration/components/widgets/integration-card.component.js.map +1 -1
- package/dist/Integration/integration.module.d.ts +6 -10
- package/dist/Integration/integration.module.d.ts.map +1 -1
- package/dist/Integration/integration.module.js +12 -20
- package/dist/Integration/integration.module.js.map +1 -1
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts +106 -0
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js +607 -0
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts +126 -0
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +1086 -0
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/components/results-detail/search-result-detail.component.d.ts +56 -0
- package/dist/KnowledgeHub/components/results-detail/search-result-detail.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/results-detail/search-result-detail.component.js +291 -0
- package/dist/KnowledgeHub/components/results-detail/search-result-detail.component.js.map +1 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.d.ts +85 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.js +461 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/index.d.ts +5 -0
- package/dist/KnowledgeHub/index.d.ts.map +1 -0
- package/dist/KnowledgeHub/index.js +6 -0
- package/dist/KnowledgeHub/index.js.map +1 -0
- package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +9 -7
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +5 -4
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +10 -9
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/MCP/components/mcp-connection-dialog.component.js +141 -132
- package/dist/MCP/components/mcp-connection-dialog.component.js.map +1 -1
- package/dist/MCP/components/mcp-log-detail-panel.component.js +4 -4
- package/dist/MCP/components/mcp-log-detail-panel.component.js.map +1 -1
- package/dist/MCP/components/mcp-server-dialog.component.js +141 -128
- package/dist/MCP/components/mcp-server-dialog.component.js.map +1 -1
- package/dist/MCP/components/mcp-test-tool-dialog.component.js +210 -218
- package/dist/MCP/components/mcp-test-tool-dialog.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +2 -2
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/MCP/mcp.module.d.ts +6 -9
- package/dist/MCP/mcp.module.d.ts.map +1 -1
- package/dist/MCP/mcp.module.js +20 -22
- package/dist/MCP/mcp.module.js.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +5 -1
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-activity.component.js +5 -4
- package/dist/Scheduling/components/scheduling-activity.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-jobs.component.js +6 -5
- package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-overview.component.js +93 -92
- package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
- package/dist/SystemDiagnostics/system-diagnostics.component.d.ts.map +1 -1
- package/dist/SystemDiagnostics/system-diagnostics.component.js +1 -0
- package/dist/SystemDiagnostics/system-diagnostics.component.js.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab-resource.component.d.ts +7 -1
- package/dist/Testing/components/testing-dashboard-tab-resource.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab-resource.component.js +63 -8
- package/dist/Testing/components/testing-dashboard-tab-resource.component.js.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab.component.d.ts +9 -1
- package/dist/Testing/components/testing-dashboard-tab.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab.component.js +109 -62
- package/dist/Testing/components/testing-dashboard-tab.component.js.map +1 -1
- package/dist/Testing/components/testing-explorer.component.d.ts +2 -1
- package/dist/Testing/components/testing-explorer.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-explorer.component.js +241 -200
- package/dist/Testing/components/testing-explorer.component.js.map +1 -1
- package/dist/Testing/components/testing-runs-resource.component.d.ts +7 -1
- package/dist/Testing/components/testing-runs-resource.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-runs-resource.component.js +63 -8
- package/dist/Testing/components/testing-runs-resource.component.js.map +1 -1
- package/dist/Testing/components/testing-runs.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-runs.component.js +7 -5
- package/dist/Testing/components/testing-runs.component.js.map +1 -1
- package/dist/Testing/testing-dashboard.component.d.ts +9 -1
- package/dist/Testing/testing-dashboard.component.d.ts.map +1 -1
- package/dist/Testing/testing-dashboard.component.js +122 -54
- package/dist/Testing/testing-dashboard.component.js.map +1 -1
- package/dist/actions-dashboards.module.d.ts +8 -13
- package/dist/actions-dashboards.module.d.ts.map +1 -1
- package/dist/actions-dashboards.module.js +6 -27
- package/dist/actions-dashboards.module.js.map +1 -1
- package/dist/ai-dashboards.module.d.ts +14 -11
- package/dist/ai-dashboards.module.d.ts.map +1 -1
- package/dist/ai-dashboards.module.js +58 -44
- package/dist/ai-dashboards.module.js.map +1 -1
- package/dist/communication-dashboards.module.d.ts +4 -8
- package/dist/communication-dashboards.module.d.ts.map +1 -1
- package/dist/communication-dashboards.module.js +0 -19
- package/dist/communication-dashboards.module.js.map +1 -1
- package/dist/component-studio-dashboards.module.d.ts +7 -11
- package/dist/component-studio-dashboards.module.d.ts.map +1 -1
- package/dist/component-studio-dashboards.module.js +22 -34
- package/dist/component-studio-dashboards.module.js.map +1 -1
- package/dist/core-dashboards.module.d.ts +13 -18
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +18 -31
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/credentials-dashboards.module.d.ts +5 -8
- package/dist/credentials-dashboards.module.d.ts.map +1 -1
- package/dist/credentials-dashboards.module.js +3 -19
- package/dist/credentials-dashboards.module.js.map +1 -1
- package/dist/data-explorer-dashboards.module.d.ts +7 -13
- package/dist/data-explorer-dashboards.module.d.ts.map +1 -1
- package/dist/data-explorer-dashboards.module.js +0 -27
- package/dist/data-explorer-dashboards.module.js.map +1 -1
- package/dist/lists-dashboards.module.d.ts +5 -8
- package/dist/lists-dashboards.module.d.ts.map +1 -1
- package/dist/lists-dashboards.module.js +3 -19
- package/dist/lists-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +5 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -1
- package/dist/public-api.js.map +1 -1
- package/dist/scheduling-dashboards.module.d.ts +6 -10
- package/dist/scheduling-dashboards.module.d.ts.map +1 -1
- package/dist/scheduling-dashboards.module.js +3 -23
- package/dist/scheduling-dashboards.module.js.map +1 -1
- package/dist/testing-dashboards.module.d.ts +7 -12
- package/dist/testing-dashboards.module.d.ts.map +1 -1
- package/dist/testing-dashboards.module.js +4 -27
- package/dist/testing-dashboards.module.js.map +1 -1
- package/package.json +47 -53
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-instrumentation.service.js","sourceRoot":"","sources":["../../../src/AI/services/ai-instrumentation.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject, from, combineLatest } from 'rxjs';\nimport { switchMap, shareReplay, tap } from 'rxjs/operators';\nimport { RunView, Metadata } from '@memberjunction/core';\nimport { MJAIPromptRunEntityExtended, MJAIAgentRunEntityExtended } from '@memberjunction/ai-core-plus';\n\nexport interface DashboardKPIs {\n totalExecutions: number;\n activeExecutions: number;\n totalCost: number;\n costCurrency: string;\n avgExecutionTime: number;\n successRate: number;\n totalTokens: number;\n costPerToken: number;\n topModel: string;\n topAgent: string;\n errorRate: number;\n dailyCostBurn: number;\n}\n\nexport interface TrendData {\n timestamp: Date;\n executions: number;\n cost: number;\n tokens: number;\n avgTime: number;\n errors: number;\n}\n\nexport interface LiveExecution {\n id: string;\n type: 'prompt' | 'agent';\n name: string;\n status: 'running' | 'completed' | 'failed';\n startTime: Date;\n duration?: number;\n cost?: number;\n tokens?: number;\n progress?: number;\n}\n\nexport interface ExecutionDetails {\n id: string;\n type: 'prompt' | 'agent';\n name: string;\n status: string;\n startTime: Date;\n endTime?: Date;\n cost: number;\n tokens: number;\n success: boolean;\n errorMessage?: string;\n parentId?: string;\n children: ExecutionDetails[];\n model?: string;\n vendor?: string;\n}\n\nexport interface ChartData {\n executionTrends: TrendData[];\n costByModel: { model: string; cost: number; tokens: number }[];\n performanceMatrix: { agent: string; model: string; avgTime: number; successRate: number }[];\n tokenEfficiency: { inputTokens: number; outputTokens: number; cost: number; model: string }[];\n}\n\n@Injectable({\n providedIn: 'root'\n})\nexport class AIInstrumentationService {\n private readonly _dateRange$ = new BehaviorSubject<{ start: Date; end: Date }>({\n start: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24 hours\n end: new Date()\n });\n \n private readonly _refreshTrigger$ = new BehaviorSubject<number>(0);\n private readonly _isLoading$ = new BehaviorSubject<boolean>(false);\n private readonly metadata = new Metadata();\n \n // Expose loading state as observable\n readonly isLoading$ = this._isLoading$.asObservable();\n\n constructor() {}\n\n // Main data streams - trigger on refresh or date range change\n readonly kpis$ = combineLatest([this._refreshTrigger$, this._dateRange$]).pipe(\n tap(() => this._isLoading$.next(true)),\n switchMap(() => from(this.loadKPIs())),\n tap(() => this.checkLoadingComplete()),\n shareReplay(1)\n );\n \n readonly trends$ = combineLatest([this._refreshTrigger$, this._dateRange$]).pipe(\n tap(() => this._isLoading$.next(true)),\n switchMap(() => from(this.loadTrends())),\n tap(() => this.checkLoadingComplete()),\n shareReplay(1)\n );\n \n readonly liveExecutions$ = combineLatest([this._refreshTrigger$, this._dateRange$]).pipe(\n tap(() => this._isLoading$.next(true)),\n switchMap(() => from(this.loadLiveExecutions())),\n tap(() => this.checkLoadingComplete()),\n shareReplay(1)\n );\n \n readonly chartData$ = combineLatest([this._refreshTrigger$, this._dateRange$]).pipe(\n tap(() => this._isLoading$.next(true)),\n switchMap(() => from(this.loadChartData())),\n tap(() => this.checkLoadingComplete()),\n shareReplay(1)\n );\n \n private loadingCount = 0;\n \n private checkLoadingComplete(): void {\n // Simple mechanism to track when all streams have completed\n setTimeout(() => {\n this._isLoading$.next(false);\n }, 100);\n }\n\n setDateRange(start: Date, end: Date): void {\n this._dateRange$.next({ start, end });\n // No need to manually refresh - streams now react to date range changes\n }\n \n refresh(): void {\n this._refreshTrigger$.next(this._refreshTrigger$.value + 1);\n }\n\n private async loadKPIs(): Promise<DashboardKPIs> {\n const { start, end } = this._dateRange$.value;\n const rv = new RunView();\n \n // Use RunViews to batch the queries\n const [promptResults, agentResults] = await rv.RunViews<any>([\n {\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `RunAt >= '${start.toISOString()}' AND RunAt <= '${end.toISOString()}'` \n },\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `StartedAt >= '${start.toISOString()}' AND StartedAt <= '${end.toISOString()}'` \n }\n ]);\n\n const promptRuns = promptResults.Results as MJAIPromptRunEntityExtended[];\n const agentRuns = agentResults.Results as MJAIAgentRunEntityExtended[];\n\n // Calculate KPIs\n const totalExecutions = promptRuns.length + agentRuns.length;\n const activeExecutions = this.countActiveExecutions(promptRuns, agentRuns);\n \n const totalCost = this.sumCosts(promptRuns, agentRuns);\n const totalTokens = this.sumTokens(promptRuns, agentRuns);\n const avgExecutionTime = this.calculateAverageExecutionTime(promptRuns, agentRuns);\n const successRate = this.calculateSuccessRate(promptRuns, agentRuns);\n const errorRate = 1 - successRate;\n \n const costPerToken = totalTokens > 0 ? totalCost / totalTokens : 0;\n const dailyCostBurn = this.calculateDailyCostBurn(promptRuns, agentRuns);\n \n const topModel = await this.getTopModel(promptRuns);\n const topAgent = await this.getTopAgent(agentRuns);\n\n return {\n totalExecutions,\n activeExecutions,\n totalCost,\n costCurrency: 'USD',\n avgExecutionTime,\n successRate,\n totalTokens,\n costPerToken,\n topModel,\n topAgent,\n errorRate,\n dailyCostBurn\n };\n }\n\n private async loadTrends(): Promise<TrendData[]> {\n const { start, end } = this._dateRange$.value;\n const hourlyBuckets = this.createHourlyBuckets(start, end);\n \n // Calculate bucket size based on the number of buckets\n const duration = end.getTime() - start.getTime();\n const hours = duration / (1000 * 60 * 60);\n let bucketSizeMs: number;\n if (hours <= 24) {\n bucketSizeMs = 60 * 60 * 1000; // 1 hour\n } else if (hours <= 24 * 7) {\n bucketSizeMs = 4 * 60 * 60 * 1000; // 4 hours\n } else {\n bucketSizeMs = 24 * 60 * 60 * 1000; // 24 hours\n }\n \n // Load all data in a single query instead of per-bucket\n const rv = new RunView();\n const [promptResults, agentResults] = await rv.RunViews<any>([\n {\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `RunAt >= '${start.toISOString()}' AND RunAt <= '${end.toISOString()}'` \n },\n {\n EntityName: 'MJ: AI Agent Runs', \n ExtraFilter: `StartedAt >= '${start.toISOString()}' AND StartedAt <= '${end.toISOString()}'` \n }\n ]);\n\n const allPromptRuns = promptResults.Results as MJAIPromptRunEntityExtended[];\n const allAgentRuns = agentResults.Results as MJAIAgentRunEntityExtended[];\n \n // Now aggregate data into buckets on the client side\n const trends: TrendData[] = [];\n \n for (const bucket of hourlyBuckets) {\n const bucketStart = bucket;\n const bucketEnd = new Date(bucket.getTime() + bucketSizeMs);\n \n // Filter runs for this bucket\n const promptRuns = allPromptRuns.filter(r => {\n const runAt = new Date(r.RunAt);\n return runAt >= bucketStart && runAt < bucketEnd;\n });\n \n const agentRuns = allAgentRuns.filter(r => {\n const startedAt = new Date(r.StartedAt);\n return startedAt >= bucketStart && startedAt < bucketEnd;\n });\n\n trends.push({\n timestamp: bucket,\n executions: promptRuns.length + agentRuns.length,\n cost: this.sumCosts(promptRuns, agentRuns),\n tokens: this.sumTokens(promptRuns, agentRuns),\n avgTime: this.calculateAverageExecutionTime(promptRuns, agentRuns),\n errors: this.countErrors(promptRuns, agentRuns)\n });\n }\n\n return trends;\n }\n\n private async loadLiveExecutions(): Promise<LiveExecution[]> {\n const now = new Date();\n const recentTime = new Date(now.getTime() - 5 * 60 * 1000);\n \n const rv = new RunView();\n const [promptResults, agentResults] = await rv.RunViews<any>([\n {\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `RunAt >= '${recentTime.toISOString()}'`,\n OrderBy: 'RunAt DESC' \n },\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `StartedAt >= '${recentTime.toISOString()}'`,\n OrderBy: 'StartedAt DESC' \n }\n ]);\n\n const promptRuns = promptResults.Results as MJAIPromptRunEntityExtended[];\n const agentRuns = agentResults.Results as MJAIAgentRunEntityExtended[];\n\n const liveExecutions: LiveExecution[] = [];\n\n // Process prompt runs - they already have the prompt name in the Prompt field\n for (const run of promptRuns) {\n const isRunning = !run.CompletedAt && run.Success !== false;\n const duration = run.CompletedAt ? \n new Date(run.CompletedAt).getTime() - new Date(run.RunAt).getTime() : \n now.getTime() - new Date(run.RunAt).getTime();\n\n liveExecutions.push({\n id: run.ID,\n type: 'prompt',\n name: run.Prompt || 'Unnamed Prompt', // Use the Prompt field directly\n status: isRunning ? 'running' : (run.Success ? 'completed' : 'failed'),\n startTime: new Date(run.RunAt),\n duration: duration,\n cost: run.Cost || 0,\n tokens: run.TokensUsed || 0,\n progress: isRunning ? Math.min(90, (duration / 30000) * 100) : 100\n });\n }\n\n // Process agent runs - they already have the agent name in the Agent field\n for (const run of agentRuns) {\n const isRunning = run.Status === 'Running';\n const duration = run.CompletedAt ? \n new Date(run.CompletedAt).getTime() - new Date(run.StartedAt).getTime() : \n now.getTime() - new Date(run.StartedAt).getTime();\n\n liveExecutions.push({\n id: run.ID,\n type: 'agent',\n name: run.Agent || 'Unnamed Agent', // Use the Agent field directly\n status: run.Status.toLowerCase() as 'running' | 'completed' | 'failed',\n startTime: new Date(run.StartedAt),\n duration: duration,\n cost: run.TotalCost || 0,\n tokens: run.TotalTokensUsed || 0,\n progress: isRunning ? Math.min(90, (duration / 60000) * 100) : 100\n });\n }\n\n return liveExecutions.sort((a, b) => b.startTime.getTime() - a.startTime.getTime());\n }\n\n private async loadChartData(): Promise<ChartData> {\n const { start, end } = this._dateRange$.value;\n const dateFilter = `RunAt >= '${start.toISOString()}' AND RunAt <= '${end.toISOString()}'`;\n\n const promptRv = new RunView();\n const promptResults = await promptRv.RunView<MJAIPromptRunEntityExtended>({\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: dateFilter \n });\n\n const promptRuns = promptResults.Results;\n const executionTrends = await this.loadTrends();\n const costByModel = await this.analyzeCostByModel(promptRuns);\n const performanceMatrix = await this.analyzePerformanceMatrix(promptRuns);\n const tokenEfficiency = await this.analyzeTokenEfficiency(promptRuns);\n\n return {\n executionTrends,\n costByModel,\n performanceMatrix,\n tokenEfficiency\n };\n }\n\n // Helper methods\n private countActiveExecutions(promptRuns: MJAIPromptRunEntityExtended[], agentRuns: MJAIAgentRunEntityExtended[]): number {\n const activePrompts = promptRuns.filter(r => !r.CompletedAt && r.Success !== false).length;\n const activeAgents = agentRuns.filter(r => r.Status === 'Running').length;\n return activePrompts + activeAgents;\n }\n\n private sumCosts(promptRuns: MJAIPromptRunEntityExtended[], agentRuns: MJAIAgentRunEntityExtended[]): number {\n const promptCost = promptRuns.reduce((sum, r) => sum + (r.Cost || 0), 0);\n const agentCost = agentRuns.reduce((sum, r) => sum + (r.TotalCost || 0), 0);\n return promptCost + agentCost;\n }\n\n private sumTokens(promptRuns: MJAIPromptRunEntityExtended[], agentRuns: MJAIAgentRunEntityExtended[]): number {\n const promptTokens = promptRuns.reduce((sum, r) => sum + (r.TokensUsed || 0), 0);\n const agentTokens = agentRuns.reduce((sum, r) => sum + (r.TotalTokensUsed || 0), 0);\n return promptTokens + agentTokens;\n }\n\n private calculateAverageExecutionTime(promptRuns: MJAIPromptRunEntityExtended[], agentRuns: MJAIAgentRunEntityExtended[]): number {\n const promptTimes = promptRuns\n .filter(r => r.ExecutionTimeMS)\n .map(r => r.ExecutionTimeMS!);\n \n const agentTimes = agentRuns\n .filter(r => r.StartedAt && r.CompletedAt)\n .map(r => new Date(r.CompletedAt!).getTime() - new Date(r.StartedAt).getTime());\n\n const allTimes = [...promptTimes, ...agentTimes];\n return allTimes.length > 0 ? allTimes.reduce((sum, time) => sum + time, 0) / allTimes.length : 0;\n }\n\n private calculateSuccessRate(promptRuns: MJAIPromptRunEntityExtended[], agentRuns: MJAIAgentRunEntityExtended[]): number {\n const totalExecutions = promptRuns.length + agentRuns.length;\n if (totalExecutions === 0) return 1;\n\n const successfulPrompts = promptRuns.filter(r => r.Success).length;\n const successfulAgents = agentRuns.filter(r => r.Success).length;\n \n return (successfulPrompts + successfulAgents) / totalExecutions;\n }\n\n private countErrors(promptRuns: MJAIPromptRunEntityExtended[], agentRuns: MJAIAgentRunEntityExtended[]): number {\n const promptErrors = promptRuns.filter(r => !r.Success).length;\n const agentErrors = agentRuns.filter(r => !r.Success).length;\n return promptErrors + agentErrors;\n }\n\n private calculateDailyCostBurn(promptRuns: MJAIPromptRunEntityExtended[], agentRuns: MJAIAgentRunEntityExtended[]): number {\n const now = new Date();\n const dayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n \n const todayPrompts = promptRuns.filter(r => new Date(r.RunAt) >= dayStart);\n const todayAgents = agentRuns.filter(r => new Date(r.StartedAt) >= dayStart);\n \n return this.sumCosts(todayPrompts, todayAgents);\n }\n\n private createHourlyBuckets(start: Date, end: Date): Date[] {\n const buckets: Date[] = [];\n const current = new Date(start);\n const duration = end.getTime() - start.getTime();\n const hours = duration / (1000 * 60 * 60);\n \n // Determine bucket size based on duration\n let bucketSize: number;\n if (hours <= 24) {\n // For up to 24 hours, use hourly buckets\n bucketSize = 1;\n current.setMinutes(0, 0, 0);\n } else if (hours <= 24 * 7) {\n // For up to 7 days, use 4-hour buckets\n bucketSize = 4;\n current.setHours(Math.floor(current.getHours() / 4) * 4, 0, 0, 0);\n } else {\n // For more than 7 days, use daily buckets\n bucketSize = 24;\n current.setHours(0, 0, 0, 0);\n }\n \n while (current < end) {\n buckets.push(new Date(current));\n current.setHours(current.getHours() + bucketSize);\n }\n \n return buckets;\n }\n\n\n\n private async getTopModel(promptRuns: MJAIPromptRunEntityExtended[]): Promise<string> {\n const modelCounts = new Map<string, number>();\n const modelNames = new Map<string, string>();\n \n // Count models and track their names from the Model field\n for (const run of promptRuns) {\n if (run.ModelID && run.Model) {\n const count = modelCounts.get(run.ModelID) || 0;\n modelCounts.set(run.ModelID, count + 1);\n modelNames.set(run.ModelID, run.Model);\n }\n }\n\n if (modelCounts.size === 0) return 'N/A';\n\n const topModelId = Array.from(modelCounts.entries())\n .sort(([,a], [,b]) => b - a)[0][0];\n\n return modelNames.get(topModelId) || 'Unknown Model';\n }\n\n private async getTopAgent(agentRuns: MJAIAgentRunEntityExtended[]): Promise<string> {\n const agentCounts = new Map<string, number>();\n const agentNames = new Map<string, string>();\n \n // Count agents and track their names from the Agent field\n for (const run of agentRuns) {\n if (run.AgentID && run.Agent) {\n const count = agentCounts.get(run.AgentID) || 0;\n agentCounts.set(run.AgentID, count + 1);\n agentNames.set(run.AgentID, run.Agent);\n }\n }\n\n if (agentCounts.size === 0) return 'N/A';\n\n const topAgentId = Array.from(agentCounts.entries())\n .sort(([,a], [,b]) => b - a)[0][0];\n\n return agentNames.get(topAgentId) || 'Unknown Agent';\n }\n\n private async analyzeCostByModel(promptRuns: MJAIPromptRunEntityExtended[]): Promise<{ model: string; cost: number; tokens: number }[]> {\n const modelStats = new Map<string, { cost: number; tokens: number; name: string }>();\n\n for (const run of promptRuns) {\n if (run.ModelID && run.Model) {\n const existing = modelStats.get(run.ModelID) || { cost: 0, tokens: 0, name: run.Model };\n existing.cost += run.Cost || 0;\n existing.tokens += run.TokensUsed || 0;\n modelStats.set(run.ModelID, existing);\n }\n }\n\n const results = [];\n for (const [modelId, stats] of modelStats.entries()) {\n results.push({\n model: stats.name,\n cost: stats.cost,\n tokens: stats.tokens\n });\n }\n\n return results.sort((a, b) => b.cost - a.cost);\n }\n\n private async analyzePerformanceMatrix(promptRuns: MJAIPromptRunEntityExtended[]): Promise<{ agent: string; model: string; avgTime: number; successRate: number }[]> {\n const combinations = new Map<string, { times: number[]; successes: number; total: number; agentName: string; modelName: string }>();\n\n for (const run of promptRuns) {\n if (run.AgentID && run.ModelID && run.ExecutionTimeMS) {\n const key = `${run.AgentID}:${run.ModelID}`;\n const existing = combinations.get(key) || { \n times: [], \n successes: 0, \n total: 0,\n agentName: run.Agent || 'Unknown Agent',\n modelName: run.Model || 'Unknown Model'\n };\n \n existing.times.push(run.ExecutionTimeMS);\n existing.total += 1;\n if (run.Success) existing.successes += 1;\n \n combinations.set(key, existing);\n }\n }\n\n const results = [];\n for (const [key, data] of combinations.entries()) {\n results.push({\n agent: data.agentName,\n model: data.modelName,\n avgTime: data.times.reduce((sum, time) => sum + time, 0) / data.times.length,\n successRate: data.successes / data.total\n });\n }\n\n return results;\n }\n\n private async analyzeTokenEfficiency(promptRuns: MJAIPromptRunEntityExtended[]): Promise<{ inputTokens: number; outputTokens: number; cost: number; model: string }[]> {\n const modelEfficiency = new Map<string, { input: number; output: number; cost: number; name: string }>();\n\n for (const run of promptRuns) {\n if (run.ModelID && run.Model && run.TokensPrompt && run.TokensCompletion) {\n const existing = modelEfficiency.get(run.ModelID) || { input: 0, output: 0, cost: 0, name: run.Model };\n existing.input += run.TokensPrompt;\n existing.output += run.TokensCompletion;\n existing.cost += run.Cost || 0;\n modelEfficiency.set(run.ModelID, existing);\n }\n }\n\n const results = [];\n for (const [modelId, data] of modelEfficiency.entries()) {\n results.push({\n inputTokens: data.input,\n outputTokens: data.output,\n cost: data.cost,\n model: data.name\n });\n }\n\n return results;\n }\n\n\n async getExecutionDetails(executionId: string, type: 'prompt' | 'agent'): Promise<ExecutionDetails | null> {\n try {\n if (type === 'prompt') {\n return await this.getPromptExecutionDetails(executionId);\n } else {\n return await this.getAgentExecutionDetails(executionId);\n }\n } catch (error) {\n console.error('Error loading execution details:', error);\n return null;\n }\n }\n\n private async getPromptExecutionDetails(promptRunId: string): Promise<ExecutionDetails> {\n const rv = new RunView();\n const result = await rv.RunView<MJAIPromptRunEntityExtended>({\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `ID = '${promptRunId}'` \n });\n\n const run = result.Results[0];\n if (!run) throw new Error('Prompt run not found');\n\n const childrenResult = await rv.RunView<MJAIPromptRunEntityExtended>({\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `ParentID = '${promptRunId}'` \n });\n\n const children = await Promise.all(\n childrenResult.Results.map(child => this.getPromptExecutionDetails(child.ID))\n );\n\n return {\n id: run.ID,\n type: 'prompt',\n name: run.Prompt || 'Unnamed Prompt',\n status: run.Success ? 'completed' : 'failed',\n startTime: new Date(run.RunAt),\n endTime: run.CompletedAt ? new Date(run.CompletedAt) : undefined,\n cost: run.Cost || 0,\n tokens: run.TokensUsed || 0,\n success: run.Success || false,\n errorMessage: run.ErrorMessage || undefined,\n parentId: run.ParentID || undefined,\n children,\n model: run.Model || undefined\n };\n }\n\n private async getAgentExecutionDetails(agentRunId: string): Promise<ExecutionDetails> {\n const rv = new RunView();\n const result = await rv.RunView<MJAIAgentRunEntityExtended>({\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `ID = '${agentRunId}'` \n });\n\n const run = result.Results[0];\n if (!run) throw new Error('Agent run not found');\n\n const childrenResult = await rv.RunView<MJAIAgentRunEntityExtended>({\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `ParentRunID = '${agentRunId}'` \n });\n\n const children = await Promise.all(\n childrenResult.Results.map(child => this.getAgentExecutionDetails(child.ID))\n );\n\n return {\n id: run.ID,\n type: 'agent',\n name: run.Agent || 'Unnamed Agent',\n status: run.Status.toLowerCase(),\n startTime: new Date(run.StartedAt),\n endTime: run.CompletedAt ? new Date(run.CompletedAt) : undefined,\n cost: run.TotalCost || 0,\n tokens: run.TotalTokensUsed || 0,\n success: run.Success || false,\n errorMessage: run.ErrorMessage || undefined,\n parentId: run.ParentRunID || undefined,\n children\n };\n }\n}"]}
|
|
1
|
+
{"version":3,"file":"ai-instrumentation.service.js","sourceRoot":"","sources":["../../../src/AI/services/ai-instrumentation.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject, from, combineLatest } from 'rxjs';\nimport { switchMap, shareReplay, tap, map } from 'rxjs/operators';\nimport { RunView } from '@memberjunction/core';\n\n/**\n * Lightweight record types for dashboard aggregation.\n * We use ResultType: 'simple' with explicit Fields to avoid pulling\n * large text columns (InputData, OutputData, etc.) that can blow up\n * the GraphQL response beyond V8's string limit.\n */\nexport interface PromptRunRecord {\n ID: string;\n RunAt: string;\n CompletedAt: string | null;\n Success: boolean;\n Cost: number | null;\n TokensUsed: number | null;\n TokensPrompt: number | null;\n TokensCompletion: number | null;\n ExecutionTimeMS: number | null;\n ModelID: string | null;\n Model: string | null;\n AgentID: string | null;\n Agent: string | null;\n Prompt: string | null;\n ErrorMessage: string | null;\n}\n\nexport interface AgentRunRecord {\n ID: string;\n StartedAt: string;\n CompletedAt: string | null;\n Status: string;\n Success: boolean;\n TotalCost: number | null;\n TotalTokensUsed: number | null;\n AgentID: string | null;\n Agent: string | null;\n ErrorMessage: string | null;\n}\n\n/** Fields to request for prompt runs — only what the dashboard needs for aggregation */\nconst PROMPT_RUN_FIELDS = [\n 'ID', 'RunAt', 'CompletedAt', 'Success', 'Cost', 'TokensUsed',\n 'TokensPrompt', 'TokensCompletion', 'ExecutionTimeMS',\n 'ModelID', 'Model', 'AgentID', 'Agent', 'Prompt', 'ErrorMessage'\n];\n\n/** Fields to request for agent runs — only what the dashboard needs for aggregation */\nconst AGENT_RUN_FIELDS = [\n 'ID', 'StartedAt', 'CompletedAt', 'Status', 'Success',\n 'TotalCost', 'TotalTokensUsed', 'AgentID', 'Agent', 'ErrorMessage'\n];\n\nexport interface DashboardKPIs {\n totalExecutions: number;\n activeExecutions: number;\n totalCost: number;\n costCurrency: string;\n avgExecutionTime: number;\n successRate: number;\n totalTokens: number;\n costPerToken: number;\n topModel: string;\n topAgent: string;\n errorRate: number;\n dailyCostBurn: number;\n}\n\nexport interface TrendData {\n timestamp: Date;\n executions: number;\n cost: number;\n tokens: number;\n avgTime: number;\n errors: number;\n}\n\nexport interface LiveExecution {\n id: string;\n type: 'prompt' | 'agent';\n name: string;\n status: 'running' | 'completed' | 'failed';\n startTime: Date;\n duration?: number;\n cost?: number;\n tokens?: number;\n progress?: number;\n}\n\nexport interface ExecutionDetails {\n id: string;\n type: 'prompt' | 'agent';\n name: string;\n status: string;\n startTime: Date;\n endTime?: Date;\n cost: number;\n tokens: number;\n success: boolean;\n errorMessage?: string;\n parentId?: string;\n children: ExecutionDetails[];\n model?: string;\n vendor?: string;\n}\n\nexport interface ChartData {\n executionTrends: TrendData[];\n costByModel: { model: string; cost: number; tokens: number }[];\n performanceMatrix: { agent: string; model: string; avgTime: number; successRate: number }[];\n tokenEfficiency: { inputTokens: number; outputTokens: number; cost: number; model: string }[];\n}\n\n/** Internal shape for the single data load that all derived streams share */\ninterface DashboardRawData {\n promptRuns: PromptRunRecord[];\n agentRuns: AgentRunRecord[];\n livePromptRuns: PromptRunRecord[];\n liveAgentRuns: AgentRunRecord[];\n}\n\n@Injectable({\n providedIn: 'root'\n})\nexport class AIInstrumentationService {\n private readonly _dateRange$ = new BehaviorSubject<{ start: Date; end: Date }>({\n start: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24 hours\n end: new Date()\n });\n\n private readonly _refreshTrigger$ = new BehaviorSubject<number>(0);\n private readonly _isLoading$ = new BehaviorSubject<boolean>(false);\n\n // Expose loading state as observable\n readonly isLoading$ = this._isLoading$.asObservable();\n\n constructor() {}\n\n /**\n * Single data load: fetches prompt runs and agent runs ONCE per refresh/date-range change.\n * All downstream streams derive from this shared dataset.\n */\n private readonly rawData$ = combineLatest([this._refreshTrigger$, this._dateRange$]).pipe(\n tap(() => this._isLoading$.next(true)),\n switchMap(() => from(this.loadAllData())),\n tap(() => this._isLoading$.next(false)),\n shareReplay(1)\n );\n\n // Derived streams — pure in-memory transforms, no extra DB queries\n readonly kpis$ = this.rawData$.pipe(\n map(data => this.computeKPIs(data.promptRuns, data.agentRuns)),\n shareReplay(1)\n );\n\n readonly trends$ = this.rawData$.pipe(\n map(data => this.computeTrends(data.promptRuns, data.agentRuns)),\n shareReplay(1)\n );\n\n readonly liveExecutions$ = this.rawData$.pipe(\n map(data => this.computeLiveExecutions(data.livePromptRuns, data.liveAgentRuns)),\n shareReplay(1)\n );\n\n readonly chartData$ = combineLatest([this.rawData$, this.trends$]).pipe(\n map(([data, executionTrends]) => this.computeChartData(data.promptRuns, executionTrends)),\n shareReplay(1)\n );\n\n setDateRange(start: Date, end: Date): void {\n this._dateRange$.next({ start, end });\n }\n\n refresh(): void {\n this._refreshTrigger$.next(this._refreshTrigger$.value + 1);\n }\n\n /**\n * Single batch query that loads all data needed by every dashboard widget.\n * Uses ResultType: 'simple' with explicit Fields to minimize payload size.\n */\n private async loadAllData(): Promise<DashboardRawData> {\n const { start, end } = this._dateRange$.value;\n const now = new Date();\n const recentTime = new Date(now.getTime() - 5 * 60 * 1000);\n\n const rv = new RunView();\n const [promptResults, agentResults, livePromptResults, liveAgentResults] = await rv.RunViews<PromptRunRecord | AgentRunRecord>([\n {\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `RunAt >= '${start.toISOString()}' AND RunAt <= '${end.toISOString()}'`,\n Fields: PROMPT_RUN_FIELDS,\n ResultType: 'simple'\n },\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `StartedAt >= '${start.toISOString()}' AND StartedAt <= '${end.toISOString()}'`,\n Fields: AGENT_RUN_FIELDS,\n ResultType: 'simple'\n },\n {\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `RunAt >= '${recentTime.toISOString()}'`,\n OrderBy: 'RunAt DESC',\n Fields: PROMPT_RUN_FIELDS,\n ResultType: 'simple'\n },\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `StartedAt >= '${recentTime.toISOString()}'`,\n OrderBy: 'StartedAt DESC',\n Fields: AGENT_RUN_FIELDS,\n ResultType: 'simple'\n }\n ]);\n\n return {\n promptRuns: promptResults.Results as PromptRunRecord[],\n agentRuns: agentResults.Results as AgentRunRecord[],\n livePromptRuns: livePromptResults.Results as PromptRunRecord[],\n liveAgentRuns: liveAgentResults.Results as AgentRunRecord[]\n };\n }\n\n // ─── KPI Computation ─────────────────────────────────────────────\n\n private computeKPIs(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): DashboardKPIs {\n const totalExecutions = promptRuns.length + agentRuns.length;\n const activeExecutions = this.countActiveExecutions(promptRuns, agentRuns);\n const totalCost = this.sumCosts(promptRuns, agentRuns);\n const totalTokens = this.sumTokens(promptRuns, agentRuns);\n const avgExecutionTime = this.calculateAverageExecutionTime(promptRuns, agentRuns);\n const successRate = this.calculateSuccessRate(promptRuns, agentRuns);\n\n return {\n totalExecutions,\n activeExecutions,\n totalCost,\n costCurrency: 'USD',\n avgExecutionTime,\n successRate,\n totalTokens,\n costPerToken: totalTokens > 0 ? totalCost / totalTokens : 0,\n topModel: this.getTopModel(promptRuns),\n topAgent: this.getTopAgent(agentRuns),\n errorRate: 1 - successRate,\n dailyCostBurn: this.calculateDailyCostBurn(promptRuns, agentRuns)\n };\n }\n\n // ─── Trend Computation ────────────────────────────────────────────\n\n private computeTrends(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): TrendData[] {\n const { start, end } = this._dateRange$.value;\n const bucketSizeMs = this.getBucketSizeMs(start, end);\n const hourlyBuckets = this.createHourlyBuckets(start, end);\n\n return hourlyBuckets.map(bucket => {\n const bucketEnd = new Date(bucket.getTime() + bucketSizeMs);\n const bucketPrompts = promptRuns.filter(r => {\n const runAt = new Date(r.RunAt);\n return runAt >= bucket && runAt < bucketEnd;\n });\n const bucketAgents = agentRuns.filter(r => {\n const startedAt = new Date(r.StartedAt);\n return startedAt >= bucket && startedAt < bucketEnd;\n });\n\n return {\n timestamp: bucket,\n executions: bucketPrompts.length + bucketAgents.length,\n cost: this.sumCosts(bucketPrompts, bucketAgents),\n tokens: this.sumTokens(bucketPrompts, bucketAgents),\n avgTime: this.calculateAverageExecutionTime(bucketPrompts, bucketAgents),\n errors: this.countErrors(bucketPrompts, bucketAgents)\n };\n });\n }\n\n // ─── Live Executions Computation ──────────────────────────────────\n\n private computeLiveExecutions(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): LiveExecution[] {\n const now = new Date();\n const liveExecutions: LiveExecution[] = [];\n\n for (const run of promptRuns) {\n const isRunning = !run.CompletedAt && run.Success !== false;\n const duration = run.CompletedAt\n ? new Date(run.CompletedAt).getTime() - new Date(run.RunAt).getTime()\n : now.getTime() - new Date(run.RunAt).getTime();\n\n liveExecutions.push({\n id: run.ID,\n type: 'prompt',\n name: run.Prompt || 'Unnamed Prompt',\n status: isRunning ? 'running' : (run.Success ? 'completed' : 'failed'),\n startTime: new Date(run.RunAt),\n duration,\n cost: run.Cost || 0,\n tokens: run.TokensUsed || 0,\n progress: isRunning ? Math.min(90, (duration / 30000) * 100) : 100\n });\n }\n\n for (const run of agentRuns) {\n const isRunning = run.Status === 'Running';\n const duration = run.CompletedAt\n ? new Date(run.CompletedAt).getTime() - new Date(run.StartedAt).getTime()\n : now.getTime() - new Date(run.StartedAt).getTime();\n\n liveExecutions.push({\n id: run.ID,\n type: 'agent',\n name: run.Agent || 'Unnamed Agent',\n status: run.Status.toLowerCase() as 'running' | 'completed' | 'failed',\n startTime: new Date(run.StartedAt),\n duration,\n cost: run.TotalCost || 0,\n tokens: run.TotalTokensUsed || 0,\n progress: isRunning ? Math.min(90, (duration / 60000) * 100) : 100\n });\n }\n\n return liveExecutions.sort((a, b) => b.startTime.getTime() - a.startTime.getTime());\n }\n\n // ─── Chart Data Computation ───────────────────────────────────────\n\n private computeChartData(promptRuns: PromptRunRecord[], executionTrends: TrendData[]): ChartData {\n return {\n executionTrends,\n costByModel: this.analyzeCostByModel(promptRuns),\n performanceMatrix: this.analyzePerformanceMatrix(promptRuns),\n tokenEfficiency: this.analyzeTokenEfficiency(promptRuns)\n };\n }\n\n // ─── Helper Methods ───────────────────────────────────────────────\n\n private countActiveExecutions(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): number {\n const activePrompts = promptRuns.filter(r => !r.CompletedAt && r.Success !== false).length;\n const activeAgents = agentRuns.filter(r => r.Status === 'Running').length;\n return activePrompts + activeAgents;\n }\n\n private sumCosts(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): number {\n const promptCost = promptRuns.reduce((sum, r) => sum + (r.Cost || 0), 0);\n const agentCost = agentRuns.reduce((sum, r) => sum + (r.TotalCost || 0), 0);\n return promptCost + agentCost;\n }\n\n private sumTokens(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): number {\n const promptTokens = promptRuns.reduce((sum, r) => sum + (r.TokensUsed || 0), 0);\n const agentTokens = agentRuns.reduce((sum, r) => sum + (r.TotalTokensUsed || 0), 0);\n return promptTokens + agentTokens;\n }\n\n private calculateAverageExecutionTime(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): number {\n const promptTimes = promptRuns\n .filter(r => r.ExecutionTimeMS)\n .map(r => r.ExecutionTimeMS!);\n\n const agentTimes = agentRuns\n .filter(r => r.StartedAt && r.CompletedAt)\n .map(r => new Date(r.CompletedAt!).getTime() - new Date(r.StartedAt).getTime());\n\n const allTimes = [...promptTimes, ...agentTimes];\n return allTimes.length > 0 ? allTimes.reduce((sum, time) => sum + time, 0) / allTimes.length : 0;\n }\n\n private calculateSuccessRate(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): number {\n const totalExecutions = promptRuns.length + agentRuns.length;\n if (totalExecutions === 0) return 1;\n\n const successfulPrompts = promptRuns.filter(r => r.Success).length;\n const successfulAgents = agentRuns.filter(r => r.Success).length;\n\n return (successfulPrompts + successfulAgents) / totalExecutions;\n }\n\n private countErrors(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): number {\n const promptErrors = promptRuns.filter(r => !r.Success).length;\n const agentErrors = agentRuns.filter(r => !r.Success).length;\n return promptErrors + agentErrors;\n }\n\n private calculateDailyCostBurn(promptRuns: PromptRunRecord[], agentRuns: AgentRunRecord[]): number {\n const now = new Date();\n const dayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n\n const todayPrompts = promptRuns.filter(r => new Date(r.RunAt) >= dayStart);\n const todayAgents = agentRuns.filter(r => new Date(r.StartedAt) >= dayStart);\n\n return this.sumCosts(todayPrompts, todayAgents);\n }\n\n private getBucketSizeMs(start: Date, end: Date): number {\n const hours = (end.getTime() - start.getTime()) / (1000 * 60 * 60);\n if (hours <= 24) return 60 * 60 * 1000; // 1 hour\n if (hours <= 24 * 7) return 4 * 60 * 60 * 1000; // 4 hours\n return 24 * 60 * 60 * 1000; // 24 hours\n }\n\n private createHourlyBuckets(start: Date, end: Date): Date[] {\n const buckets: Date[] = [];\n const current = new Date(start);\n const hours = (end.getTime() - start.getTime()) / (1000 * 60 * 60);\n\n let bucketSize: number;\n if (hours <= 24) {\n bucketSize = 1;\n current.setMinutes(0, 0, 0);\n } else if (hours <= 24 * 7) {\n bucketSize = 4;\n current.setHours(Math.floor(current.getHours() / 4) * 4, 0, 0, 0);\n } else {\n bucketSize = 24;\n current.setHours(0, 0, 0, 0);\n }\n\n while (current < end) {\n buckets.push(new Date(current));\n current.setHours(current.getHours() + bucketSize);\n }\n\n return buckets;\n }\n\n private getTopModel(promptRuns: PromptRunRecord[]): string {\n const modelCounts = new Map<string, number>();\n const modelNames = new Map<string, string>();\n\n for (const run of promptRuns) {\n if (run.ModelID && run.Model) {\n modelCounts.set(run.ModelID, (modelCounts.get(run.ModelID) || 0) + 1);\n modelNames.set(run.ModelID, run.Model);\n }\n }\n\n if (modelCounts.size === 0) return 'N/A';\n\n const topModelId = Array.from(modelCounts.entries())\n .sort(([,a], [,b]) => b - a)[0][0];\n\n return modelNames.get(topModelId) || 'Unknown Model';\n }\n\n private getTopAgent(agentRuns: AgentRunRecord[]): string {\n const agentCounts = new Map<string, number>();\n const agentNames = new Map<string, string>();\n\n for (const run of agentRuns) {\n if (run.AgentID && run.Agent) {\n agentCounts.set(run.AgentID, (agentCounts.get(run.AgentID) || 0) + 1);\n agentNames.set(run.AgentID, run.Agent);\n }\n }\n\n if (agentCounts.size === 0) return 'N/A';\n\n const topAgentId = Array.from(agentCounts.entries())\n .sort(([,a], [,b]) => b - a)[0][0];\n\n return agentNames.get(topAgentId) || 'Unknown Agent';\n }\n\n private analyzeCostByModel(promptRuns: PromptRunRecord[]): { model: string; cost: number; tokens: number }[] {\n const modelStats = new Map<string, { cost: number; tokens: number; name: string }>();\n\n for (const run of promptRuns) {\n if (run.ModelID && run.Model) {\n const existing = modelStats.get(run.ModelID) || { cost: 0, tokens: 0, name: run.Model };\n existing.cost += run.Cost || 0;\n existing.tokens += run.TokensUsed || 0;\n modelStats.set(run.ModelID, existing);\n }\n }\n\n return Array.from(modelStats.values())\n .map(stats => ({ model: stats.name, cost: stats.cost, tokens: stats.tokens }))\n .sort((a, b) => b.cost - a.cost);\n }\n\n private analyzePerformanceMatrix(promptRuns: PromptRunRecord[]): { agent: string; model: string; avgTime: number; successRate: number }[] {\n const combinations = new Map<string, { times: number[]; successes: number; total: number; agentName: string; modelName: string }>();\n\n for (const run of promptRuns) {\n if (run.AgentID && run.ModelID && run.ExecutionTimeMS) {\n const key = `${run.AgentID}:${run.ModelID}`;\n const existing = combinations.get(key) || {\n times: [], successes: 0, total: 0,\n agentName: run.Agent || 'Unknown Agent',\n modelName: run.Model || 'Unknown Model'\n };\n\n existing.times.push(run.ExecutionTimeMS);\n existing.total += 1;\n if (run.Success) existing.successes += 1;\n combinations.set(key, existing);\n }\n }\n\n return Array.from(combinations.values()).map(data => ({\n agent: data.agentName,\n model: data.modelName,\n avgTime: data.times.reduce((sum, time) => sum + time, 0) / data.times.length,\n successRate: data.successes / data.total\n }));\n }\n\n private analyzeTokenEfficiency(promptRuns: PromptRunRecord[]): { inputTokens: number; outputTokens: number; cost: number; model: string }[] {\n const modelEfficiency = new Map<string, { input: number; output: number; cost: number; name: string }>();\n\n for (const run of promptRuns) {\n if (run.ModelID && run.Model && run.TokensPrompt && run.TokensCompletion) {\n const existing = modelEfficiency.get(run.ModelID) || { input: 0, output: 0, cost: 0, name: run.Model };\n existing.input += run.TokensPrompt;\n existing.output += run.TokensCompletion;\n existing.cost += run.Cost || 0;\n modelEfficiency.set(run.ModelID, existing);\n }\n }\n\n return Array.from(modelEfficiency.values()).map(data => ({\n inputTokens: data.input,\n outputTokens: data.output,\n cost: data.cost,\n model: data.name\n }));\n }\n\n // ─── Execution Details (on-demand, not part of initial load) ──────\n\n async getExecutionDetails(executionId: string, type: 'prompt' | 'agent'): Promise<ExecutionDetails | null> {\n try {\n if (type === 'prompt') {\n return await this.getPromptExecutionDetails(executionId);\n } else {\n return await this.getAgentExecutionDetails(executionId);\n }\n } catch (error) {\n console.error('Error loading execution details:', error);\n return null;\n }\n }\n\n private async getPromptExecutionDetails(promptRunId: string): Promise<ExecutionDetails> {\n const rv = new RunView();\n const [result, childrenResult] = await rv.RunViews<PromptRunRecord>([\n {\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `ID = '${promptRunId}'`,\n Fields: PROMPT_RUN_FIELDS,\n ResultType: 'simple'\n },\n {\n EntityName: 'MJ: AI Prompt Runs',\n ExtraFilter: `ParentID = '${promptRunId}'`,\n Fields: PROMPT_RUN_FIELDS,\n ResultType: 'simple'\n }\n ]);\n\n const run = result.Results[0];\n if (!run) throw new Error('Prompt run not found');\n\n const children = await Promise.all(\n childrenResult.Results.map(child => this.getPromptExecutionDetails(child.ID))\n );\n\n return {\n id: run.ID,\n type: 'prompt',\n name: run.Prompt || 'Unnamed Prompt',\n status: run.Success ? 'completed' : 'failed',\n startTime: new Date(run.RunAt),\n endTime: run.CompletedAt ? new Date(run.CompletedAt) : undefined,\n cost: run.Cost || 0,\n tokens: run.TokensUsed || 0,\n success: run.Success || false,\n errorMessage: run.ErrorMessage || undefined,\n children,\n model: run.Model || undefined\n };\n }\n\n private async getAgentExecutionDetails(agentRunId: string): Promise<ExecutionDetails> {\n const rv = new RunView();\n const [result, childrenResult] = await rv.RunViews<AgentRunRecord>([\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `ID = '${agentRunId}'`,\n Fields: AGENT_RUN_FIELDS,\n ResultType: 'simple'\n },\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `ParentRunID = '${agentRunId}'`,\n Fields: AGENT_RUN_FIELDS,\n ResultType: 'simple'\n }\n ]);\n\n const run = result.Results[0];\n if (!run) throw new Error('Agent run not found');\n\n const children = await Promise.all(\n childrenResult.Results.map(child => this.getAgentExecutionDetails(child.ID))\n );\n\n return {\n id: run.ID,\n type: 'agent',\n name: run.Agent || 'Unnamed Agent',\n status: run.Status.toLowerCase(),\n startTime: new Date(run.StartedAt),\n endTime: run.CompletedAt ? new Date(run.CompletedAt) : undefined,\n cost: run.TotalCost || 0,\n tokens: run.TotalTokensUsed || 0,\n success: run.Success || false,\n errorMessage: run.ErrorMessage || undefined,\n parentId: undefined,\n children\n };\n }\n}\n"]}
|
|
@@ -6,10 +6,8 @@ import { UUIDsEqual } from '@memberjunction/global';
|
|
|
6
6
|
import * as i0 from "@angular/core";
|
|
7
7
|
import * as i1 from "@angular/common";
|
|
8
8
|
import * as i2 from "@angular/forms";
|
|
9
|
-
import * as i3 from "@
|
|
10
|
-
import * as i4 from "@
|
|
11
|
-
import * as i5 from "@progress/kendo-angular-inputs";
|
|
12
|
-
import * as i6 from "@memberjunction/ng-shared-generic";
|
|
9
|
+
import * as i3 from "@memberjunction/ng-ui-components";
|
|
10
|
+
import * as i4 from "@memberjunction/ng-shared-generic";
|
|
13
11
|
const _c0 = () => ["Include", "Exclude"];
|
|
14
12
|
function APIApplicationsPanelComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
15
13
|
i0.ɵɵelement(0, "mj-loading", 1);
|
|
@@ -276,8 +274,8 @@ function APIApplicationsPanelComponent_Conditional_58_For_7_Conditional_14_For_2
|
|
|
276
274
|
i0.ɵɵelementStart(0, "div", 92)(1, "input", 95);
|
|
277
275
|
i0.ɵɵtwoWayListener("ngModelChange", function APIApplicationsPanelComponent_Conditional_58_For_7_Conditional_14_For_2_Conditional_8_Template_input_ngModelChange_1_listener($event) { i0.ɵɵrestoreView(_r12); const selection_r11 = i0.ɵɵnextContext().$implicit; i0.ɵɵtwoWayBindingSet(selection_r11.pattern, $event) || (selection_r11.pattern = $event); return i0.ɵɵresetView($event); });
|
|
278
276
|
i0.ɵɵelementEnd();
|
|
279
|
-
i0.ɵɵelementStart(2, "
|
|
280
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
277
|
+
i0.ɵɵelementStart(2, "mj-dropdown", 96);
|
|
278
|
+
i0.ɵɵtwoWayListener("ngModelChange", function APIApplicationsPanelComponent_Conditional_58_For_7_Conditional_14_For_2_Conditional_8_Template_mj_dropdown_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r12); const selection_r11 = i0.ɵɵnextContext().$implicit; i0.ɵɵtwoWayBindingSet(selection_r11.patternType, $event) || (selection_r11.patternType = $event); return i0.ɵɵresetView($event); });
|
|
281
279
|
i0.ɵɵelementEnd()();
|
|
282
280
|
} if (rf & 2) {
|
|
283
281
|
const selection_r11 = i0.ɵɵnextContext().$implicit;
|
|
@@ -285,7 +283,7 @@ function APIApplicationsPanelComponent_Conditional_58_For_7_Conditional_14_For_2
|
|
|
285
283
|
i0.ɵɵtwoWayProperty("ngModel", selection_r11.pattern);
|
|
286
284
|
i0.ɵɵadvance();
|
|
287
285
|
i0.ɵɵtwoWayProperty("ngModel", selection_r11.patternType);
|
|
288
|
-
i0.ɵɵproperty("
|
|
286
|
+
i0.ɵɵproperty("Data", i0.ɵɵpureFunction0(4, _c0))("ValuePrimitive", true);
|
|
289
287
|
} }
|
|
290
288
|
function APIApplicationsPanelComponent_Conditional_58_For_7_Conditional_14_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
291
289
|
const _r10 = i0.ɵɵgetCurrentView();
|
|
@@ -911,7 +909,7 @@ export class APIApplicationsPanelComponent {
|
|
|
911
909
|
static ɵfac = function APIApplicationsPanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || APIApplicationsPanelComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
912
910
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: APIApplicationsPanelComponent, selectors: [["mj-api-applications-panel"]], hostBindings: function APIApplicationsPanelComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
913
911
|
i0.ɵɵlistener("mousemove", function APIApplicationsPanelComponent_mousemove_HostBindingHandler($event) { return ctx.onMouseMove($event); }, i0.ɵɵresolveDocument)("mouseup", function APIApplicationsPanelComponent_mouseup_HostBindingHandler() { return ctx.onMouseUp(); }, i0.ɵɵresolveDocument);
|
|
914
|
-
} }, outputs: { ApplicationUpdated: "ApplicationUpdated" }, standalone: false, decls: 65, vars:
|
|
912
|
+
} }, outputs: { ApplicationUpdated: "ApplicationUpdated" }, standalone: false, decls: 65, vars: 34, consts: [[1, "applications-panel"], ["text", "Loading applications..."], [1, "slideout-backdrop"], [1, "slideout-panel"], [1, "slideout-header"], [1, "slideout-title"], [1, "fa-solid", "fa-plus-circle"], ["title", "Close", 1, "slideout-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "slideout-content"], [1, "form-section"], [1, "form-field"], ["placeholder", "e.g., MJAPI, MCP Server, Portal", 1, "form-input", 3, "ngModelChange", "ngModel"], ["placeholder", "Describe the application's purpose...", 1, "form-textarea", 3, "ngModelChange", "ngModel", "rows"], [1, "checkbox-label"], ["type", "checkbox", 1, "mj-checkbox", 3, "ngModelChange", "ngModel"], [1, "checkbox-hint"], [1, "slideout-footer"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["size", "small", 3, "showText"], ["mjButton", "", 3, "click"], [1, "slideout-resize-handle", 3, "mousedown"], [1, "fa-solid", "fa-cube"], [1, "status-pill"], [1, "slideout-tabs"], [1, "slideout-tab", 3, "click"], [1, "fa-solid", "fa-info-circle"], [1, "fa-solid", "fa-shield-halved"], [1, "tab-badge"], [1, "tab-panel"], [1, "tab-panel", "scopes-panel"], [1, "panel-header"], [1, "header-left"], [1, "panel-title"], [1, "panel-subtitle"], [1, "btn-create", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "message", "success"], [1, "message", "error"], [1, "applications-grid"], [1, "app-card", 3, "inactive", "expanded"], [1, "empty-state"], [1, "fa-solid", "fa-check-circle"], [1, "fa-solid", "fa-circle-exclamation"], [1, "app-card"], [1, "app-header"], [1, "app-header-main", 3, "click"], [1, "app-icon"], [1, "app-info"], [1, "app-name"], [1, "status-badge"], [1, "app-description"], [1, "app-stats"], [1, "scope-count"], [1, "fa-solid", "expand-icon"], ["title", "Edit Application", 1, "app-edit-btn", 3, "click"], [1, "fa-solid", "fa-pencil"], [1, "app-details"], [1, "details-section"], [1, "scope-rules"], [1, "empty-scopes"], [1, "scope-rule"], [1, "rule-icon", 3, "ngClass"], [1, "rule-info"], [1, "rule-scope"], [1, "rule-pattern"], [1, "pattern-type"], [1, "priority"], [1, "fa-solid", "fa-shield-xmark"], [1, "slideout-backdrop", 3, "click"], [1, "fa-solid", "fa-save"], [1, "scopes-intro"], [1, "scope-categories-list"], [1, "scope-category-card"], [1, "empty-scopes-message"], [1, "category-header", 3, "click"], [1, "category-left"], [1, "category-name"], [1, "category-count-badge"], [1, "category-right"], [1, "category-all-toggle", 3, "click"], ["type", "checkbox", 1, "mj-checkbox", 3, "change", "checked"], [1, "fa-solid", "category-chevron"], [1, "category-scopes-list"], [1, "scope-row", 3, "selected"], [1, "scope-row"], [1, "scope-select"], ["type", "checkbox", 1, "mj-checkbox", 3, "ngModelChange", "change", "ngModel"], [1, "scope-label"], [1, "scope-path"], [1, "scope-description"], [1, "scope-pattern-display"], [1, "scope-pattern-config"], [1, "pattern-tag"], [1, "fa-solid"], ["placeholder", "*", "title", "Resource Pattern", 1, "pattern-field", 3, "ngModelChange", "ngModel"], ["title", "Pattern Type", 1, "type-field", 3, "ngModelChange", "ngModel", "Data", "ValuePrimitive"]], template: function APIApplicationsPanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
915
913
|
i0.ɵɵelementStart(0, "div", 0);
|
|
916
914
|
i0.ɵɵconditionalCreate(1, APIApplicationsPanelComponent_Conditional_1_Template, 1, 0, "mj-loading", 1);
|
|
917
915
|
i0.ɵɵconditionalCreate(2, APIApplicationsPanelComponent_Conditional_2_Template, 16, 3);
|
|
@@ -1013,7 +1011,7 @@ export class APIApplicationsPanelComponent {
|
|
|
1013
1011
|
i0.ɵɵadvance(3);
|
|
1014
1012
|
i0.ɵɵtwoWayProperty("ngModel", ctx.EditIsActive);
|
|
1015
1013
|
i0.ɵɵadvance(7);
|
|
1016
|
-
i0.ɵɵproperty("
|
|
1014
|
+
i0.ɵɵproperty("disabled", !ctx.EditName.trim() || ctx.IsSaving);
|
|
1017
1015
|
i0.ɵɵadvance();
|
|
1018
1016
|
i0.ɵɵconditional(ctx.IsSaving ? 32 : -1);
|
|
1019
1017
|
i0.ɵɵadvance();
|
|
@@ -1040,16 +1038,16 @@ export class APIApplicationsPanelComponent {
|
|
|
1040
1038
|
i0.ɵɵadvance();
|
|
1041
1039
|
i0.ɵɵconditional(ctx.EditTab === "scopes" ? 58 : -1);
|
|
1042
1040
|
i0.ɵɵadvance(2);
|
|
1043
|
-
i0.ɵɵproperty("
|
|
1041
|
+
i0.ɵɵproperty("disabled", !ctx.EditName.trim() || ctx.IsSaving);
|
|
1044
1042
|
i0.ɵɵadvance();
|
|
1045
1043
|
i0.ɵɵconditional(ctx.IsSaving ? 61 : -1);
|
|
1046
1044
|
i0.ɵɵadvance();
|
|
1047
1045
|
i0.ɵɵconditional(!ctx.IsSaving ? 62 : -1);
|
|
1048
|
-
} }, dependencies: [i1.NgClass, i2.DefaultValueAccessor, i2.CheckboxControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.ButtonComponent, i4.DropDownListComponent, i5.TextBoxDirective, i5.TextAreaDirective, i5.CheckBoxDirective, i6.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"] });
|
|
1046
|
+
} }, 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"] });
|
|
1049
1047
|
}
|
|
1050
1048
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(APIApplicationsPanelComponent, [{
|
|
1051
1049
|
type: Component,
|
|
1052
|
-
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 kendoTextBox [(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 kendoTextArea [(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\" kendoCheckBox [(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 kendoButton [themeColor]=\"'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 kendoButton (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 kendoTextBox [(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 kendoTextArea [(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\" kendoCheckBox [(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\" kendoCheckBox\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\" kendoCheckBox\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 kendoTextBox [(ngModel)]=\"selection.pattern\"\n placeholder=\"*\"\n class=\"pattern-field\"\n title=\"Resource Pattern\" />\n <kendo-dropdownlist [(ngModel)]=\"selection.patternType\"\n [data]=\"['Include', 'Exclude']\"\n [valuePrimitive]=\"true\"\n class=\"type-field\"\n title=\"Pattern Type\">\n </kendo-dropdownlist>\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 kendoButton [themeColor]=\"'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 kendoButton (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"] }]
|
|
1050
|
+
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"] }]
|
|
1053
1051
|
}], () => [{ type: i0.ChangeDetectorRef }], { ApplicationUpdated: [{
|
|
1054
1052
|
type: Output
|
|
1055
1053
|
}], onMouseMove: [{
|