@meetreeve/ui 0.9.0 → 0.11.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.
- package/dist/console/index.d.mts +65 -0
- package/dist/console/index.d.ts +65 -0
- package/dist/console/index.js +3528 -0
- package/dist/console/index.mjs +3490 -0
- package/dist/index.d.mts +106 -1
- package/dist/index.d.ts +106 -1
- package/dist/index.js +440 -11
- package/dist/index.mjs +443 -14
- package/package.json +10 -1
package/dist/index.mjs
CHANGED
|
@@ -1273,7 +1273,7 @@ function DropdownMenuLabel({
|
|
|
1273
1273
|
}
|
|
1274
1274
|
|
|
1275
1275
|
// src/context-switcher.tsx
|
|
1276
|
-
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1276
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1277
1277
|
function groupByOrg(items) {
|
|
1278
1278
|
return groupItemsByOrg(items, {
|
|
1279
1279
|
orgId: (it) => it.orgId,
|
|
@@ -1348,6 +1348,10 @@ function ContextSwitcher({
|
|
|
1348
1348
|
children: [
|
|
1349
1349
|
/* @__PURE__ */ jsx6(BrandGlyph, { brand: toBrand(item), subtle: true }),
|
|
1350
1350
|
/* @__PURE__ */ jsx6("span", { className: "flex-1 truncate text-left text-foreground/90", children: item.name }),
|
|
1351
|
+
item.kindLabel && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1352
|
+
" ",
|
|
1353
|
+
/* @__PURE__ */ jsx6("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: item.kindLabel })
|
|
1354
|
+
] }),
|
|
1351
1355
|
isActive && /* @__PURE__ */ jsx6(Check, { className: "size-3.5 text-emerald-500" })
|
|
1352
1356
|
]
|
|
1353
1357
|
},
|
|
@@ -1364,7 +1368,7 @@ function ContextSwitcher({
|
|
|
1364
1368
|
|
|
1365
1369
|
// src/connector-picker.tsx
|
|
1366
1370
|
import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef5, useState as useState7 } from "react";
|
|
1367
|
-
import { Fragment as
|
|
1371
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1368
1372
|
var DEFAULT_CONNECT_ERROR = "Couldn't start the connection. Please try again.";
|
|
1369
1373
|
var DEFAULT_DISCONNECT_ERROR = "Couldn't disconnect. Please try again.";
|
|
1370
1374
|
var DEFAULT_LOAD_ERROR = "Couldn't load connections. Please try again.";
|
|
@@ -1372,7 +1376,17 @@ var DEFAULT_GRANTS_LOAD_ERROR = "Couldn't load sharing. Please try again.";
|
|
|
1372
1376
|
var DEFAULT_GRANT_ERROR = "Couldn't share. Please try again.";
|
|
1373
1377
|
var DEFAULT_REVOKE_ERROR = "Couldn't revoke access. Please try again.";
|
|
1374
1378
|
var DEFAULT_ORG_DISABLED_REASON = "Org-wide connections aren't available to you.";
|
|
1379
|
+
var DEFAULT_CREDENTIAL_ERROR = "Couldn't save that credential. Please try again.";
|
|
1380
|
+
var DEFAULT_CREDENTIAL_TEST_ERROR = "Couldn't test that credential. Please try again.";
|
|
1381
|
+
var UNVERIFIABLE_CREDENTIAL_NOTE = "We can't verify this automatically \u2014 saving it is safe, but nothing has checked it yet.";
|
|
1375
1382
|
var SKELETON_COUNT = 3;
|
|
1383
|
+
var UNGROUNDED_PLACEHOLDER = /\{\{[^}]*\}\}/;
|
|
1384
|
+
function hasUngroundedPlaceholder(text) {
|
|
1385
|
+
return UNGROUNDED_PLACEHOLDER.test(text);
|
|
1386
|
+
}
|
|
1387
|
+
function isDevEnvironment() {
|
|
1388
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
1389
|
+
}
|
|
1376
1390
|
function capabilityLabel(capability) {
|
|
1377
1391
|
if (capability === "read") return "Read only";
|
|
1378
1392
|
if (capability === "write") return "Write only";
|
|
@@ -1409,6 +1423,8 @@ function ConnectorPicker({
|
|
|
1409
1423
|
const [loadError, setLoadError] = useState7(null);
|
|
1410
1424
|
const [uiState, setUiState] = useState7({});
|
|
1411
1425
|
const grantsSupported = typeof client.listGrants === "function" && typeof client.grant === "function" && typeof client.revokeGrant === "function";
|
|
1426
|
+
const credentialsSupported = typeof client.submitCredentials === "function";
|
|
1427
|
+
const credentialTestSupported = typeof client.testCredentials === "function";
|
|
1412
1428
|
const clientRef = useRef5(client);
|
|
1413
1429
|
clientRef.current = client;
|
|
1414
1430
|
const purposeRef = useRef5(purpose);
|
|
@@ -1492,6 +1508,38 @@ function ConnectorPicker({
|
|
|
1492
1508
|
},
|
|
1493
1509
|
[load]
|
|
1494
1510
|
);
|
|
1511
|
+
const handleSubmitCredentials = useCallback4(
|
|
1512
|
+
async (entry, values, kind) => {
|
|
1513
|
+
setUiState((prev) => ({ ...prev, [entry.slug]: { kind: "connecting" } }));
|
|
1514
|
+
try {
|
|
1515
|
+
await clientRef.current.submitCredentials?.(entry, values, { kind });
|
|
1516
|
+
const list = await load();
|
|
1517
|
+
const updated = list?.find((e) => e.slug === entry.slug) ?? null;
|
|
1518
|
+
setUiState((prev) => {
|
|
1519
|
+
const next = { ...prev };
|
|
1520
|
+
delete next[entry.slug];
|
|
1521
|
+
return next;
|
|
1522
|
+
});
|
|
1523
|
+
if (updated?.connection.status === "connected") {
|
|
1524
|
+
onConnectedRef.current?.(updated);
|
|
1525
|
+
} else {
|
|
1526
|
+
throw new Error(DEFAULT_CREDENTIAL_ERROR);
|
|
1527
|
+
}
|
|
1528
|
+
} catch (err) {
|
|
1529
|
+
setUiState((prev) => {
|
|
1530
|
+
const next = { ...prev };
|
|
1531
|
+
delete next[entry.slug];
|
|
1532
|
+
return next;
|
|
1533
|
+
});
|
|
1534
|
+
throw err;
|
|
1535
|
+
}
|
|
1536
|
+
},
|
|
1537
|
+
[load]
|
|
1538
|
+
);
|
|
1539
|
+
const testCredentials = useCallback4(
|
|
1540
|
+
(entry, values) => clientRef.current.testCredentials?.(entry, values) ?? Promise.resolve({ valid: null }),
|
|
1541
|
+
[]
|
|
1542
|
+
);
|
|
1495
1543
|
const handleDisconnect = useCallback4(
|
|
1496
1544
|
async (entry) => {
|
|
1497
1545
|
setUiState((prev) => ({ ...prev, [entry.slug]: { kind: "disconnecting" } }));
|
|
@@ -1564,8 +1612,12 @@ function ConnectorPicker({
|
|
|
1564
1612
|
ui: uiState[entry.slug],
|
|
1565
1613
|
layout,
|
|
1566
1614
|
grantsSupported,
|
|
1615
|
+
credentialsSupported,
|
|
1616
|
+
credentialTestSupported,
|
|
1567
1617
|
onConnect: handleConnect,
|
|
1568
1618
|
onDisconnect: handleDisconnect,
|
|
1619
|
+
onSubmitCredentials: handleSubmitCredentials,
|
|
1620
|
+
onTestCredentials: testCredentials,
|
|
1569
1621
|
listGrants,
|
|
1570
1622
|
addGrant,
|
|
1571
1623
|
revokeGrant
|
|
@@ -1583,8 +1635,12 @@ function ConnectorPicker({
|
|
|
1583
1635
|
layout,
|
|
1584
1636
|
shared: true,
|
|
1585
1637
|
grantsSupported,
|
|
1638
|
+
credentialsSupported,
|
|
1639
|
+
credentialTestSupported,
|
|
1586
1640
|
onConnect: handleConnect,
|
|
1587
1641
|
onDisconnect: handleDisconnect,
|
|
1642
|
+
onSubmitCredentials: handleSubmitCredentials,
|
|
1643
|
+
onTestCredentials: testCredentials,
|
|
1588
1644
|
listGrants,
|
|
1589
1645
|
addGrant,
|
|
1590
1646
|
revokeGrant
|
|
@@ -1600,8 +1656,12 @@ function ConnectorTile({
|
|
|
1600
1656
|
layout,
|
|
1601
1657
|
shared = false,
|
|
1602
1658
|
grantsSupported,
|
|
1659
|
+
credentialsSupported,
|
|
1660
|
+
credentialTestSupported,
|
|
1603
1661
|
onConnect,
|
|
1604
1662
|
onDisconnect,
|
|
1663
|
+
onSubmitCredentials,
|
|
1664
|
+
onTestCredentials,
|
|
1605
1665
|
listGrants,
|
|
1606
1666
|
addGrant,
|
|
1607
1667
|
revokeGrant
|
|
@@ -1612,6 +1672,11 @@ function ConnectorTile({
|
|
|
1612
1672
|
const errorMessage = ui?.kind === "error" ? ui.message : null;
|
|
1613
1673
|
const [kind, setKind] = useState7("personal");
|
|
1614
1674
|
const [sharingOpen, setSharingOpen] = useState7(false);
|
|
1675
|
+
const [setupOpen, setSetupOpen] = useState7(false);
|
|
1676
|
+
const credentialFields = entry.credential_fields ?? [];
|
|
1677
|
+
const pasteSupported = credentialsSupported && credentialFields.length > 0;
|
|
1678
|
+
const guide = entry.guide ?? null;
|
|
1679
|
+
const canSetUp = !isConnected && (pasteSupported || guide !== null);
|
|
1615
1680
|
const orgPermitted = entry.org_connect?.permitted === true;
|
|
1616
1681
|
const orgDisabledReason = entry.org_connect?.reason || DEFAULT_ORG_DISABLED_REASON;
|
|
1617
1682
|
const attribution = ownerLabel(entry);
|
|
@@ -1641,7 +1706,7 @@ function ConnectorTile({
|
|
|
1641
1706
|
] }),
|
|
1642
1707
|
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
1643
1708
|
/* @__PURE__ */ jsx7("span", { "data-testid": `connector-capability-${entry.slug}`, children: capabilityLabel(entry.capability) }),
|
|
1644
|
-
entry.category && /* @__PURE__ */ jsxs6(
|
|
1709
|
+
entry.category && /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
1645
1710
|
/* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
1646
1711
|
/* @__PURE__ */ jsx7("span", { className: "truncate", children: entry.category })
|
|
1647
1712
|
] })
|
|
@@ -1738,18 +1803,55 @@ function ConnectorTile({
|
|
|
1738
1803
|
children: sharingOpen ? "Hide sharing" : "Manage sharing"
|
|
1739
1804
|
}
|
|
1740
1805
|
)
|
|
1741
|
-
] }) : /* @__PURE__ */
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1806
|
+
] }) : /* @__PURE__ */ jsxs6("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [
|
|
1807
|
+
/* @__PURE__ */ jsx7(
|
|
1808
|
+
"button",
|
|
1809
|
+
{
|
|
1810
|
+
type: "button",
|
|
1811
|
+
"data-testid": `connector-connect-${entry.slug}`,
|
|
1812
|
+
onClick: () => onConnect(entry, kind),
|
|
1813
|
+
disabled: isConnecting,
|
|
1814
|
+
className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
|
|
1815
|
+
children: isConnecting ? "Connecting\u2026" : "Connect"
|
|
1816
|
+
}
|
|
1817
|
+
),
|
|
1818
|
+
canSetUp && /* @__PURE__ */ jsx7(
|
|
1819
|
+
"button",
|
|
1820
|
+
{
|
|
1821
|
+
type: "button",
|
|
1822
|
+
"data-testid": `connector-setup-toggle-${entry.slug}`,
|
|
1823
|
+
"aria-expanded": setupOpen,
|
|
1824
|
+
"aria-controls": `connector-setup-panel-${entry.slug}`,
|
|
1825
|
+
onClick: () => setSetupOpen((open) => !open),
|
|
1826
|
+
className: "shrink-0 rounded-md px-2.5 py-1 text-xs font-medium text-muted-foreground hover:bg-foreground/10",
|
|
1827
|
+
children: setupOpen ? "Hide setup" : pasteSupported ? "Use a token instead" : "Setup guide"
|
|
1828
|
+
}
|
|
1829
|
+
)
|
|
1830
|
+
] })
|
|
1752
1831
|
] }),
|
|
1832
|
+
canSetUp && setupOpen && /* @__PURE__ */ jsxs6(
|
|
1833
|
+
"div",
|
|
1834
|
+
{
|
|
1835
|
+
id: `connector-setup-panel-${entry.slug}`,
|
|
1836
|
+
"data-testid": `connector-setup-panel-${entry.slug}`,
|
|
1837
|
+
className: "mt-2 flex flex-col gap-3 rounded-md border border-border bg-foreground/5 p-2",
|
|
1838
|
+
children: [
|
|
1839
|
+
guide && /* @__PURE__ */ jsx7(SetupGuidePanel, { entry, guide, showVerify: !pasteSupported }),
|
|
1840
|
+
pasteSupported && /* @__PURE__ */ jsx7(
|
|
1841
|
+
CredentialForm,
|
|
1842
|
+
{
|
|
1843
|
+
entry,
|
|
1844
|
+
fields: credentialFields,
|
|
1845
|
+
kind,
|
|
1846
|
+
verify: guide?.verify ?? null,
|
|
1847
|
+
testSupported: credentialTestSupported,
|
|
1848
|
+
onSubmitCredentials,
|
|
1849
|
+
onTestCredentials
|
|
1850
|
+
}
|
|
1851
|
+
)
|
|
1852
|
+
]
|
|
1853
|
+
}
|
|
1854
|
+
),
|
|
1753
1855
|
canManageSharing && sharingOpen && /* @__PURE__ */ jsx7(
|
|
1754
1856
|
ManageSharing,
|
|
1755
1857
|
{
|
|
@@ -1763,6 +1865,333 @@ function ConnectorTile({
|
|
|
1763
1865
|
}
|
|
1764
1866
|
);
|
|
1765
1867
|
}
|
|
1868
|
+
function groundGuide(guide) {
|
|
1869
|
+
const ungrounded = [];
|
|
1870
|
+
const keep = (text) => {
|
|
1871
|
+
if (!hasUngroundedPlaceholder(text)) return true;
|
|
1872
|
+
ungrounded.push(text);
|
|
1873
|
+
return false;
|
|
1874
|
+
};
|
|
1875
|
+
const prerequisites = guide.prerequisites.filter(keep);
|
|
1876
|
+
const steps = guide.steps.filter(
|
|
1877
|
+
(step) => keep(step.text) && (!step.copyable || keep(step.copyable))
|
|
1878
|
+
);
|
|
1879
|
+
const scopeNote = keep(guide.scope_note) ? guide.scope_note : null;
|
|
1880
|
+
const verify = keep(guide.verify) ? guide.verify : null;
|
|
1881
|
+
return { prerequisites, steps, scopeNote, verify, ungrounded };
|
|
1882
|
+
}
|
|
1883
|
+
function CopyableValue({ value, testId }) {
|
|
1884
|
+
const [copied, setCopied] = useState7(false);
|
|
1885
|
+
const timer = useRef5(null);
|
|
1886
|
+
useEffect6(
|
|
1887
|
+
() => () => {
|
|
1888
|
+
if (timer.current) clearTimeout(timer.current);
|
|
1889
|
+
},
|
|
1890
|
+
[]
|
|
1891
|
+
);
|
|
1892
|
+
const copy = useCallback4(async () => {
|
|
1893
|
+
try {
|
|
1894
|
+
if (typeof navigator.clipboard?.writeText !== "function") {
|
|
1895
|
+
throw new Error("clipboard unavailable");
|
|
1896
|
+
}
|
|
1897
|
+
await navigator.clipboard.writeText(value);
|
|
1898
|
+
setCopied(true);
|
|
1899
|
+
if (timer.current) clearTimeout(timer.current);
|
|
1900
|
+
timer.current = setTimeout(() => setCopied(false), 1500);
|
|
1901
|
+
} catch {
|
|
1902
|
+
setCopied(false);
|
|
1903
|
+
}
|
|
1904
|
+
}, [value]);
|
|
1905
|
+
return /* @__PURE__ */ jsxs6("span", { className: "mt-1 flex items-center gap-1.5", children: [
|
|
1906
|
+
/* @__PURE__ */ jsx7(
|
|
1907
|
+
"code",
|
|
1908
|
+
{
|
|
1909
|
+
"data-testid": `${testId}-value`,
|
|
1910
|
+
className: "min-w-0 flex-1 truncate rounded bg-foreground/10 px-1.5 py-0.5 text-[11px]",
|
|
1911
|
+
children: value
|
|
1912
|
+
}
|
|
1913
|
+
),
|
|
1914
|
+
/* @__PURE__ */ jsx7(
|
|
1915
|
+
"button",
|
|
1916
|
+
{
|
|
1917
|
+
type: "button",
|
|
1918
|
+
"data-testid": testId,
|
|
1919
|
+
onClick: () => void copy(),
|
|
1920
|
+
className: "shrink-0 rounded-md px-1.5 py-0.5 text-[11px] font-medium text-muted-foreground hover:bg-foreground/10",
|
|
1921
|
+
children: copied ? "Copied" : "Copy"
|
|
1922
|
+
}
|
|
1923
|
+
)
|
|
1924
|
+
] });
|
|
1925
|
+
}
|
|
1926
|
+
function SetupGuidePanel({
|
|
1927
|
+
entry,
|
|
1928
|
+
guide,
|
|
1929
|
+
showVerify
|
|
1930
|
+
}) {
|
|
1931
|
+
const { prerequisites, steps, scopeNote, verify, ungrounded } = groundGuide(guide);
|
|
1932
|
+
const leaked = ungrounded.length > 0;
|
|
1933
|
+
useEffect6(() => {
|
|
1934
|
+
if (!leaked) return;
|
|
1935
|
+
console.error(
|
|
1936
|
+
`[ConnectorPicker] setup guide for "${entry.slug}" arrived with ungrounded placeholders:`,
|
|
1937
|
+
ungrounded
|
|
1938
|
+
);
|
|
1939
|
+
}, [entry.slug, guide, leaked]);
|
|
1940
|
+
return /* @__PURE__ */ jsxs6("div", { "data-testid": `connector-guide-${entry.slug}`, className: "flex flex-col gap-2", children: [
|
|
1941
|
+
leaked && isDevEnvironment() && /* @__PURE__ */ jsxs6(
|
|
1942
|
+
"p",
|
|
1943
|
+
{
|
|
1944
|
+
role: "alert",
|
|
1945
|
+
"data-testid": `connector-guide-ungrounded-${entry.slug}`,
|
|
1946
|
+
className: "rounded-md border border-destructive/30 bg-destructive/5 px-2 py-1 text-xs text-destructive",
|
|
1947
|
+
children: [
|
|
1948
|
+
"Setup guide arrived with unresolved placeholders and those lines were dropped: ",
|
|
1949
|
+
ungrounded.join(" \xB7 ")
|
|
1950
|
+
]
|
|
1951
|
+
}
|
|
1952
|
+
),
|
|
1953
|
+
prerequisites.length > 0 && /* @__PURE__ */ jsxs6("div", { children: [
|
|
1954
|
+
/* @__PURE__ */ jsx7("h4", { className: "text-xs font-medium", children: "Before you start" }),
|
|
1955
|
+
/* @__PURE__ */ jsx7(
|
|
1956
|
+
"ul",
|
|
1957
|
+
{
|
|
1958
|
+
"data-testid": `connector-guide-prerequisites-${entry.slug}`,
|
|
1959
|
+
className: "mt-1 list-disc pl-4 text-xs text-muted-foreground",
|
|
1960
|
+
children: prerequisites.map((item) => /* @__PURE__ */ jsx7("li", { children: item }, item))
|
|
1961
|
+
}
|
|
1962
|
+
)
|
|
1963
|
+
] }),
|
|
1964
|
+
steps.length > 0 && /* @__PURE__ */ jsx7(
|
|
1965
|
+
"ol",
|
|
1966
|
+
{
|
|
1967
|
+
"data-testid": `connector-guide-steps-${entry.slug}`,
|
|
1968
|
+
className: "list-decimal pl-4 text-xs",
|
|
1969
|
+
children: steps.map((step, index) => /* @__PURE__ */ jsxs6("li", { className: "mt-1 first:mt-0", children: [
|
|
1970
|
+
/* @__PURE__ */ jsx7("span", { children: step.text }),
|
|
1971
|
+
step.copyable && /* @__PURE__ */ jsx7(
|
|
1972
|
+
CopyableValue,
|
|
1973
|
+
{
|
|
1974
|
+
value: step.copyable,
|
|
1975
|
+
testId: `connector-guide-copy-${entry.slug}-${index}`
|
|
1976
|
+
}
|
|
1977
|
+
)
|
|
1978
|
+
] }, step.text))
|
|
1979
|
+
}
|
|
1980
|
+
),
|
|
1981
|
+
scopeNote && /* @__PURE__ */ jsx7(
|
|
1982
|
+
"p",
|
|
1983
|
+
{
|
|
1984
|
+
"data-testid": `connector-guide-scope-note-${entry.slug}`,
|
|
1985
|
+
className: "rounded-md border border-primary/30 bg-primary/5 px-2 py-1 text-xs font-medium",
|
|
1986
|
+
children: scopeNote
|
|
1987
|
+
}
|
|
1988
|
+
),
|
|
1989
|
+
showVerify && verify && /* @__PURE__ */ jsx7(
|
|
1990
|
+
"p",
|
|
1991
|
+
{
|
|
1992
|
+
"data-testid": `connector-guide-verify-${entry.slug}`,
|
|
1993
|
+
className: "text-xs text-muted-foreground",
|
|
1994
|
+
children: verify
|
|
1995
|
+
}
|
|
1996
|
+
)
|
|
1997
|
+
] });
|
|
1998
|
+
}
|
|
1999
|
+
function CredentialForm({
|
|
2000
|
+
entry,
|
|
2001
|
+
fields,
|
|
2002
|
+
kind,
|
|
2003
|
+
verify,
|
|
2004
|
+
testSupported,
|
|
2005
|
+
onSubmitCredentials,
|
|
2006
|
+
onTestCredentials
|
|
2007
|
+
}) {
|
|
2008
|
+
const [values, setValues] = useState7(() => {
|
|
2009
|
+
const initial = {};
|
|
2010
|
+
for (const field of fields) {
|
|
2011
|
+
if (field.defaultValue) initial[field.key] = field.defaultValue;
|
|
2012
|
+
}
|
|
2013
|
+
return initial;
|
|
2014
|
+
});
|
|
2015
|
+
const [busy, setBusy] = useState7(false);
|
|
2016
|
+
const [testing, setTesting] = useState7(false);
|
|
2017
|
+
const [error, setError] = useState7(null);
|
|
2018
|
+
const [result, setResult] = useState7(null);
|
|
2019
|
+
const entryRef = useRef5(entry);
|
|
2020
|
+
entryRef.current = entry;
|
|
2021
|
+
const updateField = useCallback4((key, value) => {
|
|
2022
|
+
setValues((prev) => ({ ...prev, [key]: value }));
|
|
2023
|
+
setError(null);
|
|
2024
|
+
setResult(null);
|
|
2025
|
+
}, []);
|
|
2026
|
+
const missingRequired = fields.filter((field) => field.required !== false).some((field) => !values[field.key]?.trim());
|
|
2027
|
+
const failedTest = result?.valid === false;
|
|
2028
|
+
const untested = testSupported && result === null;
|
|
2029
|
+
const fieldErrors = result?.field_errors ?? null;
|
|
2030
|
+
const handleTest = useCallback4(async () => {
|
|
2031
|
+
setTesting(true);
|
|
2032
|
+
setError(null);
|
|
2033
|
+
setResult(null);
|
|
2034
|
+
try {
|
|
2035
|
+
setResult(await onTestCredentials(entryRef.current, values));
|
|
2036
|
+
} catch (err) {
|
|
2037
|
+
setError(errorMessageFrom(err, DEFAULT_CREDENTIAL_TEST_ERROR));
|
|
2038
|
+
} finally {
|
|
2039
|
+
setTesting(false);
|
|
2040
|
+
}
|
|
2041
|
+
}, [onTestCredentials, values]);
|
|
2042
|
+
const handleSubmit = useCallback4(async () => {
|
|
2043
|
+
setBusy(true);
|
|
2044
|
+
setError(null);
|
|
2045
|
+
try {
|
|
2046
|
+
await onSubmitCredentials(entryRef.current, values, kind);
|
|
2047
|
+
} catch (err) {
|
|
2048
|
+
setError(errorMessageFrom(err, DEFAULT_CREDENTIAL_ERROR));
|
|
2049
|
+
} finally {
|
|
2050
|
+
setBusy(false);
|
|
2051
|
+
}
|
|
2052
|
+
}, [kind, onSubmitCredentials, values]);
|
|
2053
|
+
return /* @__PURE__ */ jsxs6(
|
|
2054
|
+
"form",
|
|
2055
|
+
{
|
|
2056
|
+
"data-testid": `connector-credential-form-${entry.slug}`,
|
|
2057
|
+
onSubmit: (event) => {
|
|
2058
|
+
event.preventDefault();
|
|
2059
|
+
if (missingRequired || busy || testing || failedTest || untested) return;
|
|
2060
|
+
void handleSubmit();
|
|
2061
|
+
},
|
|
2062
|
+
className: "flex flex-col gap-2",
|
|
2063
|
+
children: [
|
|
2064
|
+
fields.map((field) => {
|
|
2065
|
+
const inputId = `connector-credential-${entry.slug}-${field.key}`;
|
|
2066
|
+
const fieldError = fieldErrors?.[field.key] ?? null;
|
|
2067
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", children: [
|
|
2068
|
+
/* @__PURE__ */ jsxs6("label", { htmlFor: inputId, className: "text-xs font-medium", children: [
|
|
2069
|
+
field.label,
|
|
2070
|
+
field.required !== false && /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", className: "ml-0.5 text-destructive", children: "*" })
|
|
2071
|
+
] }),
|
|
2072
|
+
/* @__PURE__ */ jsx7(
|
|
2073
|
+
"input",
|
|
2074
|
+
{
|
|
2075
|
+
id: inputId,
|
|
2076
|
+
"data-testid": `connector-credential-input-${entry.slug}-${field.key}`,
|
|
2077
|
+
type: field.type ?? "text",
|
|
2078
|
+
value: values[field.key] ?? "",
|
|
2079
|
+
onChange: (event) => updateField(field.key, event.target.value),
|
|
2080
|
+
placeholder: field.placeholder ?? void 0,
|
|
2081
|
+
required: field.required !== false,
|
|
2082
|
+
disabled: busy || testing,
|
|
2083
|
+
autoComplete: "off",
|
|
2084
|
+
"aria-invalid": fieldError ? true : void 0,
|
|
2085
|
+
"aria-describedby": fieldError ? `${inputId}-error` : void 0,
|
|
2086
|
+
className: cn(
|
|
2087
|
+
"rounded-md border bg-transparent px-1.5 py-1 text-xs disabled:opacity-50",
|
|
2088
|
+
fieldError ? "border-destructive" : "border-border"
|
|
2089
|
+
)
|
|
2090
|
+
}
|
|
2091
|
+
),
|
|
2092
|
+
fieldError && /* @__PURE__ */ jsx7(
|
|
2093
|
+
"p",
|
|
2094
|
+
{
|
|
2095
|
+
id: `${inputId}-error`,
|
|
2096
|
+
"data-testid": `connector-credential-field-error-${entry.slug}-${field.key}`,
|
|
2097
|
+
className: "text-xs text-destructive",
|
|
2098
|
+
children: fieldError
|
|
2099
|
+
}
|
|
2100
|
+
)
|
|
2101
|
+
] }, field.key);
|
|
2102
|
+
}),
|
|
2103
|
+
verify && /* @__PURE__ */ jsx7(
|
|
2104
|
+
"p",
|
|
2105
|
+
{
|
|
2106
|
+
"data-testid": `connector-guide-verify-${entry.slug}`,
|
|
2107
|
+
className: "text-xs text-muted-foreground",
|
|
2108
|
+
children: verify
|
|
2109
|
+
}
|
|
2110
|
+
),
|
|
2111
|
+
result && /* @__PURE__ */ jsx7(CredentialTestResult, { entry, result }),
|
|
2112
|
+
error && /* @__PURE__ */ jsx7(
|
|
2113
|
+
"p",
|
|
2114
|
+
{
|
|
2115
|
+
role: "alert",
|
|
2116
|
+
"data-testid": `connector-credential-error-${entry.slug}`,
|
|
2117
|
+
className: "text-xs text-destructive",
|
|
2118
|
+
children: error
|
|
2119
|
+
}
|
|
2120
|
+
),
|
|
2121
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-end gap-1.5", children: [
|
|
2122
|
+
testSupported && /* @__PURE__ */ jsx7(
|
|
2123
|
+
"button",
|
|
2124
|
+
{
|
|
2125
|
+
type: "button",
|
|
2126
|
+
"data-testid": `connector-credential-test-${entry.slug}`,
|
|
2127
|
+
onClick: () => void handleTest(),
|
|
2128
|
+
disabled: missingRequired || busy || testing,
|
|
2129
|
+
className: "rounded-md border border-border px-2.5 py-1 text-xs font-medium disabled:opacity-50",
|
|
2130
|
+
children: testing ? "Testing\u2026" : "Test connection"
|
|
2131
|
+
}
|
|
2132
|
+
),
|
|
2133
|
+
/* @__PURE__ */ jsx7(
|
|
2134
|
+
"button",
|
|
2135
|
+
{
|
|
2136
|
+
type: "submit",
|
|
2137
|
+
"data-testid": `connector-credential-save-${entry.slug}`,
|
|
2138
|
+
disabled: missingRequired || busy || testing || failedTest || untested,
|
|
2139
|
+
title: failedTest ? "That credential failed its own check \u2014 fix it and test again." : untested ? "Test the credential before saving it." : void 0,
|
|
2140
|
+
className: "rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
|
|
2141
|
+
children: busy ? "Saving\u2026" : "Save credential"
|
|
2142
|
+
}
|
|
2143
|
+
)
|
|
2144
|
+
] })
|
|
2145
|
+
]
|
|
2146
|
+
}
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
function CredentialTestResult({
|
|
2150
|
+
entry,
|
|
2151
|
+
result
|
|
2152
|
+
}) {
|
|
2153
|
+
if (result.valid === true) {
|
|
2154
|
+
return /* @__PURE__ */ jsxs6(
|
|
2155
|
+
"div",
|
|
2156
|
+
{
|
|
2157
|
+
role: "status",
|
|
2158
|
+
"data-testid": `connector-credential-test-pass-${entry.slug}`,
|
|
2159
|
+
className: "rounded-md border border-primary/30 bg-primary/5 px-2 py-1 text-xs",
|
|
2160
|
+
children: [
|
|
2161
|
+
/* @__PURE__ */ jsx7("p", { children: result.account_name ? `Verified \u2014 connected as ${result.account_name}.` : "Verified \u2014 these credentials work." }),
|
|
2162
|
+
result.permissions_note && /* @__PURE__ */ jsx7(
|
|
2163
|
+
"p",
|
|
2164
|
+
{
|
|
2165
|
+
"data-testid": `connector-credential-permissions-${entry.slug}`,
|
|
2166
|
+
className: "mt-0.5 font-medium",
|
|
2167
|
+
children: result.permissions_note
|
|
2168
|
+
}
|
|
2169
|
+
)
|
|
2170
|
+
]
|
|
2171
|
+
}
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
if (result.valid === false) {
|
|
2175
|
+
return /* @__PURE__ */ jsx7(
|
|
2176
|
+
"p",
|
|
2177
|
+
{
|
|
2178
|
+
role: "alert",
|
|
2179
|
+
"data-testid": `connector-credential-test-fail-${entry.slug}`,
|
|
2180
|
+
className: "rounded-md border border-destructive/30 bg-destructive/5 px-2 py-1 text-xs text-destructive",
|
|
2181
|
+
children: result.error || "That credential didn't pass its check."
|
|
2182
|
+
}
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
return /* @__PURE__ */ jsx7(
|
|
2186
|
+
"p",
|
|
2187
|
+
{
|
|
2188
|
+
role: "status",
|
|
2189
|
+
"data-testid": `connector-credential-test-unverified-${entry.slug}`,
|
|
2190
|
+
className: "rounded-md border border-border px-2 py-1 text-xs text-muted-foreground",
|
|
2191
|
+
children: UNVERIFIABLE_CREDENTIAL_NOTE
|
|
2192
|
+
}
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
1766
2195
|
function ManageSharing({
|
|
1767
2196
|
entry,
|
|
1768
2197
|
listGrants,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetreeve/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Shared brand-adaptive UI primitives for MindFortress frontends — min-size tokens, a favicon→logo→monogram BrandGlyph, a responsive header that keeps the search bar usable on mobile, and AppSidebar's viewport-driven auto-collapse ladder.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"import": "./dist/lockup/index.mjs",
|
|
23
23
|
"require": "./dist/lockup/index.js"
|
|
24
24
|
},
|
|
25
|
+
"./console": {
|
|
26
|
+
"types": "./dist/console/index.d.ts",
|
|
27
|
+
"import": "./dist/console/index.mjs",
|
|
28
|
+
"require": "./dist/console/index.js"
|
|
29
|
+
},
|
|
25
30
|
"./tokens.css": "./dist/tokens.css"
|
|
26
31
|
},
|
|
27
32
|
"files": [
|
|
@@ -42,6 +47,7 @@
|
|
|
42
47
|
"access": "public"
|
|
43
48
|
},
|
|
44
49
|
"dependencies": {
|
|
50
|
+
"@base-ui/react": "^1.6.0",
|
|
45
51
|
"@radix-ui/react-dropdown-menu": "^2.1.16"
|
|
46
52
|
},
|
|
47
53
|
"peerDependencies": {
|
|
@@ -59,11 +65,14 @@
|
|
|
59
65
|
"@types/react": "^19",
|
|
60
66
|
"@types/react-dom": "^19",
|
|
61
67
|
"@vitejs/plugin-react": "^5.1.3",
|
|
68
|
+
"class-variance-authority": "^0.7.1",
|
|
69
|
+
"clsx": "^2.1.1",
|
|
62
70
|
"jsdom": "^28.0.0",
|
|
63
71
|
"lucide-react": "^0.400.0",
|
|
64
72
|
"next": "16.1.6",
|
|
65
73
|
"react": "19.2.3",
|
|
66
74
|
"react-dom": "19.2.3",
|
|
75
|
+
"tailwind-merge": "^3.6.0",
|
|
67
76
|
"tsup": "^8.0.0",
|
|
68
77
|
"typescript": "5.9.3",
|
|
69
78
|
"vitest": "^4.0.18",
|