diff --git a/frontend/features/studio/components/SqlPreviewPanel.tsx b/frontend/features/studio/components/SqlPreviewPanel.tsx
new file mode 100644
index 0000000..d937e1c
--- /dev/null
+++ b/frontend/features/studio/components/SqlPreviewPanel.tsx
@@ -0,0 +1,118 @@
+"use client";
+
+import { useState } from "react";
+import type { MeasureDetail } from "../types";
+
+type Props = {
+ measure: MeasureDetail;
+};
+
+function buildSql(measure: MeasureDetail): string {
+ const {
+ policyRef,
+ eligibilityCriteria,
+ exclusions,
+ complianceWindow,
+ requiredDataElements,
+ } = measure;
+
+ // Only parse as days when the string explicitly says "N days" / "N day".
+ // Strings like "Series of 3 doses over 6 months" must not be treated as a day count.
+ const windowMatch = complianceWindow?.match(/(\d+)\s*days?\b/i);
+ const windowDays = windowMatch ? parseInt(windowMatch[1], 10) : null;
+ const dueSoonDays = windowDays ? windowDays - 30 : null;
+
+ const excl = exclusions?.[0];
+ const exclSlug = excl?.criteriaText
+ ? excl.criteriaText.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/g, "")
+ : null;
+ const exclLine = exclSlug
+ ? ` WHEN ep.${exclSlug} = TRUE THEN 'EXCLUDED' -- ${excl!.label}`
+ : ` -- (no exclusion criteria defined in Spec)`;
+
+ const dataComment =
+ (requiredDataElements ?? []).length > 0
+ ? ` -- required: ${requiredDataElements.join(", ")}`
+ : "";
+
+ const enrollComment = eligibilityCriteria?.programEnrollmentText
+ ? `\n -- eligibility: ${eligibilityCriteria.programEnrollmentText}`
+ : "";
+
+ const roleClause = eligibilityCriteria?.roleFilter
+ ? ` AND e.role = '${eligibilityCriteria.roleFilter}'`
+ : ` -- role: unrestricted`;
+
+ const siteClause = eligibilityCriteria?.siteFilter
+ ? ` AND e.site = '${eligibilityCriteria.siteFilter}'`
+ : ` -- site: all sites`;
+
+ const overdueExpr =
+ windowDays !== null
+ ? `NOW() - MAX(exam.date) > INTERVAL '${windowDays} days'`
+ : `/* window: ${complianceWindow ?? "see CQL"} */`;
+
+ const dueSoonExpr =
+ dueSoonDays !== null
+ ? `NOW() - MAX(exam.date) > INTERVAL '${dueSoonDays} days'`
+ : `/* DUE_SOON: see CQL */`;
+
+ return `-- Illustrative analogy only — CQL is the compliance source of truth
+-- Policy: ${policyRef ?? "see Spec tab"}
+SELECT
+ e.id,
+ e.name,
+ e.role,
+ e.site,
+ MAX(exam.date) AS last_exam_date${dataComment ? ",\n" + dataComment : ""}
+ CASE
+${exclLine}
+ WHEN MAX(exam.date) IS NULL THEN 'MISSING_DATA'
+ WHEN ${overdueExpr} THEN 'OVERDUE'
+ WHEN ${dueSoonExpr} THEN 'DUE_SOON'
+ -- DUE_SOON threshold approximate; see CQL for exact window
+ ELSE 'COMPLIANT'
+ END AS outcome_status
+FROM employees e
+JOIN employee_programs ep ON ep.employee_id = e.id${enrollComment}
+LEFT JOIN exams exam ON exam.employee_id = e.id
+WHERE e.active = TRUE
+${roleClause}
+${siteClause}
+GROUP BY e.id, e.name, e.role, e.site, ep.exclusion_flag`;
+}
+
+export function SqlPreviewPanel({ measure }: Props) {
+ const [open, setOpen] = useState(false);
+ const sql = buildSql(measure);
+
+ return (
+
+
+
+ {open && (
+
+
+ Illustrative only
+ Not executed. CQL is the compliance source of truth. Column names and table structure are analogical.
+
+
+ {sql}
+
+
+ )}
+
+ );
+}
diff --git a/frontend/features/studio/components/__tests__/SqlPreviewPanel.test.tsx b/frontend/features/studio/components/__tests__/SqlPreviewPanel.test.tsx
new file mode 100644
index 0000000..30045fe
--- /dev/null
+++ b/frontend/features/studio/components/__tests__/SqlPreviewPanel.test.tsx
@@ -0,0 +1,93 @@
+import React from "react";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { describe, it, expect } from "vitest";
+import { SqlPreviewPanel } from "../SqlPreviewPanel";
+import type { MeasureDetail } from "../../types";
+
+const sampleMeasure: MeasureDetail = {
+ id: "m1",
+ name: "Annual Audiogram Completed",
+ policyRef: "OSHA 29 CFR 1910.95",
+ oshaReferenceId: null,
+ version: "1.0",
+ status: "Active",
+ owner: "Safety",
+ description: "Annual audiogram for employees in hearing conservation.",
+ eligibilityCriteria: {
+ roleFilter: "Safety Technician",
+ siteFilter: "Plant A",
+ programEnrollmentText: "In Hearing Conservation Program",
+ },
+ exclusions: [{ label: "Active Waiver", criteriaText: "Has Active Waiver" }],
+ complianceWindow: "365 days",
+ requiredDataElements: ["Audiogram Date", "Waiver Status"],
+ cqlText: "",
+ compileStatus: "COMPILED",
+ valueSets: [],
+ testFixtures: [],
+};
+
+describe("SqlPreviewPanel", () => {
+ it("renders a collapsed toggle button by default", () => {
+ render(
);
+ expect(screen.getByRole("button", { name: /SQL Analogy/i })).toBeInTheDocument();
+ expect(screen.queryByTestId("sql-preview-block")).toBeNull();
+ });
+
+ it("expands and shows the illustrative-only banner on click", () => {
+ render(
);
+ fireEvent.click(screen.getByRole("button", { name: /SQL Analogy/i }));
+ // The amber banner span uses exact text "Illustrative only"
+ expect(screen.getAllByText(/Illustrative only/i).length).toBeGreaterThanOrEqual(1);
+ expect(screen.getByText(/Not executed\. CQL is the compliance source of truth/i)).toBeInTheDocument();
+ });
+
+ it("shows policy ref and compliance window in the SQL block", () => {
+ render(
);
+ fireEvent.click(screen.getByRole("button", { name: /SQL Analogy/i }));
+ const block = screen.getByTestId("sql-preview-block");
+ expect(block.textContent).toContain("OSHA 29 CFR 1910.95");
+ expect(block.textContent).toContain("365 days");
+ expect(block.textContent).toContain("335 days");
+ });
+
+ it("includes role and site filters from spec", () => {
+ render(
);
+ fireEvent.click(screen.getByRole("button", { name: /SQL Analogy/i }));
+ const block = screen.getByTestId("sql-preview-block");
+ expect(block.textContent).toContain("Safety Technician");
+ expect(block.textContent).toContain("Plant A");
+ });
+
+ it("includes the approximate DUE_SOON comment", () => {
+ render(
);
+ fireEvent.click(screen.getByRole("button", { name: /SQL Analogy/i }));
+ const block = screen.getByTestId("sql-preview-block");
+ expect(block.textContent).toContain("DUE_SOON threshold approximate; see CQL for exact window");
+ });
+
+ it("collapses again on second click", () => {
+ render(
);
+ fireEvent.click(screen.getByRole("button", { name: /SQL Analogy/i }));
+ fireEvent.click(screen.getByRole("button", { name: /SQL Analogy/i }));
+ expect(screen.queryByTestId("sql-preview-block")).toBeNull();
+ });
+
+ it("renders fallback text when compliance window has no numeric value", () => {
+ const noWindow: MeasureDetail = { ...sampleMeasure, complianceWindow: "see policy" };
+ render(
);
+ fireEvent.click(screen.getByRole("button", { name: /SQL Analogy/i }));
+ const block = screen.getByTestId("sql-preview-block");
+ expect(block.textContent).toContain("see policy");
+ });
+
+ it("uses raw-string fallback for multi-number non-day windows like 'Series of 3 doses over 6 months'", () => {
+ const doseWindow: MeasureDetail = { ...sampleMeasure, complianceWindow: "Series of 3 doses over 6 months" };
+ render(
);
+ fireEvent.click(screen.getByRole("button", { name: /SQL Analogy/i }));
+ const block = screen.getByTestId("sql-preview-block");
+ // Should pass the window through as a comment, not parse '3' as a day count
+ expect(block.textContent).toContain("Series of 3 doses over 6 months");
+ expect(block.textContent).not.toContain("INTERVAL '3 days'");
+ });
+});