diff --git a/docs/plans/2026-03-18-ext-ux-audit-fixes.md b/docs/plans/2026-03-18-ext-ux-audit-fixes.md new file mode 100644 index 000000000..1132e8146 --- /dev/null +++ b/docs/plans/2026-03-18-ext-ux-audit-fixes.md @@ -0,0 +1,276 @@ +# Extension UX Audit Fixes + +**Date:** 2026-03-18 +**Branch:** `feature/ext-ux-audit` +**Scope:** 28 findings from side-by-side comparison of legacy v0.3.4 vs new extension + +## Findings Reference + +Each task references an audit finding number (#N) from the comparison session. + +## Phase 1: Cross-Cutting Infrastructure + +Independent of any single panel. Must complete before phases 2–9. + +### Task 1.1: Add Connection References + Environment Variables to sidebar (#56, #57) + +**File:** `src/PPDS.Extension/src/views/ToolsTreeView.ts` + +The static tools list has 8 items but is missing Connection References and Environment Variables. +Add them between Import Jobs and Plugin Traces to match logical grouping. + +### Task 1.2: Fix "Open" prefix inconsistency in commands (#51) + +**File:** `src/PPDS.Extension/package.json`, relevant panel registration files + +Current state: Some panels use "PPDS: Open X" (Import Jobs, Web Resources, Connection Refs, Env Variables, Metadata Browser, Solutions) while others use "PPDS: X" (Data Explorer, Plugin Traces). + +Decision: Standardize on "PPDS: Open X" for all panel-opening commands. Add aliases without "Open" for backward compat if needed. Ensure command palette titles are consistent. + +### Task 1.3: Align column header casing (#10) + +**Files:** All `src/PPDS.Extension/src/panels/webview/*-panel.ts` and `src/PPDS.Extension/src/panels/styles/*-panel.css` + +Current: Some panels use UPPERCASE headers ("SOLUTION", "STATUS"), others use Title Case. The shared `data-table.ts` component may control this. Standardize on **Title Case** to match VS Code conventions and the legacy extension. + +If casing comes from CSS `text-transform: uppercase`, remove it. If hardcoded in HTML, change to Title Case. + +### Task 1.4: Align "Maker Portal" button naming (#22) + +**Files:** All panel webview TS files that have maker buttons + +Legacy used "Open in Maker". New uses "Maker Portal" in some panels but the per-row button in Solutions uses a 🔗 icon. Standardize: toolbar buttons say "Maker Portal", per-row icons keep 🔗 with title="Open in Maker Portal". + +--- + +## Phase 2: Solutions Panel (#1, #2, #3, #5) + +### Task 2.1: Show all solutions with managed toggle (#1) + +**Files:** +- `src/PPDS.Extension/src/panels/SolutionsPanel.ts` +- `src/PPDS.Extension/src/panels/webview/solutions-panel.ts` + +The RPC `solutions/list` already supports `includeManaged: true`. Currently the panel calls it without this flag, so only unmanaged solutions (8) appear. + +Fix: Add a toggle/checkbox "Include Managed" (default: true to match legacy's 190-record view). Pass `includeManaged` to the RPC call. Persist the toggle state via `globalState`. + +### Task 2.2: Add sortable columns (#2, #5) + +**Files:** +- `src/PPDS.Extension/src/panels/webview/solutions-panel.ts` +- `src/PPDS.Extension/src/panels/styles/solutions-panel.css` + +Current: Solutions uses a tree/list view with expandable items. Legacy had a 9-column sortable table. + +The existing expandable tree view is a net improvement over legacy (component drill-down). Don't regress to a flat table. Instead: + +1. Add a **table view toggle** (list view vs table view) or convert the list to include sortable column headers above the list items. +2. At minimum, add the missing data columns to each solution row: **Visible**, **API Managed**, **Installed On**, **Modified On** — these should appear in the expandable detail card which already shows Unique Name, Publisher, Type, Installed, Modified. +3. Add client-side sort controls (by name, version, publisher, modified date). + +### Task 2.3: Add toolbar "Maker Portal" button (#3) + +**File:** `src/PPDS.Extension/src/panels/webview/solutions-panel.ts` + +Per-row 🔗 buttons exist but there's no toolbar-level "Maker Portal" button. Add one that opens the Solutions list in Maker Portal (uses `buildMakerUrl(envId, '/solutions')`). + +--- + +## Phase 3: Import Jobs Panel (#7, #8, #9) + +### Task 3.1: Add search bar (#55 — Import Jobs) + +**File:** `src/PPDS.Extension/src/panels/webview/import-jobs-panel.ts` + +Add a search/filter input using the shared `filter-bar.ts` pattern or inline search input. Filter on solution name client-side. + +### Task 3.2: Add Operation Context column (#8) + +**Files:** +- `src/PPDS.Cli/Commands/Serve/Handlers/RpcMethodHandler.cs` — add `operationContext` to `ImportJobInfoDto` +- `src/PPDS.Dataverse/Services/IImportJobService.cs` or its implementation — include `operationcontext` in the query +- `src/PPDS.Extension/src/panels/webview/import-jobs-panel.ts` — add column to table + +The `importjob` entity has `operationcontext` field (verified in generated entity). The RPC DTO just doesn't include it. Add it end-to-end. + +### Task 3.3: Add record count (#9) + +**File:** `src/PPDS.Extension/src/panels/webview/import-jobs-panel.ts` + +Add footer status showing record count (e.g., "227 import jobs"). Follow the pattern used by other panels. + +--- + +## Phase 4: Plugin Traces Panel — CRITICAL (#11, #12, #17, #18) + +### Task 4.1: Fix Trace Level button — CRITICAL (#11) + +**Files:** +- `src/PPDS.Extension/src/panels/PluginTracesPanel.ts` — already has `pluginTracesTraceLevel()` and `pluginTracesSetTraceLevel()` methods +- `src/PPDS.Extension/src/panels/webview/plugin-traces-panel.ts` — the button click handler needs to show current level and allow changing + +The daemon client already has both `pluginTracesTraceLevel` and `pluginTracesSetTraceLevel` methods. The PluginTracesPanel.ts backend already has handler methods. The issue is in the **webview JS** — the button exists but doesn't properly: +1. Fetch and display the current trace level on load +2. Show a dropdown/picker to change the level when clicked + +Fix: When the button is clicked, show a dropdown (or VS Code quick pick via postMessage) with options: Off, Exception, All. Display current level on the button text: "Trace Level: All". Update `trace-level-indicator` span. + +### Task 4.2: Fix Maker Portal URL (#12) + +**File:** `src/PPDS.Extension/src/panels/PluginTracesPanel.ts:163` + +Current: `buildMakerUrl(this.environmentId, '/plugintraceloglist')` — this path doesn't exist in Maker Portal. + +The correct approach: Plugin Trace Logs don't have a direct Maker Portal URL. Change to open the Dynamics 365 classic URL: `https://{org}.crm.dynamics.com/main.aspx?pagetype=entitylist&etn=plugintracelog`. This requires the environment URL (which we have) rather than the environment ID. + +Alternatively, if the Maker Portal does support a traces path, use it. Research needed — fallback to the Dynamics 365 URL. + +### Task 4.3: Add record count (#17) + +**File:** `src/PPDS.Extension/src/panels/webview/plugin-traces-panel.ts` + +Add footer with trace count (e.g., "100 traces"). + +### Task 4.4: Add text labels to status indicators (#18) + +**File:** `src/PPDS.Extension/src/panels/webview/plugin-traces-panel.ts` + +Current: Green/red dots only. Add "Success" / "Exception" text next to the dot for accessibility and clarity. + +--- + +## Phase 5: Web Resources Panel (#19) + +### Task 5.1: Restore Created On / Created By columns (#19) + +**File:** `src/PPDS.Extension/src/panels/webview/web-resources-panel.ts` + +The RPC `webResources/list` already returns `createdByName` and `createdOn` in the DTO. The webview just doesn't render columns for them. Add "Created On" and "Created By" columns to the table. + +--- + +## Phase 6: Connection References Panel (#23, #24, #25, #26, #28, #55) + +### Task 6.1: Add expandable flow/connection detail (#23) + +**Files:** +- `src/PPDS.Extension/src/panels/ConnectionReferencesPanel.ts` +- `src/PPDS.Extension/src/panels/webview/connection-references-panel.ts` +- `src/PPDS.Extension/src/panels/styles/connection-references-panel.css` + +The `connectionReferences/get` RPC already returns `flows: FlowInfoDto[]` per CR. When a user clicks/expands a CR row, call `connectionReferences/get` for that CR and display: +- Connection status with bound connection name +- List of flows using this CR +- Connector display name + +Follow the same expandable pattern used by Solutions panel (chevron toggle, detail card). + +### Task 6.2: Fix raw ISO timestamp formatting (#24) + +**File:** `src/PPDS.Extension/src/panels/webview/connection-references-panel.ts` + +Current: Displays raw `2026-03-08T19:35:34.0000000Z`. Use the shared `dom-utils.ts` date formatting function (or `new Date(iso).toLocaleString()`) to display human-readable dates. + +### Task 6.3: Add "Sync Deployment Settings" button (#25) + +**Files:** +- `src/PPDS.Cli/Commands/Serve/Handlers/RpcMethodHandler.cs` — add `deploymentSettings/sync` RPC endpoint +- `src/PPDS.Extension/src/panels/ConnectionReferencesPanel.ts` — handle the sync command +- `src/PPDS.Extension/src/panels/webview/connection-references-panel.ts` — add toolbar button + +The `IDeploymentSettingsService` and `SyncCommand` already exist in the CLI. Wire up an RPC endpoint that calls the same service, then add a toolbar button. + +### Task 6.4: Add search bar (#55 — Connection Refs) + +**File:** `src/PPDS.Extension/src/panels/webview/connection-references-panel.ts` + +Add search/filter input that filters on display name, logical name, and connector name. + +### Task 6.5: Fix Status "N/A" display (#28) + +**File:** `src/PPDS.Extension/src/panels/webview/connection-references-panel.ts` + +Current: Shows "N/A" badge without context. When connection status is unknown (SPN can't access Power Apps API), display "Unknown (limited access)" or a tooltip explaining why. When unbound, show "Unbound" with warning styling. + +--- + +## Phase 7: Environment Variables Panel (#29, #30, #31, #55) + +### Task 7.1: Add "Sync Deployment Settings" button (#29) + +Same RPC endpoint as Task 6.3 (shared service). Add toolbar button to Environment Variables panel. + +**File:** `src/PPDS.Extension/src/panels/webview/environment-variables-panel.ts` + +### Task 7.2: Restore Modified On column (#31) + +**File:** `src/PPDS.Extension/src/panels/webview/environment-variables-panel.ts` + +The RPC returns `modifiedOn` in the DTO. Add the column to the table. + +### Task 7.3: Add search bar (#55 — Env Variables) + +**File:** `src/PPDS.Extension/src/panels/webview/environment-variables-panel.ts` + +Add search/filter input that filters on schema name, display name, and current value. + +--- + +## Phase 8: Metadata Browser Panel (#37) + +### Task 8.1: Add "Hide metadata" filter (#37) + +**File:** `src/PPDS.Extension/src/panels/webview/metadata-browser-panel.ts` + +Legacy had a "Hide metadata" checkbox that filtered out system/internal attributes (those with `isCustom=false` or similar). Add a toggle checkbox in the toolbar. Default to showing all (matching current behavior), but allow users to hide system metadata. + +--- + +## Phase 9: Data Explorer Panel (#41, #42) + +### Task 9.1: Add Clear button (#41) + +**File:** `src/PPDS.Extension/src/panels/webview/query-panel.ts` + +Add a "Clear" button to the toolbar that clears the editor content and results area. + +### Task 9.2: Add Import button (#42) + +**Files:** +- `src/PPDS.Extension/src/panels/QueryPanel.ts` +- `src/PPDS.Extension/src/panels/webview/query-panel.ts` + +Add an "Import" button/menu that allows importing a FetchXML file from disk into the editor. Use `vscode.window.showOpenDialog` via postMessage to the panel host. + +--- + +## Phase 10: Plugin Traces Search Bar (#55 — Plugin Traces) + +### Task 10.1: Add text search to Plugin Traces + +**File:** `src/PPDS.Extension/src/panels/webview/plugin-traces-panel.ts` + +The panel already has filter fields (Entity, Message, Plugin, Mode) and quick-filter pills. Add a general text search input that filters the loaded traces client-side across all visible columns (plugin name, entity, message, etc.). + +--- + +## Execution Strategy + +- **Phase 1** first (cross-cutting) — commit when done +- **Phases 2–10** are independent per-panel — can be parallelized via agents +- Each phase gets its own commit +- After all phases: gates → verify → QA → review → PR + +## Files Changed Summary + +| Layer | Files | +|-------|-------| +| Sidebar | `ToolsTreeView.ts` | +| Commands | `package.json` | +| Panel hosts | `SolutionsPanel.ts`, `PluginTracesPanel.ts`, `ConnectionReferencesPanel.ts`, `QueryPanel.ts` | +| Webviews | All 8 `*-panel.ts` files in `panels/webview/` | +| Styles | Potentially all 8 CSS files | +| RPC | `RpcMethodHandler.cs` (add operationContext, deploymentSettings/sync) | +| Services | Import job DTO, deployment settings wiring | +| Shared | `data-table.ts` or `dom-utils.ts` if header casing is centralized | diff --git a/src/PPDS.Cli/Commands/Serve/Handlers/RpcMethodHandler.cs b/src/PPDS.Cli/Commands/Serve/Handlers/RpcMethodHandler.cs index 698cf60d0..e46802a10 100644 --- a/src/PPDS.Cli/Commands/Serve/Handlers/RpcMethodHandler.cs +++ b/src/PPDS.Cli/Commands/Serve/Handlers/RpcMethodHandler.cs @@ -2060,7 +2060,9 @@ public async Task SolutionsListAsync( Description = s.Description, CreatedOn = s.CreatedOn, ModifiedOn = s.ModifiedOn, - InstalledOn = s.InstalledOn + InstalledOn = s.InstalledOn, + IsVisible = s.IsVisible, + IsApiManaged = s.IsApiManaged }).ToList() }; }, cancellationToken); @@ -2196,7 +2198,8 @@ private static ImportJobInfoDto MapImportJobToDto(ImportJobInfo job) CreatedOn = job.CreatedOn?.ToString("o"), StartedOn = job.StartedOn?.ToString("o"), CompletedOn = job.CompletedOn?.ToString("o"), - Duration = job.FormattedDuration + Duration = job.FormattedDuration, + OperationContext = job.OperationContext }; } @@ -2213,6 +2216,7 @@ private static ImportJobDetailDto MapImportJobToDetailDto(ImportJobInfo job, str StartedOn = job.StartedOn?.ToString("o"), CompletedOn = job.CompletedOn?.ToString("o"), Duration = job.FormattedDuration, + OperationContext = job.OperationContext, Data = data }; } @@ -3777,6 +3781,18 @@ public class SolutionInfoDto [JsonPropertyName("installedOn")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DateTime? InstalledOn { get; set; } + + /// + /// Gets or sets a value indicating whether the solution is visible. + /// + [JsonPropertyName("isVisible")] + public bool IsVisible { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the solution is API managed. + /// + [JsonPropertyName("isApiManaged")] + public bool IsApiManaged { get; set; } } /// @@ -4076,6 +4092,10 @@ public class ImportJobInfoDto [JsonPropertyName("duration")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Duration { get; set; } + + [JsonPropertyName("operationContext")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? OperationContext { get; set; } } public class ImportJobDetailDto : ImportJobInfoDto diff --git a/src/PPDS.Dataverse/Services/IImportJobService.cs b/src/PPDS.Dataverse/Services/IImportJobService.cs index d1a4a6255..29db79e14 100644 --- a/src/PPDS.Dataverse/Services/IImportJobService.cs +++ b/src/PPDS.Dataverse/Services/IImportJobService.cs @@ -64,7 +64,8 @@ public record ImportJobInfo( DateTime? CompletedOn, DateTime? CreatedOn, bool IsComplete, - string? CreatedByName = null) + string? CreatedByName = null, + string? OperationContext = null) { /// /// Computed status: Succeeded, Failed, or In Progress. diff --git a/src/PPDS.Dataverse/Services/ISolutionService.cs b/src/PPDS.Dataverse/Services/ISolutionService.cs index 068b258ed..628b27515 100644 --- a/src/PPDS.Dataverse/Services/ISolutionService.cs +++ b/src/PPDS.Dataverse/Services/ISolutionService.cs @@ -87,7 +87,9 @@ public record SolutionInfo( string? Description, DateTime? CreatedOn, DateTime? ModifiedOn, - DateTime? InstalledOn); + DateTime? InstalledOn, + bool IsVisible = true, + bool IsApiManaged = false); /// /// Information about a solution component. diff --git a/src/PPDS.Dataverse/Services/ImportJobService.cs b/src/PPDS.Dataverse/Services/ImportJobService.cs index 3f1f9db75..741818f69 100644 --- a/src/PPDS.Dataverse/Services/ImportJobService.cs +++ b/src/PPDS.Dataverse/Services/ImportJobService.cs @@ -53,7 +53,8 @@ public async Task> ListAsync( ImportJob.Fields.StartedOn, ImportJob.Fields.CompletedOn, ImportJob.Fields.CreatedOn, - ImportJob.Fields.CreatedBy), + ImportJob.Fields.CreatedBy, + ImportJob.Fields.OperationContext), TopCount = top, Orders = { new OrderExpression(ImportJob.Fields.CreatedOn, OrderType.Descending) } }; @@ -86,7 +87,8 @@ public async Task> ListAsync( ImportJob.Fields.StartedOn, ImportJob.Fields.CompletedOn, ImportJob.Fields.CreatedOn, - ImportJob.Fields.CreatedBy), + ImportJob.Fields.CreatedBy, + ImportJob.Fields.OperationContext), TopCount = 1 }; @@ -179,6 +181,7 @@ private static ImportJobInfo MapToImportJobInfo(Entity entity) completedOn, entity.GetAttributeValue(ImportJob.Fields.CreatedOn), completedOn.HasValue, - createdByRef?.Name); + createdByRef?.Name, + entity.GetAttributeValue(ImportJob.Fields.OperationContext)); } } diff --git a/src/PPDS.Dataverse/Services/SolutionService.cs b/src/PPDS.Dataverse/Services/SolutionService.cs index be6b2f349..d719decb2 100644 --- a/src/PPDS.Dataverse/Services/SolutionService.cs +++ b/src/PPDS.Dataverse/Services/SolutionService.cs @@ -171,7 +171,9 @@ public async Task> ListAsync( Solution.Fields.Description, Solution.Fields.CreatedOn, Solution.Fields.ModifiedOn, - Solution.Fields.InstalledOn), + Solution.Fields.InstalledOn, + Solution.Fields.IsVisible, + Solution.Fields.IsApiManaged), Orders = { new OrderExpression(Solution.Fields.FriendlyName, OrderType.Ascending) } }; @@ -226,7 +228,9 @@ public async Task> ListAsync( Solution.Fields.Description, Solution.Fields.CreatedOn, Solution.Fields.ModifiedOn, - Solution.Fields.InstalledOn), + Solution.Fields.InstalledOn, + Solution.Fields.IsVisible, + Solution.Fields.IsApiManaged), TopCount = 1 }; @@ -265,7 +269,9 @@ public async Task> ListAsync( Solution.Fields.Description, Solution.Fields.CreatedOn, Solution.Fields.ModifiedOn, - Solution.Fields.InstalledOn), + Solution.Fields.InstalledOn, + Solution.Fields.IsVisible, + Solution.Fields.IsApiManaged), TopCount = 1 }; @@ -550,6 +556,8 @@ private static SolutionInfo MapToSolutionInfo(Entity entity) entity.GetAttributeValue(Solution.Fields.Description), entity.GetAttributeValue(Solution.Fields.CreatedOn), entity.GetAttributeValue(Solution.Fields.ModifiedOn), - entity.GetAttributeValue(Solution.Fields.InstalledOn)); + entity.GetAttributeValue(Solution.Fields.InstalledOn), + entity.GetAttributeValue(Solution.Fields.IsVisible) ?? true, + entity.GetAttributeValue(Solution.Fields.IsApiManaged) ?? false); } } diff --git a/src/PPDS.Extension/knip.json b/src/PPDS.Extension/knip.json index f50cd812f..61f8e61f5 100644 --- a/src/PPDS.Extension/knip.json +++ b/src/PPDS.Extension/knip.json @@ -5,6 +5,12 @@ "src/panels/monaco-worker.ts", "src/panels/webview/query-panel.ts", "src/panels/webview/solutions-panel.ts", + "src/panels/webview/connection-references-panel.ts", + "src/panels/webview/environment-variables-panel.ts", + "src/panels/webview/import-jobs-panel.ts", + "src/panels/webview/metadata-browser-panel.ts", + "src/panels/webview/plugin-traces-panel.ts", + "src/panels/webview/web-resources-panel.ts", "src/panels/webview/shared/vscode-api.ts" ], "ignore": [ diff --git a/src/PPDS.Extension/package.json b/src/PPDS.Extension/package.json index b2927ba59..8a08a745c 100644 --- a/src/PPDS.Extension/package.json +++ b/src/PPDS.Extension/package.json @@ -155,7 +155,7 @@ }, { "command": "ppds.dataExplorer", - "title": "Data Explorer", + "title": "Open Data Explorer", "category": "PPDS", "icon": "$(database)" }, @@ -238,7 +238,7 @@ }, { "command": "ppds.openPluginTraces", - "title": "Plugin Traces", + "title": "Open Plugin Traces", "category": "PPDS", "icon": "$(debug-stackframe)" }, diff --git a/src/PPDS.Extension/src/panels/ConnectionReferencesPanel.ts b/src/PPDS.Extension/src/panels/ConnectionReferencesPanel.ts index 4e8fe1ddb..b04796624 100644 --- a/src/PPDS.Extension/src/panels/ConnectionReferencesPanel.ts +++ b/src/PPDS.Extension/src/panels/ConnectionReferencesPanel.ts @@ -85,6 +85,9 @@ export class ConnectionReferencesPanel extends WebviewPanelBase Refresh Analyze + Sync Deployment Settings Maker Portal
+ ${getEnvironmentPickerHtml()} @@ -289,14 +297,6 @@ export class ConnectionReferencesPanel extends WebviewPanelBaseLoading connection references... - -
Loading...
diff --git a/src/PPDS.Extension/src/panels/EnvironmentVariablesPanel.ts b/src/PPDS.Extension/src/panels/EnvironmentVariablesPanel.ts index 4cfec7cbd..97d724893 100644 --- a/src/PPDS.Extension/src/panels/EnvironmentVariablesPanel.ts +++ b/src/PPDS.Extension/src/panels/EnvironmentVariablesPanel.ts @@ -101,6 +101,9 @@ export class EnvironmentVariablesPanel extends WebviewPanelBase 1); break; @@ -314,8 +317,10 @@ export class EnvironmentVariablesPanel extends WebviewPanelBase Refresh Export + Sync Settings Maker Portal
+ ${getEnvironmentPickerHtml()} diff --git a/src/PPDS.Extension/src/panels/ImportJobsPanel.ts b/src/PPDS.Extension/src/panels/ImportJobsPanel.ts index 547464d5d..061aec88a 100644 --- a/src/PPDS.Extension/src/panels/ImportJobsPanel.ts +++ b/src/PPDS.Extension/src/panels/ImportJobsPanel.ts @@ -135,6 +135,7 @@ export class ImportJobsPanel extends WebviewPanelBase Refresh Maker Portal + ${getEnvironmentPickerHtml()} diff --git a/src/PPDS.Extension/src/panels/MetadataBrowserPanel.ts b/src/PPDS.Extension/src/panels/MetadataBrowserPanel.ts index 94d0f7669..422c467cf 100644 --- a/src/PPDS.Extension/src/panels/MetadataBrowserPanel.ts +++ b/src/PPDS.Extension/src/panels/MetadataBrowserPanel.ts @@ -216,6 +216,12 @@ export class MetadataBrowserPanel extends WebviewPanelBase +
+ +
diff --git a/src/PPDS.Extension/src/panels/PluginTracesPanel.ts b/src/PPDS.Extension/src/panels/PluginTracesPanel.ts index 93eb32e7a..c8eaf235d 100644 --- a/src/PPDS.Extension/src/panels/PluginTracesPanel.ts +++ b/src/PPDS.Extension/src/panels/PluginTracesPanel.ts @@ -3,7 +3,6 @@ import * as vscode from 'vscode'; import type { DaemonClient } from '../daemonClient.js'; import type { TraceFilterDto } from '../types.js'; import { handleAuthError } from '../utils/errorUtils.js'; -import { buildMakerUrl } from '../commands/browserCommands.js'; import { WebviewPanelBase } from './WebviewPanelBase.js'; import { getNonce } from './webviewUtils.js'; @@ -159,11 +158,12 @@ export class PluginTracesPanel extends WebviewPanelBase { - if (this.environmentId) { - const url = buildMakerUrl(this.environmentId, '/plugintraceloglist'); + if (this.environmentUrl) { + const baseUrl = this.environmentUrl.replace(/\/+$/, ''); + const url = `${baseUrl}/main.aspx?pagetype=entitylist&etn=plugintracelog`; await vscode.env.openExternal(vscode.Uri.parse(url)); } else { - vscode.window.showInformationMessage('Environment ID not available \u2014 cannot open Maker Portal.'); + vscode.window.showInformationMessage('Environment URL not available \u2014 cannot open Plugin Trace Logs.'); } } @@ -436,6 +436,7 @@ export class PluginTracesPanel extends WebviewPanelBaseExport Maker Portal + ${getEnvironmentPickerHtml()}
diff --git a/src/PPDS.Extension/src/panels/QueryPanel.ts b/src/PPDS.Extension/src/panels/QueryPanel.ts index 2b80fca5c..996d74b18 100644 --- a/src/PPDS.Extension/src/panels/QueryPanel.ts +++ b/src/PPDS.Extension/src/panels/QueryPanel.ts @@ -604,6 +604,8 @@ export class QueryPanel extends WebviewPanelBase Execute + Clear + Import Export \u25BE History diff --git a/src/PPDS.Extension/src/panels/SolutionsPanel.ts b/src/PPDS.Extension/src/panels/SolutionsPanel.ts index 950533320..ed6e733b1 100644 --- a/src/PPDS.Extension/src/panels/SolutionsPanel.ts +++ b/src/PPDS.Extension/src/panels/SolutionsPanel.ts @@ -149,6 +149,8 @@ export class SolutionsPanel extends WebviewPanelBase Refresh - + Maker Portal + + + ${getEnvironmentPickerHtml()} diff --git a/src/PPDS.Extension/src/panels/styles/connection-references-panel.css b/src/PPDS.Extension/src/panels/styles/connection-references-panel.css index 30bbdd4dc..f03845cf9 100644 --- a/src/PPDS.Extension/src/panels/styles/connection-references-panel.css +++ b/src/PPDS.Extension/src/panels/styles/connection-references-panel.css @@ -49,7 +49,6 @@ text-align: left; padding: 6px 8px; font-size: 11px; - text-transform: uppercase; color: var(--vscode-descriptionForeground); border-bottom: 1px solid var(--vscode-panel-border); } @@ -89,6 +88,20 @@ color: var(--vscode-list-activeSelectionForeground); } +/* ── Chevron toggle for expandable rows ──────────────────────────────── */ + +.cr-chevron { + display: inline-block; + font-size: 10px; + transition: transform 0.15s ease; + color: var(--vscode-descriptionForeground); + user-select: none; +} + +.cr-chevron.expanded { + transform: rotate(90deg); +} + /* ── Status badges ──────────────────────────────────────────────────── */ .status-badge { @@ -113,6 +126,100 @@ color: var(--vscode-descriptionForeground); } +.status-unknown { + background: color-mix(in srgb, var(--vscode-descriptionForeground) 15%, transparent); + color: var(--vscode-descriptionForeground); + cursor: help; +} + +.status-unbound { + background: color-mix(in srgb, var(--vscode-editorWarning-foreground, #cca700) 20%, transparent); + color: var(--vscode-editorWarning-foreground, #cca700); + cursor: help; +} + +.status-unbound-text { + color: var(--vscode-editorWarning-foreground, #cca700); +} + +/* ── Inline expandable detail card ──────────────────────────────────── */ + +.cr-inline-detail td { + padding: 0; + border-bottom: 1px solid var(--vscode-panel-border); + background: color-mix(in srgb, var(--vscode-editor-background) 95%, var(--vscode-list-hoverBackground)); +} + +.cr-detail-card { + padding: 10px 16px; + font-size: 12px; +} + +.cr-detail-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4px 24px; + margin-bottom: 8px; +} + +.cr-detail-item { + padding: 2px 0; +} + +.cr-detail-item .detail-label { + color: var(--vscode-descriptionForeground); + font-weight: 500; + margin-right: 4px; +} + +.cr-detail-full { + grid-column: 1 / -1; +} + +.cr-detail-flows { + border-top: 1px solid var(--vscode-panel-border); + padding-top: 8px; + margin-top: 4px; +} + +.cr-flow-list { + list-style: none; + margin: 0; + padding: 0; +} + +.cr-flow-item { + padding: 2px 0 2px 12px; + font-size: 12px; +} + +.cr-flow-name { + color: var(--vscode-foreground); +} + +.cr-flow-state { + font-size: 11px; + margin-left: 6px; + padding: 1px 4px; + border-radius: 2px; +} + +.cr-flow-state-active { + background: color-mix(in srgb, var(--vscode-testing-iconPassed, #73c991) 15%, transparent); + color: var(--vscode-testing-iconPassed, #73c991); +} + +.cr-flow-state-inactive { + background: color-mix(in srgb, var(--vscode-descriptionForeground) 15%, transparent); + color: var(--vscode-descriptionForeground); +} + +.cr-no-flows { + font-style: italic; + color: var(--vscode-descriptionForeground); + font-size: 12px; +} + /* ── Detail pane ────────────────────────────────────────────────────── */ .detail-pane { @@ -240,3 +347,26 @@ color: var(--vscode-testing-iconPassed, #73c991); padding: 8px 0; } + +/* ── Toolbar Search ──────────────────────────────────────────── */ +.toolbar-search { + flex: 1; + min-width: 120px; + max-width: 300px; + background: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-input-border, transparent); + padding: 3px 8px; + border-radius: 2px; + font-size: 12px; + font-family: var(--vscode-font-family); + outline: none; +} + +.toolbar-search:focus { + outline: 1px solid var(--vscode-focusBorder); +} + +.toolbar-search::placeholder { + color: var(--vscode-input-placeholderForeground); +} diff --git a/src/PPDS.Extension/src/panels/styles/environment-variables-panel.css b/src/PPDS.Extension/src/panels/styles/environment-variables-panel.css index 0f8f2b172..a70027f28 100644 --- a/src/PPDS.Extension/src/panels/styles/environment-variables-panel.css +++ b/src/PPDS.Extension/src/panels/styles/environment-variables-panel.css @@ -49,7 +49,6 @@ text-align: left; padding: 6px 8px; font-size: 11px; - text-transform: uppercase; color: var(--vscode-descriptionForeground); border-bottom: 1px solid var(--vscode-panel-border); } @@ -272,3 +271,26 @@ margin-top: 4px; border-radius: 2px; } + +/* ── Toolbar Search ──────────────────────────────────────────── */ +.toolbar-search { + flex: 1; + min-width: 120px; + max-width: 300px; + background: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-input-border, transparent); + padding: 3px 8px; + border-radius: 2px; + font-size: 12px; + font-family: var(--vscode-font-family); + outline: none; +} + +.toolbar-search:focus { + outline: 1px solid var(--vscode-focusBorder); +} + +.toolbar-search::placeholder { + color: var(--vscode-input-placeholderForeground); +} diff --git a/src/PPDS.Extension/src/panels/styles/import-jobs-panel.css b/src/PPDS.Extension/src/panels/styles/import-jobs-panel.css index e16751443..6746a98e2 100644 --- a/src/PPDS.Extension/src/panels/styles/import-jobs-panel.css +++ b/src/PPDS.Extension/src/panels/styles/import-jobs-panel.css @@ -27,7 +27,6 @@ text-align: left; padding: 6px 8px; font-size: 11px; - text-transform: uppercase; color: var(--vscode-descriptionForeground); border-bottom: 1px solid var(--vscode-panel-border); } @@ -138,3 +137,26 @@ color: var(--vscode-foreground); background: var(--vscode-editor-background); } + +/* ── Toolbar Search ──────────────────────────────────────────── */ +.toolbar-search { + flex: 1; + min-width: 120px; + max-width: 300px; + background: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-input-border, transparent); + padding: 3px 8px; + border-radius: 2px; + font-size: 12px; + font-family: var(--vscode-font-family); + outline: none; +} + +.toolbar-search:focus { + outline: 1px solid var(--vscode-focusBorder); +} + +.toolbar-search::placeholder { + color: var(--vscode-input-placeholderForeground); +} diff --git a/src/PPDS.Extension/src/panels/styles/metadata-browser-panel.css b/src/PPDS.Extension/src/panels/styles/metadata-browser-panel.css index 64ce98204..0916c1e28 100644 --- a/src/PPDS.Extension/src/panels/styles/metadata-browser-panel.css +++ b/src/PPDS.Extension/src/panels/styles/metadata-browser-panel.css @@ -50,6 +50,28 @@ white-space: nowrap; } +/* ── Filter toggle ─────────────────────────────────────────────────────── */ + +.filter-toggle-container { + padding: 2px 6px 4px; +} + +.filter-toggle-label { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + color: var(--vscode-descriptionForeground); + cursor: pointer; + user-select: none; +} + +.filter-toggle-label input[type="checkbox"] { + accent-color: var(--vscode-focusBorder); + cursor: pointer; + margin: 0; +} + .entity-list { flex: 1; overflow-y: auto; @@ -145,7 +167,6 @@ text-align: left; padding: 6px 8px; font-size: 11px; - text-transform: uppercase; color: var(--vscode-descriptionForeground); border-bottom: 1px solid var(--vscode-panel-border); } diff --git a/src/PPDS.Extension/src/panels/styles/plugin-traces-panel.css b/src/PPDS.Extension/src/panels/styles/plugin-traces-panel.css index 7d26ff9b7..5261df210 100644 --- a/src/PPDS.Extension/src/panels/styles/plugin-traces-panel.css +++ b/src/PPDS.Extension/src/panels/styles/plugin-traces-panel.css @@ -216,7 +216,7 @@ } /* Column widths */ -.col-status { width: 40px; } +.col-status { width: 100px; } .col-time { width: 140px; } .col-duration { width: 80px; } .col-plugin { width: 200px; } @@ -442,6 +442,80 @@ font-family: var(--vscode-font-family); } +/* ── Status cell with text label (#18) ────────────────────────── */ +.trace-status-cell { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.trace-status-label { + font-size: 11px; +} + +/* ── Trace Level Dropdown (#11) ──────────────────────────────── */ +.trace-level-dropdown { + position: absolute; + top: 100%; + left: 0; + z-index: 10; + min-width: 140px; + background: var(--vscode-menu-background, var(--vscode-editor-background)); + border: 1px solid var(--vscode-menu-border, var(--vscode-panel-border)); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + padding: 4px 0; +} + +.trace-level-dropdown-item { + display: block; + width: 100%; + padding: 6px 12px; + font-size: 12px; + text-align: left; + background: none; + border: none; + color: var(--vscode-menu-foreground, var(--vscode-foreground)); + cursor: pointer; + font-family: var(--vscode-font-family); +} + +.trace-level-dropdown-item:hover { + background: var(--vscode-menu-selectionBackground, var(--vscode-list-hoverBackground)); + color: var(--vscode-menu-selectionForeground, var(--vscode-foreground)); +} + +.trace-level-dropdown-item.active { + font-weight: 600; +} + +.trace-level-indicator.trace-level-off { + opacity: 0.6; +} + +.trace-level-indicator.trace-level-on { + opacity: 1; +} + +/* ── Toolbar search (#55) ────────────────────────────────────── */ +.toolbar-search { + background: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-input-border, transparent); + padding: 3px 8px; + font-size: 12px; + font-family: var(--vscode-font-family); + min-width: 160px; + max-width: 240px; +} + +.toolbar-search:focus { + outline: 1px solid var(--vscode-focusBorder); +} + +.toolbar-search::placeholder { + color: var(--vscode-input-placeholderForeground); +} + /* ── Export Dropdown ───────────────────────────────────────────── */ .export-dropdown { position: absolute; diff --git a/src/PPDS.Extension/src/panels/styles/solutions-panel.css b/src/PPDS.Extension/src/panels/styles/solutions-panel.css index 189578094..e8097f906 100644 --- a/src/PPDS.Extension/src/panels/styles/solutions-panel.css +++ b/src/PPDS.Extension/src/panels/styles/solutions-panel.css @@ -2,6 +2,22 @@ /* ── Solutions Panel specific styles ──────────────────────────────────────── */ +.toolbar-checkbox { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + color: var(--vscode-foreground); + cursor: pointer; + white-space: nowrap; + user-select: none; +} + +.toolbar-checkbox input[type="checkbox"] { + margin: 0; + cursor: pointer; +} + .content { flex: 1; overflow: auto; @@ -224,3 +240,41 @@ -webkit-box-orient: vertical; overflow: hidden; } + +/* ── Toolbar Search ──────────────────────────────────────────── */ +.toolbar-search { + flex: 1; + min-width: 120px; + max-width: 300px; + background: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-input-border, transparent); + padding: 3px 8px; + border-radius: 2px; + font-size: 12px; + font-family: var(--vscode-font-family); + outline: none; +} + +.toolbar-search:focus { + outline: 1px solid var(--vscode-focusBorder); +} + +.toolbar-search::placeholder { + color: var(--vscode-input-placeholderForeground); +} + +/* ── Toolbar Select ──────────────────────────────────────────── */ +.toolbar-select { + padding: 3px 6px; + background: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-input-border, transparent); + border-radius: 2px; + font-size: 12px; + font-family: var(--vscode-font-family); +} + +.toolbar-select:focus { + outline: 1px solid var(--vscode-focusBorder); +} diff --git a/src/PPDS.Extension/src/panels/styles/web-resources-panel.css b/src/PPDS.Extension/src/panels/styles/web-resources-panel.css index a9a4ca955..eb1318030 100644 --- a/src/PPDS.Extension/src/panels/styles/web-resources-panel.css +++ b/src/PPDS.Extension/src/panels/styles/web-resources-panel.css @@ -27,7 +27,6 @@ text-align: left; padding: 6px 8px; font-size: 11px; - text-transform: uppercase; color: var(--vscode-descriptionForeground); border-bottom: 1px solid var(--vscode-panel-border); } diff --git a/src/PPDS.Extension/src/panels/webview/connection-references-panel.ts b/src/PPDS.Extension/src/panels/webview/connection-references-panel.ts index bc37aa397..517e27627 100644 --- a/src/PPDS.Extension/src/panels/webview/connection-references-panel.ts +++ b/src/PPDS.Extension/src/panels/webview/connection-references-panel.ts @@ -2,7 +2,7 @@ // External webview script for the Connection References panel. // Built by esbuild as IIFE for browser, loaded via