diff --git a/dashboard/src/skills/App.tsx b/dashboard/src/skills/App.tsx
index 7813bf4e0..ecb9e3789 100644
--- a/dashboard/src/skills/App.tsx
+++ b/dashboard/src/skills/App.tsx
@@ -20,6 +20,21 @@ import {
/** Number of trailing days shown in every graph. */
const WINDOW_DAYS = 10;
+/**
+ * Build the Azure Data Explorer telemetry dashboard URL for a skill.
+ * The dashboard ID and item fragment are fixed; the skill name is passed
+ * through the `p-_selectedPluginSkill` parameter with a `v-` prefix.
+ */
+export function telemetryUrl(skillName: string): string {
+ const base = "https://dataexplorer.azure.com/dashboards/d1281268-c49e-4e82-bdc9-79e6c3c6cb43";
+ const params = new URLSearchParams({
+ "p-_startTime": "90days",
+ "p-_endTime": "now",
+ "p-_selectedPluginSkill": `v-${skillName}`,
+ });
+ return `${base}?${params}#e9eade80-7b12-49db-a865-a6d3365d03eb`;
+}
+
/** A plugin skill with its description, as surfaced by the frontmatter collector. */
interface Skill {
name: string;
@@ -264,6 +279,16 @@ export default function App() {
Files: {selectedSkill.fileCount}
+
+
+ View telemetry ↗
+
+
diff --git a/dashboard/src/skills/__tests__/telemetryUrl.test.ts b/dashboard/src/skills/__tests__/telemetryUrl.test.ts
new file mode 100644
index 000000000..667c75ffe
--- /dev/null
+++ b/dashboard/src/skills/__tests__/telemetryUrl.test.ts
@@ -0,0 +1,22 @@
+import { describe, it, expect } from "vitest";
+import { telemetryUrl } from "../App";
+
+describe("telemetryUrl", () => {
+ it("builds the dashboard URL for a skill name", () => {
+ const url = new URL(telemetryUrl("azure-deploy"));
+ expect(url.origin).toBe("https://dataexplorer.azure.com");
+ expect(url.pathname).toBe("/dashboards/d1281268-c49e-4e82-bdc9-79e6c3c6cb43");
+ expect(url.searchParams.get("p-_startTime")).toBe("90days");
+ expect(url.searchParams.get("p-_endTime")).toBe("now");
+ expect(url.searchParams.get("p-_selectedPluginSkill")).toBe("v-azure-deploy");
+ expect(url.hash).toBe("#e9eade80-7b12-49db-a865-a6d3365d03eb");
+ });
+
+ it("encodes the skill name into the selectedPluginSkill parameter", () => {
+ const url = new URL(telemetryUrl("azure-cost-management"));
+ expect(url.searchParams.get("p-_selectedPluginSkill")).toBe("v-azure-cost-management");
+ expect(url.searchParams.get("p-_startTime")).toBe("90days");
+ expect(url.searchParams.get("p-_endTime")).toBe("now");
+ expect(url.hash).toBe("#e9eade80-7b12-49db-a865-a6d3365d03eb");
+ });
+});
diff --git a/dashboard/src/skills/skills.css b/dashboard/src/skills/skills.css
index 4b70c6580..ea28b15cb 100644
--- a/dashboard/src/skills/skills.css
+++ b/dashboard/src/skills/skills.css
@@ -89,6 +89,20 @@
font-size: 0.9rem;
}
+.skills-telemetry {
+ margin: 0.5rem 0 0;
+}
+
+.skills-telemetry-link {
+ color: var(--color-focus);
+ font-size: 0.9rem;
+ text-decoration: none;
+}
+
+.skills-telemetry-link:hover {
+ text-decoration: underline;
+}
+
.skills-tests-heading {
margin: 1.5rem 0 0.75rem;
font-size: 1.15rem;