@inkindcards/semantic-layer 2.2.6 → 2.3.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.
@@ -998,48 +998,83 @@ function matchDisplayName(column, catalog) {
998
998
  if (lower.length < 3) return null;
999
999
  return catalog.find((f) => f.displayName.toLowerCase().includes(lower)) ?? catalog.find((f) => f.description.toLowerCase().includes(lower)) ?? null;
1000
1000
  }
1001
+ function matchField(column, catalog) {
1002
+ const exact = matchExact(column, catalog);
1003
+ if (exact) return { field: exact, confidence: "exact" };
1004
+ const normalized = matchNormalized(column, catalog);
1005
+ if (normalized) return { field: normalized, confidence: "fuzzy" };
1006
+ const display = matchDisplayName(column, catalog);
1007
+ if (display) return { field: display, confidence: "fuzzy" };
1008
+ return { field: null, confidence: "manual" };
1009
+ }
1001
1010
  function matchFields(columns, catalog) {
1002
1011
  return columns.map((column) => {
1003
- const exact = matchExact(column, catalog);
1004
- if (exact) return { column, suggestedField: exact, confidence: "exact" };
1005
- const normalized = matchNormalized(column, catalog);
1006
- if (normalized) return { column, suggestedField: normalized, confidence: "fuzzy" };
1007
- const display = matchDisplayName(column, catalog);
1008
- if (display) return { column, suggestedField: display, confidence: "fuzzy" };
1009
- return { column, suggestedField: null, confidence: "manual" };
1012
+ const { field, confidence } = matchField(column, catalog);
1013
+ return { column, suggestedField: field, confidence };
1010
1014
  });
1011
1015
  }
1016
+ function analyzeChartData(columns, sampleValues) {
1017
+ const dimensionColumns = [];
1018
+ const metricColumns = [];
1019
+ for (const col of columns) {
1020
+ const values = sampleValues.map((row) => row[col]).filter((v) => v !== null && v !== void 0);
1021
+ if (values.length === 0) {
1022
+ dimensionColumns.push(col);
1023
+ continue;
1024
+ }
1025
+ const numericCount = values.filter((v) => typeof v === "number" || typeof v === "string" && !isNaN(Number(v)) && v.trim() !== "").length;
1026
+ const isNumeric = numericCount / values.length > 0.5;
1027
+ if (isNumeric) {
1028
+ metricColumns.push(col);
1029
+ } else {
1030
+ dimensionColumns.push(col);
1031
+ }
1032
+ }
1033
+ return { dimensionColumns, metricColumns };
1034
+ }
1012
1035
 
1013
1036
  // src/components/prompt-generator.ts
1014
- function generateMigrationPrompt(componentLabel, mappings) {
1015
- const resolved = mappings.filter((m) => m.field !== null);
1016
- if (resolved.length === 0) {
1037
+ function generateMigrationPrompt(config) {
1038
+ const { componentLabel, dimensions, metrics, grain, originalMetricColumns } = config;
1039
+ if (dimensions.length === 0 && metrics.length === 0) {
1017
1040
  return `Replace the sample/hardcoded data in the "${componentLabel}" component with live data from the semantic layer using useSemanticQuery. Use the DataCatalog to find the right field names.`;
1018
1041
  }
1019
- const metrics = resolved.filter((m) => m.field.type === "metric").map((m) => m.field.name);
1020
- const timeDims = resolved.filter((m) => m.field.type === "time_dimension").map((m) => m.field.name);
1021
- const dims = resolved.filter((m) => m.field.type === "dimension").map((m) => m.field.name);
1022
- const groupBy = [...timeDims, ...dims];
1023
1042
  const parts = [
1024
- `Replace the sample/hardcoded data in the "${componentLabel}" component with a useSemanticQuery call using:`
1043
+ `Replace the sample/hardcoded data in the "${componentLabel}" component with a useSemanticQuery call using:`,
1044
+ ""
1025
1045
  ];
1026
1046
  if (metrics.length > 0) {
1027
- parts.push(` metrics: [${metrics.map((n) => `'${n}'`).join(", ")}]`);
1047
+ parts.push(` metrics: [${metrics.map((m) => `'${m.name}'`).join(", ")}]`);
1028
1048
  }
1029
- if (groupBy.length > 0) {
1049
+ if (dimensions.length > 0) {
1050
+ const groupBy = dimensions.map((d) => d.field.name);
1030
1051
  parts.push(` groupBy: [${groupBy.map((n) => `'${n}'`).join(", ")}]`);
1031
1052
  }
1032
- if (timeDims.length > 0) {
1033
- parts.push(` grain: 'month'`);
1053
+ if (grain) {
1054
+ parts.push(` grain: '${grain}'`);
1034
1055
  }
1035
1056
  parts.push("");
1036
- parts.push("Map the query result columns to the component's props:");
1037
- for (const m of resolved) {
1038
- parts.push(` "${m.column}" \u2192 data column "${m.field.name}"`);
1057
+ const sampleRow = [];
1058
+ for (const d of dimensions) {
1059
+ sampleRow.push(`${d.field.name}: "..."`);
1060
+ }
1061
+ for (const m of metrics) {
1062
+ sampleRow.push(`${m.name}: 1234`);
1063
+ }
1064
+ parts.push("The query returns rows like:");
1065
+ parts.push(` { ${sampleRow.join(", ")} }`);
1066
+ parts.push("");
1067
+ parts.push("Map to the chart's expected format:");
1068
+ for (const d of dimensions) {
1069
+ parts.push(` X-axis / category: "${d.field.name}" (was "${d.column}" in the hardcoded data)`);
1070
+ }
1071
+ if (metrics.length > 0 && originalMetricColumns.length > 0) {
1072
+ parts.push(` Series / values: each metric (${metrics.map((m) => `"${m.name}"`).join(", ")}) becomes a line/area/bar in the chart`);
1073
+ parts.push(` (replaces the old columns: ${originalMetricColumns.map((c) => `"${c}"`).join(", ")})`);
1039
1074
  }
1040
1075
  parts.push("");
1041
1076
  parts.push(
1042
- "Remove the old hardcoded/sample data and any fetch logic it used. Use the data, isLoading, and error values from useSemanticQuery to render the component."
1077
+ "Remove the old hardcoded/sample data and any fetch logic it used. Use the data, isLoading, and error values from useSemanticQuery to render the component. Show a loading spinner while isLoading is true. Show an error message if error is not null."
1043
1078
  );
1044
1079
  return parts.join("\n");
1045
1080
  }
@@ -1149,30 +1184,53 @@ function InspectorModal({
1149
1184
  onClose
1150
1185
  }) {
1151
1186
  const { fields: catalog, error: catalogError, isLoading: catalogLoading } = chunkT2C43AAL_cjs.useMetrics();
1152
- const initialMatches = react.useMemo(
1153
- () => matchFields(entry.columns, catalog),
1154
- [entry.columns, catalog]
1155
- );
1156
- const [mappings, setMappings] = react.useState(
1157
- () => new Map(initialMatches.map((m) => [m.column, m.suggestedField]))
1187
+ const analysis = react.useMemo(
1188
+ () => analyzeChartData(entry.columns, entry.sampleValues),
1189
+ [entry.columns, entry.sampleValues]
1158
1190
  );
1191
+ const dimensions = catalog.filter((f) => f.type === "dimension" || f.type === "time_dimension");
1192
+ const metrics = catalog.filter((f) => f.type === "metric");
1193
+ const initialDimMappings = react.useMemo(() => {
1194
+ const map = /* @__PURE__ */ new Map();
1195
+ for (const col of analysis.dimensionColumns) {
1196
+ const { field } = matchField(col, dimensions);
1197
+ map.set(col, field);
1198
+ }
1199
+ return map;
1200
+ }, [analysis.dimensionColumns, dimensions]);
1201
+ const [dimMappings, setDimMappings] = react.useState(initialDimMappings);
1159
1202
  react.useEffect(() => {
1160
- setMappings(new Map(initialMatches.map((m) => [m.column, m.suggestedField])));
1161
- }, [initialMatches]);
1203
+ setDimMappings(initialDimMappings);
1204
+ }, [initialDimMappings]);
1205
+ const [selectedMetrics, setSelectedMetrics] = react.useState([]);
1206
+ const hasTimeDim = Array.from(dimMappings.values()).some((f) => f?.type === "time_dimension");
1207
+ const [grain, setGrain] = react.useState("month");
1162
1208
  const [copied, setCopied] = react.useState(false);
1163
- const handleFieldChange = react.useCallback((column, field) => {
1164
- setMappings((prev) => {
1209
+ const handleDimChange = react.useCallback((col, field) => {
1210
+ setDimMappings((prev) => {
1165
1211
  const next = new Map(prev);
1166
- next.set(column, field);
1212
+ next.set(col, field);
1167
1213
  return next;
1168
1214
  });
1169
1215
  }, []);
1216
+ const handleToggleMetric = react.useCallback((field) => {
1217
+ setSelectedMetrics(
1218
+ (prev) => prev.some((m) => m.name === field.name) ? prev.filter((m) => m.name !== field.name) : [...prev, field]
1219
+ );
1220
+ }, []);
1170
1221
  const handleGenerate = react.useCallback(async () => {
1171
- const fieldMappings = entry.columns.map((col) => ({
1172
- column: col,
1173
- field: mappings.get(col) ?? null
1174
- }));
1175
- const prompt = generateMigrationPrompt(entry.label, fieldMappings);
1222
+ const resolvedDims = [];
1223
+ for (const [col, field] of dimMappings) {
1224
+ if (field) resolvedDims.push({ column: col, field });
1225
+ }
1226
+ const migrationConfig = {
1227
+ componentLabel: entry.label,
1228
+ dimensions: resolvedDims,
1229
+ metrics: selectedMetrics,
1230
+ grain: hasTimeDim && grain ? grain : null,
1231
+ originalMetricColumns: analysis.metricColumns
1232
+ };
1233
+ const prompt = generateMigrationPrompt(migrationConfig);
1176
1234
  try {
1177
1235
  await navigator.clipboard.writeText(prompt);
1178
1236
  setCopied(true);
@@ -1187,12 +1245,8 @@ function InspectorModal({
1187
1245
  setCopied(true);
1188
1246
  setTimeout(() => setCopied(false), 2e3);
1189
1247
  }
1190
- }, [entry, mappings]);
1191
- const confidenceMap = react.useMemo(() => {
1192
- const map = /* @__PURE__ */ new Map();
1193
- for (const m of initialMatches) map.set(m.column, m.confidence);
1194
- return map;
1195
- }, [initialMatches]);
1248
+ }, [entry, dimMappings, selectedMetrics, grain, hasTimeDim, analysis.metricColumns]);
1249
+ const canGenerate = selectedMetrics.length > 0 || Array.from(dimMappings.values()).some(Boolean);
1196
1250
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1197
1251
  /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: onClose, style: backdropStyle }),
1198
1252
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: modalStyle, children: [
@@ -1206,92 +1260,175 @@ function InspectorModal({
1206
1260
  ] }),
1207
1261
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onClose, style: closeBtnStyle, children: "\xD7" })
1208
1262
  ] }),
1209
- entry.sampleValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, children: [
1210
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: sectionTitleStyle, children: "Current Data Preview" }),
1211
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflow: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs("table", { style: previewTableStyle, children: [
1212
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: entry.columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("th", { style: previewThStyle, children: col }, col)) }) }),
1213
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: entry.sampleValues.map((row, i) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: entry.columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: previewTdStyle, children: row[col] === null || row[col] === void 0 ? "\u2014" : String(row[col]) }, col)) }, i)) })
1214
- ] }) })
1215
- ] }),
1216
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, children: [
1217
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: sectionTitleStyle, children: "Field Mapping" }),
1218
- /* @__PURE__ */ jsxRuntime.jsxs("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: 13 }, children: [
1219
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1220
- /* @__PURE__ */ jsxRuntime.jsx("th", { style: mappingThStyle, children: "Current Column" }),
1221
- /* @__PURE__ */ jsxRuntime.jsx("th", { style: mappingThStyle, children: "Confidence" }),
1222
- /* @__PURE__ */ jsxRuntime.jsx("th", { style: mappingThStyle, children: "Semantic Layer Field" })
1263
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { overflow: "auto", flex: 1 }, children: [
1264
+ entry.sampleValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, children: [
1265
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: sectionTitleStyle, children: "Current Data Preview" }),
1266
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflow: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs("table", { style: previewTableStyle, children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: entry.columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("th", { style: previewThStyle, children: col }, col)) }) }),
1268
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: entry.sampleValues.map((row, i) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: entry.columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: previewTdStyle, children: row[col] === null || row[col] === void 0 ? "\u2014" : String(row[col]) }, col)) }, i)) })
1223
1269
  ] }) }),
1224
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: entry.columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
1225
- MappingRow,
1226
- {
1227
- column: col,
1228
- confidence: confidenceMap.get(col) ?? "manual",
1229
- selectedField: mappings.get(col) ?? null,
1230
- catalog,
1231
- onChange: (field) => handleFieldChange(col, field)
1232
- },
1233
- col
1234
- )) })
1270
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 8, fontSize: 11, color: "#9ca3af" }, children: [
1271
+ "Detected: ",
1272
+ analysis.dimensionColumns.length,
1273
+ " dimension column",
1274
+ analysis.dimensionColumns.length !== 1 ? "s" : "",
1275
+ " (",
1276
+ analysis.dimensionColumns.join(", ") || "none",
1277
+ ")",
1278
+ " \xB7 ",
1279
+ analysis.metricColumns.length,
1280
+ " value column",
1281
+ analysis.metricColumns.length !== 1 ? "s" : "",
1282
+ " (",
1283
+ analysis.metricColumns.join(", ") || "none",
1284
+ ")"
1285
+ ] })
1286
+ ] }),
1287
+ catalog.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...sectionStyle, textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#9ca3af" }, children: catalogLoading ? "Loading catalog..." : catalogError ? `Error loading catalog: ${catalogError}` : "No semantic layer catalog available. Connect to the gateway to enable smart matching." }) }),
1288
+ catalog.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, children: [
1289
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: sectionTitleStyle, children: "Dimensions (Group By)" }),
1290
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "#9ca3af", marginBottom: 8 }, children: "What categories define the rows? (X-axis, grouping)" }),
1291
+ analysis.dimensionColumns.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#9ca3af", fontStyle: "italic" }, children: "No dimension columns detected in the data." }) : /* @__PURE__ */ jsxRuntime.jsxs("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: 13 }, children: [
1292
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1293
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: mappingThStyle, children: "Current Column" }),
1294
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: mappingThStyle, children: "Sample Values" }),
1295
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: mappingThStyle, children: "Semantic Layer Dimension" })
1296
+ ] }) }),
1297
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: analysis.dimensionColumns.map((col) => {
1298
+ const sampleVals = entry.sampleValues.map((row) => row[col]).filter((v) => v !== null && v !== void 0).slice(0, 4).map(String);
1299
+ return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1300
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: mappingTdStyle, children: /* @__PURE__ */ jsxRuntime.jsx("code", { style: { fontSize: 12, color: "#6366f1", fontFamily: "monospace" }, children: col }) }),
1301
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { style: { ...mappingTdStyle, fontSize: 11, color: "#9ca3af" }, children: [
1302
+ sampleVals.join(", "),
1303
+ sampleVals.length >= 4 ? ", ..." : ""
1304
+ ] }),
1305
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: mappingTdStyle, children: /* @__PURE__ */ jsxRuntime.jsxs(
1306
+ "select",
1307
+ {
1308
+ value: dimMappings.get(col)?.name ?? "",
1309
+ onChange: (e) => {
1310
+ const field = dimensions.find((f) => f.name === e.target.value) ?? null;
1311
+ handleDimChange(col, field);
1312
+ },
1313
+ style: selectStyle2,
1314
+ children: [
1315
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "-- select dimension --" }),
1316
+ dimensions.map((f) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: f.name, children: [
1317
+ f.displayName,
1318
+ " (",
1319
+ f.type,
1320
+ ") \u2014 ",
1321
+ f.name
1322
+ ] }, f.name))
1323
+ ]
1324
+ }
1325
+ ) })
1326
+ ] }, col);
1327
+ }) })
1328
+ ] }),
1329
+ hasTimeDim && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 10, display: "flex", alignItems: "center", gap: 8 }, children: [
1330
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: { fontSize: 12, fontWeight: 600, color: "#374151" }, children: "Time Grain:" }),
1331
+ /* @__PURE__ */ jsxRuntime.jsxs(
1332
+ "select",
1333
+ {
1334
+ value: grain,
1335
+ onChange: (e) => setGrain(e.target.value),
1336
+ style: { ...selectStyle2, width: "auto" },
1337
+ children: [
1338
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "day", children: "Day" }),
1339
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "week", children: "Week" }),
1340
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "month", children: "Month" }),
1341
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "quarter", children: "Quarter" }),
1342
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "year", children: "Year" })
1343
+ ]
1344
+ }
1345
+ )
1346
+ ] })
1235
1347
  ] }),
1236
- catalog.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#9ca3af", marginTop: 8, textAlign: "center" }, children: catalogLoading ? "Loading catalog..." : catalogError ? `Error loading catalog: ${catalogError}` : "No semantic layer catalog available. Connect to the gateway to enable smart matching." })
1348
+ catalog.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, children: [
1349
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: sectionTitleStyle, children: "Metrics (Values)" }),
1350
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 11, color: "#9ca3af", marginBottom: 8 }, children: [
1351
+ "Which metrics should be queried? These become the series/values in the chart.",
1352
+ analysis.metricColumns.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1353
+ " (replaces: ",
1354
+ analysis.metricColumns.join(", "),
1355
+ ")"
1356
+ ] })
1357
+ ] }),
1358
+ metrics.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#9ca3af", fontStyle: "italic" }, children: "No metrics available in the catalog." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 4, maxHeight: 200, overflow: "auto" }, children: metrics.map((m) => {
1359
+ const isSelected = selectedMetrics.some((s) => s.name === m.name);
1360
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1361
+ "label",
1362
+ {
1363
+ style: {
1364
+ display: "flex",
1365
+ alignItems: "center",
1366
+ gap: 8,
1367
+ padding: "4px 8px",
1368
+ borderRadius: 4,
1369
+ cursor: "pointer",
1370
+ backgroundColor: isSelected ? "#eff6ff" : "transparent",
1371
+ border: isSelected ? "1px solid #bfdbfe" : "1px solid transparent",
1372
+ fontSize: 12
1373
+ },
1374
+ children: [
1375
+ /* @__PURE__ */ jsxRuntime.jsx(
1376
+ "input",
1377
+ {
1378
+ type: "checkbox",
1379
+ checked: isSelected,
1380
+ onChange: () => handleToggleMetric(m),
1381
+ style: { margin: 0 }
1382
+ }
1383
+ ),
1384
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, color: "#111827" }, children: m.displayName }),
1385
+ /* @__PURE__ */ jsxRuntime.jsx("code", { style: { fontSize: 10, color: "#6366f1", fontFamily: "monospace" }, children: m.name }),
1386
+ m.category && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { marginLeft: "auto", fontSize: 10, color: "#9ca3af" }, children: m.category })
1387
+ ]
1388
+ },
1389
+ m.name
1390
+ );
1391
+ }) }),
1392
+ selectedMetrics.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 8, fontSize: 11, color: "#6b7280" }, children: [
1393
+ "Selected: ",
1394
+ selectedMetrics.map((m) => m.displayName).join(", ")
1395
+ ] })
1396
+ ] }),
1397
+ canGenerate && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, children: [
1398
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: sectionTitleStyle, children: "Query Preview" }),
1399
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { style: {
1400
+ fontSize: 11,
1401
+ fontFamily: "monospace",
1402
+ backgroundColor: "#f9fafb",
1403
+ border: "1px solid #e5e7eb",
1404
+ borderRadius: 6,
1405
+ padding: "8px 12px",
1406
+ whiteSpace: "pre-wrap",
1407
+ color: "#374151",
1408
+ margin: 0
1409
+ }, children: `useSemanticQuery({${selectedMetrics.length > 0 ? `
1410
+ metrics: [${selectedMetrics.map((m) => `'${m.name}'`).join(", ")}],` : ""}${Array.from(dimMappings.values()).some(Boolean) ? `
1411
+ groupBy: [${Array.from(dimMappings.values()).filter(Boolean).map((f) => `'${f.name}'`).join(", ")}],` : ""}${hasTimeDim && grain ? `
1412
+ grain: '${grain}',` : ""}
1413
+ })` })
1414
+ ] })
1237
1415
  ] }),
1238
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "12px 20px", borderTop: "1px solid #e5e7eb" }, children: /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleGenerate, style: generateBtnStyle, children: copied ? "Copied to clipboard!" : "Generate Migration Prompt" }) })
1416
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "12px 20px", borderTop: "1px solid #e5e7eb" }, children: /* @__PURE__ */ jsxRuntime.jsx(
1417
+ "button",
1418
+ {
1419
+ onClick: handleGenerate,
1420
+ disabled: !canGenerate,
1421
+ style: {
1422
+ ...generateBtnStyle,
1423
+ opacity: canGenerate ? 1 : 0.5,
1424
+ cursor: canGenerate ? "pointer" : "not-allowed"
1425
+ },
1426
+ children: copied ? "Copied to clipboard!" : "Generate Migration Prompt"
1427
+ }
1428
+ ) })
1239
1429
  ] })
1240
1430
  ] });
1241
1431
  }
1242
- function MappingRow({
1243
- column,
1244
- confidence,
1245
- selectedField,
1246
- catalog,
1247
- onChange
1248
- }) {
1249
- const badgeColors = {
1250
- exact: { bg: "#dcfce7", color: "#15803d" },
1251
- fuzzy: { bg: "#fef9c3", color: "#a16207" },
1252
- manual: { bg: "#fee2e2", color: "#dc2626" }
1253
- };
1254
- const badge = badgeColors[confidence] ?? badgeColors.manual;
1255
- return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1256
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: mappingTdStyle, children: /* @__PURE__ */ jsxRuntime.jsx("code", { style: { fontSize: 12, color: "#6366f1", fontFamily: "monospace" }, children: column }) }),
1257
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: { ...mappingTdStyle, textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(
1258
- "span",
1259
- {
1260
- style: {
1261
- display: "inline-block",
1262
- padding: "1px 6px",
1263
- fontSize: 10,
1264
- fontWeight: 600,
1265
- borderRadius: 4,
1266
- backgroundColor: badge.bg,
1267
- color: badge.color
1268
- },
1269
- children: confidence
1270
- }
1271
- ) }),
1272
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: mappingTdStyle, children: catalog.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
1273
- "select",
1274
- {
1275
- value: selectedField?.name ?? "",
1276
- onChange: (e) => {
1277
- const field = catalog.find((f) => f.name === e.target.value) ?? null;
1278
- onChange(field);
1279
- },
1280
- style: selectStyle2,
1281
- children: [
1282
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "-- none --" }),
1283
- catalog.map((f) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: f.name, children: [
1284
- f.displayName,
1285
- " (",
1286
- f.type,
1287
- ") \u2014 ",
1288
- f.name
1289
- ] }, f.name))
1290
- ]
1291
- }
1292
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#9ca3af" }, children: selectedField ? selectedField.name : "No catalog" }) })
1293
- ] });
1294
- }
1295
1432
  var backdropStyle = {
1296
1433
  position: "fixed",
1297
1434
  inset: 0,
@@ -1304,8 +1441,8 @@ var modalStyle = {
1304
1441
  left: "50%",
1305
1442
  transform: "translate(-50%, -50%)",
1306
1443
  zIndex: 100001,
1307
- width: "min(640px, 90vw)",
1308
- maxHeight: "80vh",
1444
+ width: "min(700px, 90vw)",
1445
+ maxHeight: "85vh",
1309
1446
  backgroundColor: "#fff",
1310
1447
  borderRadius: 12,
1311
1448
  boxShadow: "0 8px 30px rgba(0,0,0,0.2)",
@@ -1331,9 +1468,7 @@ var closeBtnStyle = {
1331
1468
  lineHeight: 1
1332
1469
  };
1333
1470
  var sectionStyle = {
1334
- padding: "12px 20px",
1335
- overflow: "auto",
1336
- flex: 1
1471
+ padding: "12px 20px"
1337
1472
  };
1338
1473
  var sectionTitleStyle = {
1339
1474
  fontSize: 11,
@@ -1341,7 +1476,7 @@ var sectionTitleStyle = {
1341
1476
  textTransform: "uppercase",
1342
1477
  letterSpacing: "0.05em",
1343
1478
  color: "#6b7280",
1344
- marginBottom: 8
1479
+ marginBottom: 4
1345
1480
  };
1346
1481
  var previewTableStyle = {
1347
1482
  width: "100%",
@@ -1396,8 +1531,7 @@ var generateBtnStyle = {
1396
1531
  color: "#fff",
1397
1532
  backgroundColor: "#3b82f6",
1398
1533
  border: "none",
1399
- borderRadius: 8,
1400
- cursor: "pointer"
1534
+ borderRadius: 8
1401
1535
  };
1402
1536
 
1403
1537
  exports.AdminDashboard = AdminDashboard;
@@ -1406,7 +1540,9 @@ exports.DataInspector = DataInspector;
1406
1540
  exports.Inspectable = Inspectable;
1407
1541
  exports.MetricPicker = MetricPicker;
1408
1542
  exports.ResultsTable = ResultsTable;
1543
+ exports.analyzeChartData = analyzeChartData;
1409
1544
  exports.generateMigrationPrompt = generateMigrationPrompt;
1545
+ exports.matchField = matchField;
1410
1546
  exports.matchFields = matchFields;
1411
1547
  //# sourceMappingURL=components.cjs.map
1412
1548
  //# sourceMappingURL=components.cjs.map