@inkindcards/semantic-layer 2.4.0 → 2.4.1
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/dist/components.cjs +205 -33
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +4 -1
- package/dist/components.d.ts +4 -1
- package/dist/components.js +205 -34
- package/dist/components.js.map +1 -1
- package/package.json +1 -1
package/dist/components.cjs
CHANGED
|
@@ -1083,6 +1083,14 @@ function generateMigrationPrompt(config) {
|
|
|
1083
1083
|
}
|
|
1084
1084
|
|
|
1085
1085
|
// src/components/report-prompt-generator.ts
|
|
1086
|
+
var TIME_PRESET_LABELS = {
|
|
1087
|
+
"12m": "12 months",
|
|
1088
|
+
"6m": "6 months",
|
|
1089
|
+
"3m": "3 months",
|
|
1090
|
+
"1m": "1 month",
|
|
1091
|
+
"1w": "1 week",
|
|
1092
|
+
"1d": "1 day"
|
|
1093
|
+
};
|
|
1086
1094
|
var PALETTE = [
|
|
1087
1095
|
"#3b82f6",
|
|
1088
1096
|
"#10b981",
|
|
@@ -1140,8 +1148,19 @@ function generateReportPrompt(config) {
|
|
|
1140
1148
|
parts.push("};");
|
|
1141
1149
|
parts.push("```");
|
|
1142
1150
|
parts.push("");
|
|
1143
|
-
|
|
1144
|
-
|
|
1151
|
+
const timeFilters = sharedFilters.filter((f) => f.dimension.type === "time_dimension" && f.timePreset);
|
|
1152
|
+
const dimFilters = sharedFilters.filter((f) => f.dimension.type !== "time_dimension" || !f.timePreset);
|
|
1153
|
+
if (timeFilters.length > 0) {
|
|
1154
|
+
parts.push("For time filters, compute a date range relative to today:");
|
|
1155
|
+
for (const f of timeFilters) {
|
|
1156
|
+
parts.push(` - "${f.dimension.name}": default to the last ${TIME_PRESET_LABELS[f.timePreset]}. Compute a start date (e.g. \`new Date(); startDate.setMonth(startDate.getMonth() - N)\`) and pass it as a filter value. Include preset buttons (${Object.values(TIME_PRESET_LABELS).join(", ")}) so the user can switch ranges.`);
|
|
1157
|
+
}
|
|
1158
|
+
parts.push("");
|
|
1159
|
+
}
|
|
1160
|
+
if (dimFilters.length > 0) {
|
|
1161
|
+
parts.push("For each dimension filter dropdown, query the distinct values of the dimension using useSemanticQuery with that dimension as a groupBy and any single metric, then populate the dropdown options from the returned rows.");
|
|
1162
|
+
parts.push("");
|
|
1163
|
+
}
|
|
1145
1164
|
}
|
|
1146
1165
|
parts.push("## Layout");
|
|
1147
1166
|
parts.push("");
|
|
@@ -1296,6 +1315,13 @@ function sanitize(name) {
|
|
|
1296
1315
|
return name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
1297
1316
|
}
|
|
1298
1317
|
var LOVABLE_CHAR_LIMIT = 5e4;
|
|
1318
|
+
function isRunningInIframe() {
|
|
1319
|
+
try {
|
|
1320
|
+
return window.self !== window.top;
|
|
1321
|
+
} catch {
|
|
1322
|
+
return true;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1299
1325
|
function canOpenInLovable(prompt) {
|
|
1300
1326
|
return prompt.length <= LOVABLE_CHAR_LIMIT;
|
|
1301
1327
|
}
|
|
@@ -1441,16 +1467,48 @@ function UnifiedModal({
|
|
|
1441
1467
|
entry,
|
|
1442
1468
|
onClose
|
|
1443
1469
|
}) {
|
|
1470
|
+
const [dragPos, setDragPos] = react.useState(null);
|
|
1471
|
+
const dragRef = react.useRef(null);
|
|
1472
|
+
const modalRef = react.useRef(null);
|
|
1473
|
+
const handleHeaderMouseDown = react.useCallback((e) => {
|
|
1474
|
+
if (e.target.closest("button")) return;
|
|
1475
|
+
e.preventDefault();
|
|
1476
|
+
const modal = modalRef.current;
|
|
1477
|
+
if (!modal) return;
|
|
1478
|
+
const rect = modal.getBoundingClientRect();
|
|
1479
|
+
dragRef.current = { startX: e.clientX, startY: e.clientY, origX: rect.left, origY: rect.top };
|
|
1480
|
+
const onMove = (ev) => {
|
|
1481
|
+
if (!dragRef.current) return;
|
|
1482
|
+
const dx = ev.clientX - dragRef.current.startX;
|
|
1483
|
+
const dy = ev.clientY - dragRef.current.startY;
|
|
1484
|
+
setDragPos({ x: dragRef.current.origX + dx, y: dragRef.current.origY + dy });
|
|
1485
|
+
};
|
|
1486
|
+
const onUp = () => {
|
|
1487
|
+
dragRef.current = null;
|
|
1488
|
+
document.removeEventListener("mousemove", onMove);
|
|
1489
|
+
document.removeEventListener("mouseup", onUp);
|
|
1490
|
+
};
|
|
1491
|
+
document.addEventListener("mousemove", onMove);
|
|
1492
|
+
document.addEventListener("mouseup", onUp);
|
|
1493
|
+
}, []);
|
|
1494
|
+
const positionStyle = dragPos ? { position: "fixed", top: dragPos.y, left: dragPos.x, transform: "none" } : { position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)" };
|
|
1444
1495
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1445
1496
|
/* @__PURE__ */ jsxRuntime.jsx("div", { onClick: onClose, style: backdropStyle }),
|
|
1446
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: modalStyle, children: [
|
|
1447
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1497
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { ref: modalRef, style: { ...modalStyle, ...positionStyle }, children: [
|
|
1498
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1499
|
+
"div",
|
|
1500
|
+
{
|
|
1501
|
+
onMouseDown: handleHeaderMouseDown,
|
|
1502
|
+
style: { ...modalHeaderStyle, cursor: dragRef.current ? "grabbing" : "grab" },
|
|
1503
|
+
children: [
|
|
1504
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 0 }, children: [
|
|
1505
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabButton, { label: "Migrate", active: tab === "migrate", onClick: () => onTabChange("migrate") }),
|
|
1506
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabButton, { label: "Build", active: tab === "build", onClick: () => onTabChange("build") })
|
|
1507
|
+
] }),
|
|
1508
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onClose, style: closeBtnStyle, children: "\xD7" })
|
|
1509
|
+
]
|
|
1510
|
+
}
|
|
1511
|
+
),
|
|
1454
1512
|
tab === "migrate" ? /* @__PURE__ */ jsxRuntime.jsx(MigrateTab, { entry, onClose }) : /* @__PURE__ */ jsxRuntime.jsx(BuildTab, {})
|
|
1455
1513
|
] })
|
|
1456
1514
|
] });
|
|
@@ -1683,7 +1741,14 @@ var WIDGET_TYPES = [
|
|
|
1683
1741
|
function BuildTab() {
|
|
1684
1742
|
const { fields: catalog, isLoading: catalogLoading } = chunkT2C43AAL_cjs.useMetrics();
|
|
1685
1743
|
const allMetrics = react.useMemo(() => catalog.filter((f) => f.type === "metric"), [catalog]);
|
|
1686
|
-
const allDimensions = react.useMemo(() =>
|
|
1744
|
+
const allDimensions = react.useMemo(() => {
|
|
1745
|
+
const dims = catalog.filter((f) => f.type === "dimension" || f.type === "time_dimension");
|
|
1746
|
+
return dims.sort((a, b) => {
|
|
1747
|
+
if (a.name === "metric_time") return -1;
|
|
1748
|
+
if (b.name === "metric_time") return 1;
|
|
1749
|
+
return 0;
|
|
1750
|
+
});
|
|
1751
|
+
}, [catalog]);
|
|
1687
1752
|
const [dashboardTitle, setDashboardTitle] = react.useState("Dashboard");
|
|
1688
1753
|
const [layout, setLayout] = react.useState("dashboard");
|
|
1689
1754
|
const [widgets, setWidgets] = react.useState([]);
|
|
@@ -1692,29 +1757,74 @@ function BuildTab() {
|
|
|
1692
1757
|
const [copied, setCopied] = react.useState(false);
|
|
1693
1758
|
const [lovableSent, setLovableSent] = react.useState(false);
|
|
1694
1759
|
const [previewOpen, setPreviewOpen] = react.useState(false);
|
|
1760
|
+
const [catalogWidth, setCatalogWidth] = react.useState(220);
|
|
1761
|
+
const catalogDragRef = react.useRef(null);
|
|
1762
|
+
const inIframe = react.useMemo(() => isRunningInIframe(), []);
|
|
1763
|
+
const handleCatalogDragStart = react.useCallback((e) => {
|
|
1764
|
+
e.preventDefault();
|
|
1765
|
+
catalogDragRef.current = { startX: e.clientX, startW: catalogWidth };
|
|
1766
|
+
const onMove = (ev) => {
|
|
1767
|
+
if (!catalogDragRef.current) return;
|
|
1768
|
+
const delta = ev.clientX - catalogDragRef.current.startX;
|
|
1769
|
+
setCatalogWidth(Math.max(160, Math.min(400, catalogDragRef.current.startW + delta)));
|
|
1770
|
+
};
|
|
1771
|
+
const onUp = () => {
|
|
1772
|
+
catalogDragRef.current = null;
|
|
1773
|
+
document.removeEventListener("mousemove", onMove);
|
|
1774
|
+
document.removeEventListener("mouseup", onUp);
|
|
1775
|
+
};
|
|
1776
|
+
document.addEventListener("mousemove", onMove);
|
|
1777
|
+
document.addEventListener("mouseup", onUp);
|
|
1778
|
+
}, [catalogWidth]);
|
|
1695
1779
|
const addWidget = react.useCallback((type) => {
|
|
1780
|
+
const isTimeSeries = type === "line" || type === "area" || type === "kpi";
|
|
1781
|
+
const metricTime = allDimensions.find((d) => d.name === "metric_time");
|
|
1696
1782
|
setWidgets((prev) => [...prev, {
|
|
1697
1783
|
id: nextWidgetId(),
|
|
1698
1784
|
title: `${WIDGET_TYPES.find((t) => t.type === type)?.label ?? "Widget"} ${prev.length + 1}`,
|
|
1699
1785
|
type,
|
|
1700
1786
|
metrics: [],
|
|
1701
|
-
groupBy: [],
|
|
1702
|
-
grain: null
|
|
1787
|
+
groupBy: isTimeSeries && metricTime ? [metricTime] : [],
|
|
1788
|
+
grain: isTimeSeries && metricTime ? "month" : null
|
|
1703
1789
|
}]);
|
|
1704
|
-
}, []);
|
|
1790
|
+
}, [allDimensions]);
|
|
1705
1791
|
const removeWidget = react.useCallback((id) => {
|
|
1706
1792
|
setWidgets((prev) => prev.filter((w) => w.id !== id));
|
|
1707
1793
|
}, []);
|
|
1708
1794
|
const updateWidget = react.useCallback((id, patch) => {
|
|
1709
1795
|
setWidgets((prev) => prev.map((w) => w.id === id ? { ...w, ...patch } : w));
|
|
1710
1796
|
}, []);
|
|
1797
|
+
const duplicateWidget = react.useCallback((id) => {
|
|
1798
|
+
setWidgets((prev) => {
|
|
1799
|
+
const source = prev.find((w) => w.id === id);
|
|
1800
|
+
if (!source) return prev;
|
|
1801
|
+
const copy = {
|
|
1802
|
+
...source,
|
|
1803
|
+
id: nextWidgetId(),
|
|
1804
|
+
title: `${source.title} (2)`,
|
|
1805
|
+
metrics: [...source.metrics],
|
|
1806
|
+
groupBy: [...source.groupBy]
|
|
1807
|
+
};
|
|
1808
|
+
const idx = prev.findIndex((w) => w.id === id);
|
|
1809
|
+
return [...prev.slice(0, idx + 1), copy, ...prev.slice(idx + 1)];
|
|
1810
|
+
});
|
|
1811
|
+
}, []);
|
|
1711
1812
|
const addFilter = react.useCallback((dim) => {
|
|
1712
1813
|
if (sharedFilters.some((f) => f.dimension.name === dim.name)) return;
|
|
1713
|
-
setSharedFilters((prev) => [...prev, {
|
|
1814
|
+
setSharedFilters((prev) => [...prev, {
|
|
1815
|
+
dimension: dim,
|
|
1816
|
+
label: dim.displayName,
|
|
1817
|
+
timePreset: dim.type === "time_dimension" ? "12m" : null
|
|
1818
|
+
}]);
|
|
1714
1819
|
}, [sharedFilters]);
|
|
1715
1820
|
const removeFilter = react.useCallback((name) => {
|
|
1716
1821
|
setSharedFilters((prev) => prev.filter((f) => f.dimension.name !== name));
|
|
1717
1822
|
}, []);
|
|
1823
|
+
const updateFilterPreset = react.useCallback((name, preset) => {
|
|
1824
|
+
setSharedFilters((prev) => prev.map(
|
|
1825
|
+
(f) => f.dimension.name === name ? { ...f, timePreset: preset } : f
|
|
1826
|
+
));
|
|
1827
|
+
}, []);
|
|
1718
1828
|
const addFieldToWidget = react.useCallback((widgetId, field) => {
|
|
1719
1829
|
setWidgets((prev) => prev.map((w) => {
|
|
1720
1830
|
if (w.id !== widgetId) return w;
|
|
@@ -1748,11 +1858,17 @@ function BuildTab() {
|
|
|
1748
1858
|
setCopied(true);
|
|
1749
1859
|
setTimeout(() => setCopied(false), 2e3);
|
|
1750
1860
|
}, [prompt]);
|
|
1751
|
-
const handleLovable = react.useCallback(() => {
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1861
|
+
const handleLovable = react.useCallback(async () => {
|
|
1862
|
+
if (inIframe) {
|
|
1863
|
+
await copyToClipboard(prompt);
|
|
1864
|
+
setLovableSent(true);
|
|
1865
|
+
setTimeout(() => setLovableSent(false), 3e3);
|
|
1866
|
+
} else {
|
|
1867
|
+
openInLovable(prompt);
|
|
1868
|
+
setLovableSent(true);
|
|
1869
|
+
setTimeout(() => setLovableSent(false), 3e3);
|
|
1870
|
+
}
|
|
1871
|
+
}, [prompt, inIframe]);
|
|
1756
1872
|
const filteredMetrics = react.useMemo(() => {
|
|
1757
1873
|
const q = catalogSearch.toLowerCase();
|
|
1758
1874
|
if (!q) return allMetrics;
|
|
@@ -1796,7 +1912,7 @@ function BuildTab() {
|
|
|
1796
1912
|
)) })
|
|
1797
1913
|
] }),
|
|
1798
1914
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flex: 1, minHeight: 0 }, children: [
|
|
1799
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { width:
|
|
1915
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { width: catalogWidth, borderRight: "1px solid #e5e7eb", overflow: "auto", flexShrink: 0 }, children: [
|
|
1800
1916
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "8px 12px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1801
1917
|
"input",
|
|
1802
1918
|
{
|
|
@@ -1822,11 +1938,40 @@ function BuildTab() {
|
|
|
1822
1938
|
filteredDimensions.length,
|
|
1823
1939
|
")"
|
|
1824
1940
|
] }),
|
|
1825
|
-
filteredDimensions.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1941
|
+
filteredDimensions.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1942
|
+
CatalogChip,
|
|
1943
|
+
{
|
|
1944
|
+
field: f,
|
|
1945
|
+
widgets,
|
|
1946
|
+
onAddToWidget: addFieldToWidget,
|
|
1947
|
+
onAddAsFilter: addFilter,
|
|
1948
|
+
suffix: f.name === "metric_time" ? " (recommended)" : void 0
|
|
1949
|
+
},
|
|
1950
|
+
f.name
|
|
1951
|
+
)),
|
|
1826
1952
|
filteredDimensions.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "#9ca3af", padding: "4px 0" }, children: "None found" })
|
|
1827
1953
|
] })
|
|
1828
1954
|
] })
|
|
1829
1955
|
] }),
|
|
1956
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1957
|
+
"div",
|
|
1958
|
+
{
|
|
1959
|
+
onMouseDown: handleCatalogDragStart,
|
|
1960
|
+
style: {
|
|
1961
|
+
width: 4,
|
|
1962
|
+
cursor: "col-resize",
|
|
1963
|
+
backgroundColor: "transparent",
|
|
1964
|
+
flexShrink: 0,
|
|
1965
|
+
position: "relative"
|
|
1966
|
+
},
|
|
1967
|
+
onMouseEnter: (e) => {
|
|
1968
|
+
e.currentTarget.style.backgroundColor = "#d1d5db";
|
|
1969
|
+
},
|
|
1970
|
+
onMouseLeave: (e) => {
|
|
1971
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
),
|
|
1830
1975
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "auto", padding: "12px 16px" }, children: widgets.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", padding: "40px 20px", color: "#9ca3af" }, children: [
|
|
1831
1976
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 14, fontWeight: 500, marginBottom: 12 }, children: "Compose your dashboard" }),
|
|
1832
1977
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, marginBottom: 20 }, children: "Add widgets below, then pick metrics and dimensions from the catalog on the left." }),
|
|
@@ -1838,6 +1983,7 @@ function BuildTab() {
|
|
|
1838
1983
|
widget: w,
|
|
1839
1984
|
onUpdate: updateWidget,
|
|
1840
1985
|
onRemove: removeWidget,
|
|
1986
|
+
onDuplicate: duplicateWidget,
|
|
1841
1987
|
onRemoveField: removeFieldFromWidget,
|
|
1842
1988
|
allDimensions
|
|
1843
1989
|
},
|
|
@@ -1847,9 +1993,31 @@ function BuildTab() {
|
|
|
1847
1993
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 16, padding: "12px 0", borderTop: "1px solid #f3f4f6" }, children: [
|
|
1848
1994
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: sectionTitleStyle, children: "Shared Filters" }),
|
|
1849
1995
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "#9ca3af", marginBottom: 8 }, children: "Dimensions that filter all widgets together. Add from the catalog sidebar." }),
|
|
1850
|
-
sharedFilters.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#d1d5db", fontStyle: "italic" }, children: "None added yet" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex",
|
|
1851
|
-
|
|
1852
|
-
|
|
1996
|
+
sharedFilters.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#d1d5db", fontStyle: "italic" }, children: "None added yet" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: sharedFilters.map((f) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1997
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: chipStyle, children: [
|
|
1998
|
+
f.label,
|
|
1999
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => removeFilter(f.dimension.name), style: chipRemoveStyle, children: "\xD7" })
|
|
2000
|
+
] }) }),
|
|
2001
|
+
f.dimension.type === "time_dimension" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: 4, marginTop: 4, marginLeft: 4 }, children: Object.keys(TIME_PRESET_LABELS).map((p) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2002
|
+
"button",
|
|
2003
|
+
{
|
|
2004
|
+
onClick: () => updateFilterPreset(f.dimension.name, p),
|
|
2005
|
+
style: {
|
|
2006
|
+
padding: "2px 8px",
|
|
2007
|
+
fontSize: 10,
|
|
2008
|
+
fontWeight: 600,
|
|
2009
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
2010
|
+
borderRadius: 999,
|
|
2011
|
+
border: f.timePreset === p ? "1px solid #3b82f6" : "1px solid #e5e7eb",
|
|
2012
|
+
backgroundColor: f.timePreset === p ? "#eff6ff" : "#fff",
|
|
2013
|
+
color: f.timePreset === p ? "#3b82f6" : "#9ca3af",
|
|
2014
|
+
cursor: "pointer",
|
|
2015
|
+
transition: "all 0.1s"
|
|
2016
|
+
},
|
|
2017
|
+
children: TIME_PRESET_LABELS[p]
|
|
2018
|
+
},
|
|
2019
|
+
p
|
|
2020
|
+
)) })
|
|
1853
2021
|
] }, f.dimension.name)) })
|
|
1854
2022
|
] }),
|
|
1855
2023
|
canGenerate && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 12 }, children: [
|
|
@@ -1901,8 +2069,8 @@ function BuildTab() {
|
|
|
1901
2069
|
gap: 6
|
|
1902
2070
|
},
|
|
1903
2071
|
children: [
|
|
1904
|
-
lovableSent ? "Opening Lovable..." : "Build in Lovable",
|
|
1905
|
-
!lovableSent && /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2072
|
+
lovableSent ? inIframe ? "Copied! Paste into Lovable chat" : "Opening Lovable..." : inIframe ? "Send to Lovable \u2764\uFE0F" : "Build in Lovable \u2764\uFE0F",
|
|
2073
|
+
!lovableSent && !inIframe && /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1906
2074
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 17L17 7" }),
|
|
1907
2075
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 7h10v10" })
|
|
1908
2076
|
] })
|
|
@@ -1916,7 +2084,8 @@ function CatalogChip({
|
|
|
1916
2084
|
field,
|
|
1917
2085
|
widgets,
|
|
1918
2086
|
onAddToWidget,
|
|
1919
|
-
onAddAsFilter
|
|
2087
|
+
onAddAsFilter,
|
|
2088
|
+
suffix
|
|
1920
2089
|
}) {
|
|
1921
2090
|
const [menuOpen, setMenuOpen] = react.useState(false);
|
|
1922
2091
|
const typeIcon = field.type === "metric" ? "\u2581" : field.type === "time_dimension" ? "\u25F7" : "\u2022";
|
|
@@ -1958,7 +2127,10 @@ ${field.description}`,
|
|
|
1958
2127
|
},
|
|
1959
2128
|
children: [
|
|
1960
2129
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: typeColor, fontSize: 10, flexShrink: 0 }, children: typeIcon }),
|
|
1961
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2130
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: [
|
|
2131
|
+
field.displayName,
|
|
2132
|
+
suffix && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#9ca3af", fontWeight: 400 }, children: suffix })
|
|
2133
|
+
] })
|
|
1962
2134
|
]
|
|
1963
2135
|
}
|
|
1964
2136
|
),
|
|
@@ -2048,6 +2220,7 @@ function WidgetCard({
|
|
|
2048
2220
|
widget,
|
|
2049
2221
|
onUpdate,
|
|
2050
2222
|
onRemove,
|
|
2223
|
+
onDuplicate,
|
|
2051
2224
|
onRemoveField,
|
|
2052
2225
|
allDimensions
|
|
2053
2226
|
}) {
|
|
@@ -2086,6 +2259,7 @@ function WidgetCard({
|
|
|
2086
2259
|
},
|
|
2087
2260
|
wt.type
|
|
2088
2261
|
)) }),
|
|
2262
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => onDuplicate(widget.id), style: { background: "none", border: "none", cursor: "pointer", fontSize: 12, color: "#d1d5db", padding: "0 2px" }, title: "Duplicate widget", children: "\u2398" }),
|
|
2089
2263
|
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => onRemove(widget.id), style: { background: "none", border: "none", cursor: "pointer", fontSize: 14, color: "#d1d5db", padding: "0 2px" }, title: "Remove widget", children: "\xD7" })
|
|
2090
2264
|
] }),
|
|
2091
2265
|
!collapsed && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "8px 12px" }, children: [
|
|
@@ -2144,12 +2318,9 @@ var backdropStyle = {
|
|
|
2144
2318
|
zIndex: 1e5
|
|
2145
2319
|
};
|
|
2146
2320
|
var modalStyle = {
|
|
2147
|
-
position: "fixed",
|
|
2148
|
-
top: "50%",
|
|
2149
|
-
left: "50%",
|
|
2150
|
-
transform: "translate(-50%, -50%)",
|
|
2151
2321
|
zIndex: 100001,
|
|
2152
2322
|
width: "min(900px, 95vw)",
|
|
2323
|
+
height: "85vh",
|
|
2153
2324
|
maxHeight: "85vh",
|
|
2154
2325
|
backgroundColor: "#fff",
|
|
2155
2326
|
borderRadius: 12,
|
|
@@ -2303,6 +2474,7 @@ exports.analyzeChartData = analyzeChartData;
|
|
|
2303
2474
|
exports.canOpenInLovable = canOpenInLovable;
|
|
2304
2475
|
exports.generateMigrationPrompt = generateMigrationPrompt;
|
|
2305
2476
|
exports.generateReportPrompt = generateReportPrompt;
|
|
2477
|
+
exports.isRunningInIframe = isRunningInIframe;
|
|
2306
2478
|
exports.matchField = matchField;
|
|
2307
2479
|
exports.matchFields = matchFields;
|
|
2308
2480
|
exports.openInLovable = openInLovable;
|