@meetreeve/ui 0.8.0 → 0.9.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/index.d.mts +76 -3
- package/dist/index.d.ts +76 -3
- package/dist/index.js +443 -99
- package/dist/index.mjs +443 -99
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -784,13 +784,23 @@ function Flyout({
|
|
|
784
784
|
className: cn(
|
|
785
785
|
"group flex w-full items-center gap-2.5 rounded-md px-3 py-1.5 text-sm transition-colors",
|
|
786
786
|
collapsed && "justify-center px-0",
|
|
787
|
-
|
|
787
|
+
// DEV-6586: this trigger renders as a row inside AppSidebar's own
|
|
788
|
+
// `<nav>` (a `bg-sidebar` surface that stays dark navy in both
|
|
789
|
+
// themes) — same fix as NavLink, same measured ratios (default
|
|
790
|
+
// 8.05:1, hover 13.65:1, active 10.95:1). The flyout PANEL below
|
|
791
|
+
// is intentionally left on document tokens: it renders via
|
|
792
|
+
// `position: fixed`, floating over the main content area on
|
|
793
|
+
// `bg-background`, not over the sidebar.
|
|
794
|
+
active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/70 hover:bg-sidebar-foreground/5 hover:text-sidebar-foreground"
|
|
788
795
|
),
|
|
789
796
|
children: [
|
|
790
797
|
/* @__PURE__ */ jsx3(
|
|
791
798
|
Icon,
|
|
792
799
|
{
|
|
793
|
-
className: cn(
|
|
800
|
+
className: cn(
|
|
801
|
+
"size-4 shrink-0",
|
|
802
|
+
active ? "text-sidebar-accent-foreground" : "text-sidebar-foreground/70"
|
|
803
|
+
),
|
|
794
804
|
strokeWidth: 1.5,
|
|
795
805
|
"aria-hidden": "true"
|
|
796
806
|
}
|
|
@@ -873,13 +883,23 @@ function NavLink({
|
|
|
873
883
|
className: cn(
|
|
874
884
|
"group flex items-center gap-2.5 rounded-md px-3 py-1.5 text-sm transition-colors",
|
|
875
885
|
collapsed && "justify-center px-0",
|
|
876
|
-
|
|
886
|
+
// DEV-6586: the sidebar is a permanently dark-navy surface in BOTH
|
|
887
|
+
// app themes (see `--sidebar-*` in globals.css) — styling from
|
|
888
|
+
// document-theme tokens (text-foreground/text-muted-foreground) put
|
|
889
|
+
// dark-on-dark text here in light mode. `--sidebar-foreground` /
|
|
890
|
+
// `--sidebar-accent(-foreground)` are theme-invariant, so hover/
|
|
891
|
+
// active reliably INCREASE legibility instead of erasing it:
|
|
892
|
+
// default 8.05:1, hover 13.65:1, active 10.95:1 (all >= 4.5:1 AA).
|
|
893
|
+
active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/70 hover:bg-sidebar-foreground/5 hover:text-sidebar-foreground"
|
|
877
894
|
),
|
|
878
895
|
children: [
|
|
879
896
|
/* @__PURE__ */ jsx4(
|
|
880
897
|
Icon,
|
|
881
898
|
{
|
|
882
|
-
className: cn(
|
|
899
|
+
className: cn(
|
|
900
|
+
"size-4 shrink-0",
|
|
901
|
+
active ? "text-sidebar-accent-foreground" : "text-sidebar-foreground/70"
|
|
902
|
+
),
|
|
883
903
|
strokeWidth: 1.5,
|
|
884
904
|
"aria-hidden": "true"
|
|
885
905
|
}
|
|
@@ -1022,7 +1042,7 @@ function AppSidebar({
|
|
|
1022
1042
|
href: brand.href,
|
|
1023
1043
|
"data-testid": "app-sidebar-brand",
|
|
1024
1044
|
title: brand.label,
|
|
1025
|
-
className: "flex items-center justify-center border-b border-border px-1 py-4",
|
|
1045
|
+
className: "flex items-center justify-center border-b border-sidebar-border px-1 py-4",
|
|
1026
1046
|
children: [
|
|
1027
1047
|
/* @__PURE__ */ jsx4("span", { className: "sr-only", children: brand.label }),
|
|
1028
1048
|
/* @__PURE__ */ jsx4(BrandGlyph, { brand: { name: brand.label }, size: "xl" })
|
|
@@ -1035,9 +1055,9 @@ function AppSidebar({
|
|
|
1035
1055
|
{
|
|
1036
1056
|
href: brand.href,
|
|
1037
1057
|
"data-testid": "app-sidebar-brand",
|
|
1038
|
-
className: "flex items-center gap-2.5 border-b border-border px-5 py-4",
|
|
1058
|
+
className: "flex items-center gap-2.5 border-b border-sidebar-border px-5 py-4 text-sidebar-foreground",
|
|
1039
1059
|
children: [
|
|
1040
|
-
/* @__PURE__ */ jsx4("span", { className: "grid size-7 place-items-center rounded-md bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx4("span", { className: "font-mono text-sm font-bold", children: (brand.label[0] ?? "r").toUpperCase() }) }),
|
|
1060
|
+
/* @__PURE__ */ jsx4("span", { className: "grid size-7 place-items-center rounded-md bg-sidebar-primary text-sidebar-primary-foreground", children: /* @__PURE__ */ jsx4("span", { className: "font-mono text-sm font-bold", children: (brand.label[0] ?? "r").toUpperCase() }) }),
|
|
1041
1061
|
/* @__PURE__ */ jsx4("span", { className: "truncate text-sm font-semibold tracking-tight", children: brand.label })
|
|
1042
1062
|
]
|
|
1043
1063
|
}
|
|
@@ -1093,7 +1113,7 @@ function AppSidebar({
|
|
|
1093
1113
|
"aria-modal": "true",
|
|
1094
1114
|
"aria-label": "Navigation",
|
|
1095
1115
|
"data-testid": "app-sidebar-mobile-drawer",
|
|
1096
|
-
className: "absolute inset-y-0 left-0 flex w-72 flex-col overflow-y-auto border-r border-border bg-sidebar",
|
|
1116
|
+
className: "absolute inset-y-0 left-0 flex w-72 flex-col overflow-y-auto border-r border-sidebar-border bg-sidebar",
|
|
1097
1117
|
children: [
|
|
1098
1118
|
/* @__PURE__ */ jsx4("div", { className: "sticky top-0 z-10 flex justify-end bg-sidebar p-2", children: /* @__PURE__ */ jsx4(
|
|
1099
1119
|
"button",
|
|
@@ -1104,7 +1124,7 @@ function AppSidebar({
|
|
|
1104
1124
|
"aria-label": `Close ${menuLabel.toLowerCase()}`,
|
|
1105
1125
|
onClick: () => setMobileOpen(false),
|
|
1106
1126
|
style: TOUCH_TARGET_STYLE,
|
|
1107
|
-
className: "flex items-center justify-center rounded-md p-2 text-foreground hover:bg-foreground/5",
|
|
1127
|
+
className: "flex items-center justify-center rounded-md p-2 text-sidebar-foreground/70 hover:bg-sidebar-foreground/5 hover:text-sidebar-foreground",
|
|
1108
1128
|
children: /* @__PURE__ */ jsx4(X2, { className: "h-5 w-5", "aria-hidden": "true" })
|
|
1109
1129
|
}
|
|
1110
1130
|
) }),
|
|
@@ -1122,7 +1142,7 @@ function AppSidebar({
|
|
|
1122
1142
|
{
|
|
1123
1143
|
"data-testid": "app-sidebar-desktop",
|
|
1124
1144
|
className: cn(
|
|
1125
|
-
"hidden shrink-0 flex-col border-r border-border bg-sidebar md:flex",
|
|
1145
|
+
"hidden shrink-0 flex-col border-r border-sidebar-border bg-sidebar md:flex",
|
|
1126
1146
|
collapsed ? COLLAPSED_WIDTH_CLASS : EXPANDED_WIDTH_CLASS,
|
|
1127
1147
|
// DEV-5331: a plain CSS width transition is enough — no new deps
|
|
1128
1148
|
// for a spring/JS animation. Respect prefers-reduced-motion.
|
|
@@ -1143,7 +1163,7 @@ function AppSidebar({
|
|
|
1143
1163
|
) })
|
|
1144
1164
|
}
|
|
1145
1165
|
),
|
|
1146
|
-
collapsible && /* @__PURE__ */ jsx4("div", { className: "border-t border-border px-2 py-2", children: /* @__PURE__ */ jsxs4(
|
|
1166
|
+
collapsible && /* @__PURE__ */ jsx4("div", { className: "border-t border-sidebar-border px-2 py-2", children: /* @__PURE__ */ jsxs4(
|
|
1147
1167
|
"button",
|
|
1148
1168
|
{
|
|
1149
1169
|
type: "button",
|
|
@@ -1153,7 +1173,7 @@ function AppSidebar({
|
|
|
1153
1173
|
title: collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1154
1174
|
onClick: handleToggleCollapsed,
|
|
1155
1175
|
className: cn(
|
|
1156
|
-
"flex items-center gap-2.5 rounded-md px-3 py-1.5 text-sm text-
|
|
1176
|
+
"flex items-center gap-2.5 rounded-md px-3 py-1.5 text-sm text-sidebar-foreground/70 transition-colors hover:bg-sidebar-foreground/5 hover:text-sidebar-foreground",
|
|
1157
1177
|
collapsed && "justify-center px-0"
|
|
1158
1178
|
),
|
|
1159
1179
|
children: [
|
|
@@ -1291,18 +1311,18 @@ function ContextSwitcher({
|
|
|
1291
1311
|
type: "button",
|
|
1292
1312
|
title: active?.name || placeholder,
|
|
1293
1313
|
"aria-label": active?.name || placeholder,
|
|
1294
|
-
className: "flex h-10 w-10 items-center justify-center rounded-lg text-
|
|
1314
|
+
className: "flex h-10 w-10 items-center justify-center rounded-lg text-sidebar-foreground/70 transition-colors hover:bg-sidebar-foreground/5 hover:text-sidebar-foreground",
|
|
1295
1315
|
children: /* @__PURE__ */ jsx6(BrandGlyph, { brand: toBrand(active ?? { name: "" }), size: "lg" })
|
|
1296
1316
|
}
|
|
1297
1317
|
) : /* @__PURE__ */ jsxs5(
|
|
1298
1318
|
"button",
|
|
1299
1319
|
{
|
|
1300
1320
|
type: "button",
|
|
1301
|
-
className: "group flex items-center gap-2 rounded-md border border-border bg-
|
|
1321
|
+
className: "group flex items-center gap-2 rounded-md border border-sidebar-border bg-sidebar-accent/60 px-2.5 py-1.5 text-sm font-medium transition-colors hover:bg-sidebar",
|
|
1302
1322
|
children: [
|
|
1303
1323
|
/* @__PURE__ */ jsx6(BrandGlyph, { brand: toBrand(active ?? { name: "" }) }),
|
|
1304
|
-
/* @__PURE__ */ jsx6("span", { className: "max-w-[140px] truncate text-foreground
|
|
1305
|
-
/* @__PURE__ */ jsx6(ChevronDown, { className: "h-3.5 w-3.5 text-
|
|
1324
|
+
/* @__PURE__ */ jsx6("span", { className: "max-w-[140px] truncate text-sidebar-accent-foreground", children: active?.name || placeholder }),
|
|
1325
|
+
/* @__PURE__ */ jsx6(ChevronDown, { className: "h-3.5 w-3.5 text-sidebar-foreground/70 transition-transform group-data-[state=open]:rotate-180" })
|
|
1306
1326
|
]
|
|
1307
1327
|
}
|
|
1308
1328
|
) }),
|
|
@@ -1348,6 +1368,10 @@ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-run
|
|
|
1348
1368
|
var DEFAULT_CONNECT_ERROR = "Couldn't start the connection. Please try again.";
|
|
1349
1369
|
var DEFAULT_DISCONNECT_ERROR = "Couldn't disconnect. Please try again.";
|
|
1350
1370
|
var DEFAULT_LOAD_ERROR = "Couldn't load connections. Please try again.";
|
|
1371
|
+
var DEFAULT_GRANTS_LOAD_ERROR = "Couldn't load sharing. Please try again.";
|
|
1372
|
+
var DEFAULT_GRANT_ERROR = "Couldn't share. Please try again.";
|
|
1373
|
+
var DEFAULT_REVOKE_ERROR = "Couldn't revoke access. Please try again.";
|
|
1374
|
+
var DEFAULT_ORG_DISABLED_REASON = "Org-wide connections aren't available to you.";
|
|
1351
1375
|
var SKELETON_COUNT = 3;
|
|
1352
1376
|
function capabilityLabel(capability) {
|
|
1353
1377
|
if (capability === "read") return "Read only";
|
|
@@ -1362,6 +1386,17 @@ function formatSince(since) {
|
|
|
1362
1386
|
function errorMessageFrom(err, fallback) {
|
|
1363
1387
|
return err instanceof Error && err.message ? err.message : fallback;
|
|
1364
1388
|
}
|
|
1389
|
+
function ownerLabel(entry) {
|
|
1390
|
+
const connection = entry.connection;
|
|
1391
|
+
if (connection.status !== "connected") return null;
|
|
1392
|
+
if (connection.kind === "org") return "Org";
|
|
1393
|
+
if (connection.owned_by_viewer) return "Yours";
|
|
1394
|
+
if (connection.shared_by) return `Shared by ${connection.shared_by.name}`;
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
1397
|
+
function isSharedWithViewer(entry) {
|
|
1398
|
+
return entry.connection.status === "connected" && Boolean(entry.connection.shared_by) && !entry.connection.owned_by_viewer;
|
|
1399
|
+
}
|
|
1365
1400
|
function ConnectorPicker({
|
|
1366
1401
|
client,
|
|
1367
1402
|
purpose,
|
|
@@ -1373,6 +1408,7 @@ function ConnectorPicker({
|
|
|
1373
1408
|
const [entries, setEntries] = useState7(null);
|
|
1374
1409
|
const [loadError, setLoadError] = useState7(null);
|
|
1375
1410
|
const [uiState, setUiState] = useState7({});
|
|
1411
|
+
const grantsSupported = typeof client.listGrants === "function" && typeof client.grant === "function" && typeof client.revokeGrant === "function";
|
|
1376
1412
|
const clientRef = useRef5(client);
|
|
1377
1413
|
clientRef.current = client;
|
|
1378
1414
|
const purposeRef = useRef5(purpose);
|
|
@@ -1431,10 +1467,10 @@ function ConnectorPicker({
|
|
|
1431
1467
|
};
|
|
1432
1468
|
}, [gated, refreshAfterReturn]);
|
|
1433
1469
|
const handleConnect = useCallback4(
|
|
1434
|
-
async (entry) => {
|
|
1470
|
+
async (entry, kind) => {
|
|
1435
1471
|
setUiState((prev) => ({ ...prev, [entry.slug]: { kind: "connecting" } }));
|
|
1436
1472
|
try {
|
|
1437
|
-
const result = await clientRef.current.connect(entry);
|
|
1473
|
+
const result = await clientRef.current.connect(entry, { kind });
|
|
1438
1474
|
if (result?.connect_url) {
|
|
1439
1475
|
window.open(result.connect_url, "_blank", "noopener,noreferrer");
|
|
1440
1476
|
return;
|
|
@@ -1474,121 +1510,429 @@ function ConnectorPicker({
|
|
|
1474
1510
|
},
|
|
1475
1511
|
[load]
|
|
1476
1512
|
);
|
|
1513
|
+
const listGrants = useCallback4(
|
|
1514
|
+
(entry) => clientRef.current.listGrants?.(entry) ?? Promise.resolve([]),
|
|
1515
|
+
[]
|
|
1516
|
+
);
|
|
1517
|
+
const addGrant = useCallback4(
|
|
1518
|
+
(entry, input) => clientRef.current.grant?.(entry, input) ?? Promise.resolve(),
|
|
1519
|
+
[]
|
|
1520
|
+
);
|
|
1521
|
+
const revokeGrant = useCallback4(
|
|
1522
|
+
(entry, grantId) => clientRef.current.revokeGrant?.(entry, grantId) ?? Promise.resolve(),
|
|
1523
|
+
[]
|
|
1524
|
+
);
|
|
1477
1525
|
if (gated) return null;
|
|
1478
1526
|
const containerClassName = cn(
|
|
1479
1527
|
"flex flex-col",
|
|
1480
1528
|
layout === "tiles" ? "gap-3 sm:grid sm:grid-cols-2 lg:grid-cols-3" : "divide-y divide-border"
|
|
1481
1529
|
);
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
"
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
"
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
{
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1530
|
+
const ownEntries = entries?.filter((entry) => !isSharedWithViewer(entry)) ?? null;
|
|
1531
|
+
const sharedEntries = entries?.filter(isSharedWithViewer) ?? [];
|
|
1532
|
+
return /* @__PURE__ */ jsxs6("div", { "data-testid": "connector-picker", "data-layout": layout, className: "flex flex-col gap-4", children: [
|
|
1533
|
+
/* @__PURE__ */ jsxs6("div", { className: containerClassName, children: [
|
|
1534
|
+
entries === null && !loadError && Array.from({ length: SKELETON_COUNT }).map((_, i) => /* @__PURE__ */ jsxs6(
|
|
1535
|
+
"div",
|
|
1536
|
+
{
|
|
1537
|
+
"data-testid": "connector-picker-skeleton-item",
|
|
1538
|
+
"aria-hidden": "true",
|
|
1539
|
+
className: "flex items-center gap-3 rounded-lg border border-border p-3",
|
|
1540
|
+
children: [
|
|
1541
|
+
/* @__PURE__ */ jsx7("span", { className: "h-8 w-8 shrink-0 animate-pulse rounded bg-foreground/10" }),
|
|
1542
|
+
/* @__PURE__ */ jsx7("span", { className: "h-3 flex-1 animate-pulse rounded bg-foreground/10" })
|
|
1543
|
+
]
|
|
1544
|
+
},
|
|
1545
|
+
i
|
|
1546
|
+
)),
|
|
1547
|
+
loadError && /* @__PURE__ */ jsxs6(
|
|
1548
|
+
"div",
|
|
1549
|
+
{
|
|
1550
|
+
role: "alert",
|
|
1551
|
+
"data-testid": "connector-picker-load-error",
|
|
1552
|
+
className: "flex items-center justify-between gap-3 rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive",
|
|
1553
|
+
children: [
|
|
1554
|
+
/* @__PURE__ */ jsx7("span", { children: loadError }),
|
|
1555
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => void load(), className: "shrink-0 underline", children: "Retry" })
|
|
1556
|
+
]
|
|
1557
|
+
}
|
|
1558
|
+
),
|
|
1559
|
+
entries !== null && entries.length === 0 && !loadError && /* @__PURE__ */ jsx7("p", { "data-testid": "connector-picker-empty", className: "text-sm text-muted-foreground", children: "No connections available." }),
|
|
1560
|
+
ownEntries?.map((entry) => /* @__PURE__ */ jsx7(
|
|
1561
|
+
ConnectorTile,
|
|
1562
|
+
{
|
|
1563
|
+
entry,
|
|
1564
|
+
ui: uiState[entry.slug],
|
|
1565
|
+
layout,
|
|
1566
|
+
grantsSupported,
|
|
1567
|
+
onConnect: handleConnect,
|
|
1568
|
+
onDisconnect: handleDisconnect,
|
|
1569
|
+
listGrants,
|
|
1570
|
+
addGrant,
|
|
1571
|
+
revokeGrant
|
|
1572
|
+
},
|
|
1573
|
+
entry.slug
|
|
1574
|
+
))
|
|
1575
|
+
] }),
|
|
1576
|
+
sharedEntries.length > 0 && /* @__PURE__ */ jsxs6("section", { "data-testid": "connector-picker-shared-with-me", className: "flex flex-col gap-2", children: [
|
|
1577
|
+
/* @__PURE__ */ jsx7("h3", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Shared with me" }),
|
|
1578
|
+
/* @__PURE__ */ jsx7("div", { className: containerClassName, children: sharedEntries.map((entry) => /* @__PURE__ */ jsx7(
|
|
1579
|
+
ConnectorTile,
|
|
1580
|
+
{
|
|
1581
|
+
entry,
|
|
1582
|
+
ui: uiState[entry.slug],
|
|
1583
|
+
layout,
|
|
1584
|
+
shared: true,
|
|
1585
|
+
grantsSupported,
|
|
1586
|
+
onConnect: handleConnect,
|
|
1587
|
+
onDisconnect: handleDisconnect,
|
|
1588
|
+
listGrants,
|
|
1589
|
+
addGrant,
|
|
1590
|
+
revokeGrant
|
|
1591
|
+
},
|
|
1592
|
+
entry.slug
|
|
1593
|
+
)) })
|
|
1594
|
+
] })
|
|
1520
1595
|
] });
|
|
1521
1596
|
}
|
|
1522
1597
|
function ConnectorTile({
|
|
1523
1598
|
entry,
|
|
1524
1599
|
ui,
|
|
1525
1600
|
layout,
|
|
1601
|
+
shared = false,
|
|
1602
|
+
grantsSupported,
|
|
1526
1603
|
onConnect,
|
|
1527
|
-
onDisconnect
|
|
1604
|
+
onDisconnect,
|
|
1605
|
+
listGrants,
|
|
1606
|
+
addGrant,
|
|
1607
|
+
revokeGrant
|
|
1528
1608
|
}) {
|
|
1529
1609
|
const isConnected = entry.connection.status === "connected";
|
|
1530
1610
|
const isConnecting = ui?.kind === "connecting";
|
|
1531
1611
|
const isDisconnecting = ui?.kind === "disconnecting";
|
|
1532
1612
|
const errorMessage = ui?.kind === "error" ? ui.message : null;
|
|
1613
|
+
const [kind, setKind] = useState7("personal");
|
|
1614
|
+
const [sharingOpen, setSharingOpen] = useState7(false);
|
|
1615
|
+
const orgPermitted = entry.org_connect?.permitted === true;
|
|
1616
|
+
const orgDisabledReason = entry.org_connect?.reason || DEFAULT_ORG_DISABLED_REASON;
|
|
1617
|
+
const attribution = ownerLabel(entry);
|
|
1618
|
+
const canDisconnect = isConnected && !shared;
|
|
1619
|
+
const canManageSharing = isConnected && entry.connection.owned_by_viewer === true && grantsSupported;
|
|
1533
1620
|
return /* @__PURE__ */ jsxs6(
|
|
1534
1621
|
"div",
|
|
1535
1622
|
{
|
|
1536
1623
|
"data-testid": `connector-tile-${entry.slug}`,
|
|
1537
1624
|
"data-connected": isConnected || void 0,
|
|
1538
1625
|
"data-deprecated": entry.deprecated || void 0,
|
|
1539
|
-
className: cn("flex
|
|
1626
|
+
className: cn("flex flex-col p-3", layout === "tiles" && "rounded-lg border border-border"),
|
|
1540
1627
|
children: [
|
|
1541
|
-
/* @__PURE__ */
|
|
1542
|
-
|
|
1543
|
-
/* @__PURE__ */ jsxs6("div", { className: "
|
|
1544
|
-
/* @__PURE__ */
|
|
1545
|
-
|
|
1546
|
-
|
|
1628
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [
|
|
1629
|
+
/* @__PURE__ */ jsx7(BrandGlyph, { brand: { name: entry.name, logo: entry.logo }, size: "lg" }),
|
|
1630
|
+
/* @__PURE__ */ jsxs6("div", { className: "min-w-0 flex-1", children: [
|
|
1631
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
|
|
1632
|
+
/* @__PURE__ */ jsx7("p", { className: "truncate text-sm font-medium", children: entry.name }),
|
|
1633
|
+
entry.deprecated && /* @__PURE__ */ jsx7(
|
|
1634
|
+
"span",
|
|
1635
|
+
{
|
|
1636
|
+
"data-testid": `connector-deprecated-${entry.slug}`,
|
|
1637
|
+
className: "shrink-0 rounded-full bg-foreground/10 px-2 py-0.5 text-[11px] text-muted-foreground",
|
|
1638
|
+
children: "No longer available"
|
|
1639
|
+
}
|
|
1640
|
+
)
|
|
1641
|
+
] }),
|
|
1642
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
1643
|
+
/* @__PURE__ */ jsx7("span", { "data-testid": `connector-capability-${entry.slug}`, children: capabilityLabel(entry.capability) }),
|
|
1644
|
+
entry.category && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1645
|
+
/* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
1646
|
+
/* @__PURE__ */ jsx7("span", { className: "truncate", children: entry.category })
|
|
1647
|
+
] })
|
|
1648
|
+
] }),
|
|
1649
|
+
isConnected && /* @__PURE__ */ jsx7(
|
|
1650
|
+
"p",
|
|
1547
1651
|
{
|
|
1548
|
-
"data-testid": `connector-
|
|
1549
|
-
className: "
|
|
1550
|
-
children:
|
|
1652
|
+
"data-testid": `connector-connected-since-${entry.slug}`,
|
|
1653
|
+
className: "text-xs text-muted-foreground",
|
|
1654
|
+
children: entry.connection.since ? `Connected since ${formatSince(entry.connection.since)}` : "Connected"
|
|
1551
1655
|
}
|
|
1552
|
-
)
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1656
|
+
),
|
|
1657
|
+
attribution && /* @__PURE__ */ jsx7(
|
|
1658
|
+
"p",
|
|
1659
|
+
{
|
|
1660
|
+
"data-testid": `connector-owner-${entry.slug}`,
|
|
1661
|
+
className: "text-xs text-muted-foreground",
|
|
1662
|
+
children: attribution
|
|
1663
|
+
}
|
|
1664
|
+
),
|
|
1665
|
+
!isConnected && /* @__PURE__ */ jsxs6(
|
|
1666
|
+
"div",
|
|
1667
|
+
{
|
|
1668
|
+
"data-testid": `connector-kind-toggle-${entry.slug}`,
|
|
1669
|
+
role: "group",
|
|
1670
|
+
"aria-label": "Connect as",
|
|
1671
|
+
className: "mt-1 flex items-center gap-1",
|
|
1672
|
+
children: [
|
|
1673
|
+
/* @__PURE__ */ jsx7(
|
|
1674
|
+
"button",
|
|
1675
|
+
{
|
|
1676
|
+
type: "button",
|
|
1677
|
+
"data-testid": `connector-kind-personal-${entry.slug}`,
|
|
1678
|
+
"aria-pressed": kind === "personal",
|
|
1679
|
+
disabled: isConnecting,
|
|
1680
|
+
onClick: () => setKind("personal"),
|
|
1681
|
+
className: cn(
|
|
1682
|
+
"rounded-full border px-2 py-0.5 text-[11px]",
|
|
1683
|
+
kind === "personal" ? "border-primary bg-primary/10 font-medium" : "border-border text-muted-foreground",
|
|
1684
|
+
"disabled:opacity-50"
|
|
1685
|
+
),
|
|
1686
|
+
children: "Personal"
|
|
1687
|
+
}
|
|
1688
|
+
),
|
|
1689
|
+
/* @__PURE__ */ jsxs6(
|
|
1690
|
+
"button",
|
|
1691
|
+
{
|
|
1692
|
+
type: "button",
|
|
1693
|
+
"data-testid": `connector-kind-org-${entry.slug}`,
|
|
1694
|
+
"aria-pressed": kind === "org",
|
|
1695
|
+
disabled: isConnecting || !orgPermitted,
|
|
1696
|
+
title: orgPermitted ? void 0 : orgDisabledReason,
|
|
1697
|
+
onClick: () => setKind("org"),
|
|
1698
|
+
className: cn(
|
|
1699
|
+
"rounded-full border px-2 py-0.5 text-[11px]",
|
|
1700
|
+
kind === "org" ? "border-primary bg-primary/10 font-medium" : "border-border text-muted-foreground",
|
|
1701
|
+
"disabled:opacity-50"
|
|
1702
|
+
),
|
|
1703
|
+
children: [
|
|
1704
|
+
"Org",
|
|
1705
|
+
!orgPermitted && /* @__PURE__ */ jsxs6("span", { className: "sr-only", children: [
|
|
1706
|
+
" (",
|
|
1707
|
+
orgDisabledReason,
|
|
1708
|
+
")"
|
|
1709
|
+
] })
|
|
1710
|
+
]
|
|
1711
|
+
}
|
|
1712
|
+
)
|
|
1713
|
+
]
|
|
1714
|
+
}
|
|
1715
|
+
),
|
|
1716
|
+
errorMessage && /* @__PURE__ */ jsx7("p", { role: "alert", "data-testid": `connector-error-${entry.slug}`, className: "text-xs text-destructive", children: errorMessage })
|
|
1560
1717
|
] }),
|
|
1561
|
-
isConnected
|
|
1562
|
-
|
|
1718
|
+
isConnected ? /* @__PURE__ */ jsxs6("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [
|
|
1719
|
+
canDisconnect && /* @__PURE__ */ jsx7(
|
|
1720
|
+
"button",
|
|
1721
|
+
{
|
|
1722
|
+
type: "button",
|
|
1723
|
+
"data-testid": `connector-disconnect-${entry.slug}`,
|
|
1724
|
+
onClick: () => onDisconnect(entry),
|
|
1725
|
+
disabled: isDisconnecting,
|
|
1726
|
+
className: "shrink-0 rounded-md px-2.5 py-1 text-xs font-medium text-destructive hover:bg-destructive/10 disabled:opacity-50",
|
|
1727
|
+
children: isDisconnecting ? "Disconnecting\u2026" : "Disconnect"
|
|
1728
|
+
}
|
|
1729
|
+
),
|
|
1730
|
+
canManageSharing && /* @__PURE__ */ jsx7(
|
|
1731
|
+
"button",
|
|
1732
|
+
{
|
|
1733
|
+
type: "button",
|
|
1734
|
+
"data-testid": `connector-manage-sharing-${entry.slug}`,
|
|
1735
|
+
"aria-expanded": sharingOpen,
|
|
1736
|
+
onClick: () => setSharingOpen((open) => !open),
|
|
1737
|
+
className: "shrink-0 rounded-md px-2.5 py-1 text-xs font-medium text-muted-foreground hover:bg-foreground/10",
|
|
1738
|
+
children: sharingOpen ? "Hide sharing" : "Manage sharing"
|
|
1739
|
+
}
|
|
1740
|
+
)
|
|
1741
|
+
] }) : /* @__PURE__ */ jsx7(
|
|
1742
|
+
"button",
|
|
1563
1743
|
{
|
|
1564
|
-
"
|
|
1565
|
-
|
|
1566
|
-
|
|
1744
|
+
type: "button",
|
|
1745
|
+
"data-testid": `connector-connect-${entry.slug}`,
|
|
1746
|
+
onClick: () => onConnect(entry, kind),
|
|
1747
|
+
disabled: isConnecting,
|
|
1748
|
+
className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
|
|
1749
|
+
children: isConnecting ? "Connecting\u2026" : "Connect"
|
|
1567
1750
|
}
|
|
1568
|
-
)
|
|
1569
|
-
errorMessage && /* @__PURE__ */ jsx7("p", { role: "alert", "data-testid": `connector-error-${entry.slug}`, className: "text-xs text-destructive", children: errorMessage })
|
|
1751
|
+
)
|
|
1570
1752
|
] }),
|
|
1571
|
-
|
|
1572
|
-
|
|
1753
|
+
canManageSharing && sharingOpen && /* @__PURE__ */ jsx7(
|
|
1754
|
+
ManageSharing,
|
|
1573
1755
|
{
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
className: "shrink-0 rounded-md px-2.5 py-1 text-xs font-medium text-destructive hover:bg-destructive/10 disabled:opacity-50",
|
|
1579
|
-
children: isDisconnecting ? "Disconnecting\u2026" : "Disconnect"
|
|
1756
|
+
entry,
|
|
1757
|
+
listGrants,
|
|
1758
|
+
addGrant,
|
|
1759
|
+
revokeGrant
|
|
1580
1760
|
}
|
|
1581
|
-
)
|
|
1582
|
-
|
|
1761
|
+
)
|
|
1762
|
+
]
|
|
1763
|
+
}
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
function ManageSharing({
|
|
1767
|
+
entry,
|
|
1768
|
+
listGrants,
|
|
1769
|
+
addGrant,
|
|
1770
|
+
revokeGrant
|
|
1771
|
+
}) {
|
|
1772
|
+
const [grants, setGrants] = useState7(null);
|
|
1773
|
+
const [error, setError] = useState7(null);
|
|
1774
|
+
const [busy, setBusy] = useState7(false);
|
|
1775
|
+
const [granteeType, setGranteeType] = useState7("user");
|
|
1776
|
+
const [granteeId, setGranteeId] = useState7("");
|
|
1777
|
+
const roleOptions = entry.org_connect?.role_options ?? [];
|
|
1778
|
+
const entryRef = useRef5(entry);
|
|
1779
|
+
entryRef.current = entry;
|
|
1780
|
+
const load = useCallback4(async () => {
|
|
1781
|
+
try {
|
|
1782
|
+
setGrants(await listGrants(entryRef.current));
|
|
1783
|
+
setError(null);
|
|
1784
|
+
} catch (err) {
|
|
1785
|
+
setError(errorMessageFrom(err, DEFAULT_GRANTS_LOAD_ERROR));
|
|
1786
|
+
}
|
|
1787
|
+
}, [listGrants]);
|
|
1788
|
+
useEffect6(() => {
|
|
1789
|
+
void load();
|
|
1790
|
+
}, [load]);
|
|
1791
|
+
const handleAdd = useCallback4(async () => {
|
|
1792
|
+
const id = granteeId.trim();
|
|
1793
|
+
if (!id) return;
|
|
1794
|
+
setBusy(true);
|
|
1795
|
+
try {
|
|
1796
|
+
await addGrant(entryRef.current, { granteeType, granteeId: id });
|
|
1797
|
+
setGranteeId("");
|
|
1798
|
+
await load();
|
|
1799
|
+
} catch (err) {
|
|
1800
|
+
setError(errorMessageFrom(err, DEFAULT_GRANT_ERROR));
|
|
1801
|
+
} finally {
|
|
1802
|
+
setBusy(false);
|
|
1803
|
+
}
|
|
1804
|
+
}, [addGrant, granteeId, granteeType, load]);
|
|
1805
|
+
const handleRevoke = useCallback4(
|
|
1806
|
+
async (grantId) => {
|
|
1807
|
+
setBusy(true);
|
|
1808
|
+
try {
|
|
1809
|
+
await revokeGrant(entryRef.current, grantId);
|
|
1810
|
+
await load();
|
|
1811
|
+
} catch (err) {
|
|
1812
|
+
setError(errorMessageFrom(err, DEFAULT_REVOKE_ERROR));
|
|
1813
|
+
} finally {
|
|
1814
|
+
setBusy(false);
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
[load, revokeGrant]
|
|
1818
|
+
);
|
|
1819
|
+
return /* @__PURE__ */ jsxs6(
|
|
1820
|
+
"div",
|
|
1821
|
+
{
|
|
1822
|
+
"data-testid": `connector-sharing-panel-${entry.slug}`,
|
|
1823
|
+
className: "mt-2 flex flex-col gap-2 rounded-md border border-border bg-foreground/5 p-2",
|
|
1824
|
+
children: [
|
|
1825
|
+
grants === null && !error && /* @__PURE__ */ jsx7(
|
|
1826
|
+
"p",
|
|
1583
1827
|
{
|
|
1584
|
-
|
|
1585
|
-
"
|
|
1586
|
-
|
|
1587
|
-
disabled: isConnecting,
|
|
1588
|
-
className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
|
|
1589
|
-
children: isConnecting ? "Connecting\u2026" : "Connect"
|
|
1828
|
+
"data-testid": `connector-grants-loading-${entry.slug}`,
|
|
1829
|
+
className: "text-xs text-muted-foreground",
|
|
1830
|
+
children: "Loading sharing\u2026"
|
|
1590
1831
|
}
|
|
1591
|
-
)
|
|
1832
|
+
),
|
|
1833
|
+
error && /* @__PURE__ */ jsx7(
|
|
1834
|
+
"p",
|
|
1835
|
+
{
|
|
1836
|
+
role: "alert",
|
|
1837
|
+
"data-testid": `connector-sharing-error-${entry.slug}`,
|
|
1838
|
+
className: "text-xs text-destructive",
|
|
1839
|
+
children: error
|
|
1840
|
+
}
|
|
1841
|
+
),
|
|
1842
|
+
grants !== null && grants.length === 0 && /* @__PURE__ */ jsx7(
|
|
1843
|
+
"p",
|
|
1844
|
+
{
|
|
1845
|
+
"data-testid": `connector-grants-empty-${entry.slug}`,
|
|
1846
|
+
className: "text-xs text-muted-foreground",
|
|
1847
|
+
children: "Not shared with anyone yet."
|
|
1848
|
+
}
|
|
1849
|
+
),
|
|
1850
|
+
grants?.map((grant) => /* @__PURE__ */ jsxs6(
|
|
1851
|
+
"div",
|
|
1852
|
+
{
|
|
1853
|
+
"data-testid": `connector-grant-${entry.slug}-${grant.id}`,
|
|
1854
|
+
className: "flex items-center justify-between gap-2 text-xs",
|
|
1855
|
+
children: [
|
|
1856
|
+
/* @__PURE__ */ jsxs6("span", { className: "truncate", children: [
|
|
1857
|
+
grant.grantee_name || grant.grantee_id,
|
|
1858
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-muted-foreground", children: [
|
|
1859
|
+
" ",
|
|
1860
|
+
"\xB7 ",
|
|
1861
|
+
grant.grantee_type === "role" ? "Role" : "User"
|
|
1862
|
+
] })
|
|
1863
|
+
] }),
|
|
1864
|
+
/* @__PURE__ */ jsx7(
|
|
1865
|
+
"button",
|
|
1866
|
+
{
|
|
1867
|
+
type: "button",
|
|
1868
|
+
"data-testid": `connector-grant-revoke-${entry.slug}-${grant.id}`,
|
|
1869
|
+
onClick: () => void handleRevoke(grant.id),
|
|
1870
|
+
disabled: busy,
|
|
1871
|
+
className: "shrink-0 rounded-md px-2 py-0.5 font-medium text-destructive hover:bg-destructive/10 disabled:opacity-50",
|
|
1872
|
+
children: "Revoke"
|
|
1873
|
+
}
|
|
1874
|
+
)
|
|
1875
|
+
]
|
|
1876
|
+
},
|
|
1877
|
+
grant.id
|
|
1878
|
+
)),
|
|
1879
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5", children: [
|
|
1880
|
+
/* @__PURE__ */ jsxs6(
|
|
1881
|
+
"select",
|
|
1882
|
+
{
|
|
1883
|
+
"data-testid": `connector-grant-type-${entry.slug}`,
|
|
1884
|
+
"aria-label": "Grantee type",
|
|
1885
|
+
value: granteeType,
|
|
1886
|
+
onChange: (event) => {
|
|
1887
|
+
setGranteeType(event.target.value);
|
|
1888
|
+
setGranteeId("");
|
|
1889
|
+
},
|
|
1890
|
+
disabled: busy,
|
|
1891
|
+
className: "rounded-md border border-border bg-transparent px-1.5 py-1 text-xs disabled:opacity-50",
|
|
1892
|
+
children: [
|
|
1893
|
+
/* @__PURE__ */ jsx7("option", { value: "user", children: "User" }),
|
|
1894
|
+
/* @__PURE__ */ jsx7("option", { value: "role", children: "Role" })
|
|
1895
|
+
]
|
|
1896
|
+
}
|
|
1897
|
+
),
|
|
1898
|
+
granteeType === "user" ? /* @__PURE__ */ jsx7(
|
|
1899
|
+
"input",
|
|
1900
|
+
{
|
|
1901
|
+
"data-testid": `connector-grant-grantee-${entry.slug}`,
|
|
1902
|
+
"aria-label": "User ID",
|
|
1903
|
+
value: granteeId,
|
|
1904
|
+
onChange: (event) => setGranteeId(event.target.value),
|
|
1905
|
+
placeholder: "User ID",
|
|
1906
|
+
disabled: busy,
|
|
1907
|
+
className: "min-w-0 flex-1 rounded-md border border-border bg-transparent px-1.5 py-1 text-xs disabled:opacity-50"
|
|
1908
|
+
}
|
|
1909
|
+
) : /* @__PURE__ */ jsxs6(
|
|
1910
|
+
"select",
|
|
1911
|
+
{
|
|
1912
|
+
"data-testid": `connector-grant-role-${entry.slug}`,
|
|
1913
|
+
"aria-label": "Role",
|
|
1914
|
+
value: granteeId,
|
|
1915
|
+
onChange: (event) => setGranteeId(event.target.value),
|
|
1916
|
+
disabled: busy,
|
|
1917
|
+
className: "min-w-0 flex-1 rounded-md border border-border bg-transparent px-1.5 py-1 text-xs disabled:opacity-50",
|
|
1918
|
+
children: [
|
|
1919
|
+
/* @__PURE__ */ jsx7("option", { value: "", children: "Select a role\u2026" }),
|
|
1920
|
+
roleOptions.map((role) => /* @__PURE__ */ jsx7("option", { value: role.id, children: role.name }, role.id))
|
|
1921
|
+
]
|
|
1922
|
+
}
|
|
1923
|
+
),
|
|
1924
|
+
/* @__PURE__ */ jsx7(
|
|
1925
|
+
"button",
|
|
1926
|
+
{
|
|
1927
|
+
type: "button",
|
|
1928
|
+
"data-testid": `connector-grant-add-${entry.slug}`,
|
|
1929
|
+
onClick: () => void handleAdd(),
|
|
1930
|
+
disabled: busy || !granteeId.trim(),
|
|
1931
|
+
className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
|
|
1932
|
+
children: "Share"
|
|
1933
|
+
}
|
|
1934
|
+
)
|
|
1935
|
+
] })
|
|
1592
1936
|
]
|
|
1593
1937
|
}
|
|
1594
1938
|
);
|