@retinalabsllc/zairusjs 0.2.2 → 0.2.3
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 +135 -3
- package/dist/index.d.ts +135 -3
- package/dist/index.js +759 -5
- package/dist/index.mjs +767 -5
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1065,12 +1065,11 @@ var PageSpinner = ({
|
|
|
1065
1065
|
// src/components/ManagedToaster.tsx
|
|
1066
1066
|
import React19 from "react";
|
|
1067
1067
|
import { Toaster } from "react-hot-toast";
|
|
1068
|
-
var ManagedToaster = (
|
|
1068
|
+
var ManagedToaster = () => {
|
|
1069
1069
|
return /* @__PURE__ */ React19.createElement(
|
|
1070
1070
|
Toaster,
|
|
1071
1071
|
{
|
|
1072
1072
|
position: "top-right",
|
|
1073
|
-
...props,
|
|
1074
1073
|
toastOptions: {
|
|
1075
1074
|
style: {
|
|
1076
1075
|
background: "#171717",
|
|
@@ -1092,9 +1091,7 @@ var ManagedToaster = (props) => {
|
|
|
1092
1091
|
primary: "#fafafa",
|
|
1093
1092
|
secondary: "#171717"
|
|
1094
1093
|
}
|
|
1095
|
-
}
|
|
1096
|
-
...props.toastOptions
|
|
1097
|
-
// Allows overriding specific toast styles if needed
|
|
1094
|
+
}
|
|
1098
1095
|
}
|
|
1099
1096
|
}
|
|
1100
1097
|
);
|
|
@@ -1427,6 +1424,766 @@ var GifFeatureCard = ({
|
|
|
1427
1424
|
}
|
|
1428
1425
|
), /* @__PURE__ */ React24.createElement("div", { className: "absolute inset-x-0 bottom-0 p-6 sm:p-8 z-30 flex flex-col justify-end text-left pointer-events-none" }, title && /* @__PURE__ */ React24.createElement("h3", { className: "text-xl sm:text-2xl md:text-3xl font-medium text-white tracking-tight mb-2 sm:mb-3 drop-shadow-md" }, title), subtitle && /* @__PURE__ */ React24.createElement("p", { className: "text-[13px] sm:text-[15px] leading-relaxed text-neutral-300 max-w-2xl drop-shadow-sm" }, subtitle)));
|
|
1429
1426
|
};
|
|
1427
|
+
|
|
1428
|
+
// src/components/UniversalSidebar.tsx
|
|
1429
|
+
import React25, { useState as useState9, useEffect as useEffect6 } from "react";
|
|
1430
|
+
import Link10 from "next/link";
|
|
1431
|
+
import { HugeiconsIcon as HugeiconsIcon13 } from "@hugeicons/react";
|
|
1432
|
+
import { SidebarLeft01Icon, Cancel01Icon } from "@hugeicons/core-free-icons";
|
|
1433
|
+
var UniversalSidebar = ({
|
|
1434
|
+
navItems,
|
|
1435
|
+
currentPath,
|
|
1436
|
+
isMobileOpen,
|
|
1437
|
+
closeMobile,
|
|
1438
|
+
onNavClick,
|
|
1439
|
+
showInterceptDialog = false,
|
|
1440
|
+
onCancelIntercept,
|
|
1441
|
+
onConfirmIntercept,
|
|
1442
|
+
interceptTitle = "Discard Changes",
|
|
1443
|
+
interceptMessage = "Are you sure you want to leave? All unsaved changes will be lost."
|
|
1444
|
+
}) => {
|
|
1445
|
+
const [isCollapsed, setIsCollapsed] = useState9(false);
|
|
1446
|
+
useEffect6(() => {
|
|
1447
|
+
if (isMobileOpen) {
|
|
1448
|
+
closeMobile();
|
|
1449
|
+
}
|
|
1450
|
+
}, [currentPath]);
|
|
1451
|
+
return /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(
|
|
1452
|
+
"div",
|
|
1453
|
+
{
|
|
1454
|
+
className: `fixed inset-0 bg-black/30 shadow-2xl transition-opacity duration-300 md:hidden z-90 ${isMobileOpen ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"}`,
|
|
1455
|
+
onClick: closeMobile
|
|
1456
|
+
}
|
|
1457
|
+
), /* @__PURE__ */ React25.createElement(
|
|
1458
|
+
"aside",
|
|
1459
|
+
{
|
|
1460
|
+
className: `
|
|
1461
|
+
fixed md:relative top-0 left-0 h-full bg-white
|
|
1462
|
+
transition-all duration-300 ease-in-out shrink-0 flex flex-col z-100 md:z-10
|
|
1463
|
+
${isMobileOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0"}
|
|
1464
|
+
${isCollapsed ? "md:w-16 w-64" : "w-64"}
|
|
1465
|
+
`
|
|
1466
|
+
},
|
|
1467
|
+
/* @__PURE__ */ React25.createElement("div", { className: "md:hidden flex justify-end p-4 shrink-0" }, /* @__PURE__ */ React25.createElement(
|
|
1468
|
+
"button",
|
|
1469
|
+
{
|
|
1470
|
+
onClick: closeMobile,
|
|
1471
|
+
className: "text-neutral-400 hover:text-black transition-colors outline-none",
|
|
1472
|
+
"aria-label": "Close Menu"
|
|
1473
|
+
},
|
|
1474
|
+
/* @__PURE__ */ React25.createElement(HugeiconsIcon13, { icon: Cancel01Icon, size: 24 })
|
|
1475
|
+
)),
|
|
1476
|
+
/* @__PURE__ */ React25.createElement("nav", { className: "flex-1 py-6 flex flex-col gap-1.5 px-3 overflow-y-auto custom-scrollbar" }, navItems.map((item) => {
|
|
1477
|
+
const isCurrentRoute = item.path === "/mod" || item.path === "/app" ? currentPath === item.path : currentPath === item.path || currentPath?.startsWith(`${item.path}/`);
|
|
1478
|
+
return /* @__PURE__ */ React25.createElement(
|
|
1479
|
+
Link10,
|
|
1480
|
+
{
|
|
1481
|
+
key: item.name,
|
|
1482
|
+
href: item.path,
|
|
1483
|
+
onClick: (e) => onNavClick ? onNavClick(e, item.path) : void 0,
|
|
1484
|
+
className: `flex items-center gap-3 px-3 py-2 rounded-full transition-all outline-none group ${isCurrentRoute ? "bg-neutral-100 text-black " : "text-neutral-500 hover:text-black hover:bg-neutral-50"}`,
|
|
1485
|
+
title: isCollapsed && !isMobileOpen ? item.name : void 0
|
|
1486
|
+
},
|
|
1487
|
+
/* @__PURE__ */ React25.createElement(
|
|
1488
|
+
HugeiconsIcon13,
|
|
1489
|
+
{
|
|
1490
|
+
icon: item.icon,
|
|
1491
|
+
size: 18,
|
|
1492
|
+
className: `shrink-0 transition-colors ${isCurrentRoute ? "text-black" : "text-neutral-400 group-hover:text-black"}`
|
|
1493
|
+
}
|
|
1494
|
+
),
|
|
1495
|
+
(!isCollapsed || isMobileOpen) && /* @__PURE__ */ React25.createElement("span", { className: "text-xs tracking-wide truncate" }, item.name)
|
|
1496
|
+
);
|
|
1497
|
+
})),
|
|
1498
|
+
/* @__PURE__ */ React25.createElement("div", { className: "p-4 hidden md:block shrink-0" }, /* @__PURE__ */ React25.createElement(
|
|
1499
|
+
"button",
|
|
1500
|
+
{
|
|
1501
|
+
onClick: () => setIsCollapsed(!isCollapsed),
|
|
1502
|
+
className: "flex items-center justify-center md:justify-start gap-3 w-full p-2.5 rounded-full text-neutral-500 hover:text-black hover:bg-neutral-50 transition-colors outline-none"
|
|
1503
|
+
},
|
|
1504
|
+
/* @__PURE__ */ React25.createElement(HugeiconsIcon13, { icon: SidebarLeft01Icon, size: 18, className: "shrink-0 text-neutral-400" }),
|
|
1505
|
+
!isCollapsed && /* @__PURE__ */ React25.createElement("span", { className: "text-xs" }, "Collapse")
|
|
1506
|
+
))
|
|
1507
|
+
), showInterceptDialog && /* @__PURE__ */ React25.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4 pointer-events-auto" }, /* @__PURE__ */ React25.createElement(
|
|
1508
|
+
"div",
|
|
1509
|
+
{
|
|
1510
|
+
className: "absolute inset-0 bg-black/30",
|
|
1511
|
+
onClick: onCancelIntercept
|
|
1512
|
+
}
|
|
1513
|
+
), /* @__PURE__ */ React25.createElement("div", { className: "relative w-72 bg-white rounded-2xl flex flex-col items-center overflow-hidden shadow-2xl animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React25.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React25.createElement("h3", { className: "text-[14px] text-black tracking-tight mb-1" }, interceptTitle), /* @__PURE__ */ React25.createElement("p", { className: "text-[12px] text-neutral-500 leading-snug mt-2" }, interceptMessage)), /* @__PURE__ */ React25.createElement("div", { className: "w-full flex" }, /* @__PURE__ */ React25.createElement(
|
|
1514
|
+
"button",
|
|
1515
|
+
{
|
|
1516
|
+
onClick: onCancelIntercept,
|
|
1517
|
+
className: "flex-1 py-2 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors outline-none"
|
|
1518
|
+
},
|
|
1519
|
+
"Cancel"
|
|
1520
|
+
), /* @__PURE__ */ React25.createElement(
|
|
1521
|
+
"button",
|
|
1522
|
+
{
|
|
1523
|
+
onClick: onConfirmIntercept,
|
|
1524
|
+
className: "flex-1 py-2 text-[13px] text-[#F16A50] hover:bg-neutral-50 transition-colors flex justify-center items-center outline-none"
|
|
1525
|
+
},
|
|
1526
|
+
"Leave"
|
|
1527
|
+
)))));
|
|
1528
|
+
};
|
|
1529
|
+
|
|
1530
|
+
// src/components/UniversalHeader.tsx
|
|
1531
|
+
import React26, { useState as useState10 } from "react";
|
|
1532
|
+
import Link11 from "next/link";
|
|
1533
|
+
import { HugeiconsIcon as HugeiconsIcon14 } from "@hugeicons/react";
|
|
1534
|
+
import {
|
|
1535
|
+
ArrowDataTransferHorizontalIcon,
|
|
1536
|
+
SidebarLeft01Icon as SidebarLeft01Icon2,
|
|
1537
|
+
CheckmarkCircle01Icon,
|
|
1538
|
+
LogoutCircle02Icon,
|
|
1539
|
+
ArrowLeft01Icon as ArrowLeft01Icon2
|
|
1540
|
+
} from "@hugeicons/core-free-icons";
|
|
1541
|
+
var ButtonSpinner = () => /* @__PURE__ */ React26.createElement("svg", { className: "animate-spin h-4 w-4 text-neutral-400", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24" }, /* @__PURE__ */ React26.createElement("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), /* @__PURE__ */ React26.createElement("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" }));
|
|
1542
|
+
var UniversalHeader = ({
|
|
1543
|
+
onOpenMobile,
|
|
1544
|
+
title,
|
|
1545
|
+
subtitle,
|
|
1546
|
+
homeUrl = "/app",
|
|
1547
|
+
showBackButton = false,
|
|
1548
|
+
backUrl = "/app",
|
|
1549
|
+
showWorkspaceSwitcher = false,
|
|
1550
|
+
workspaces = [],
|
|
1551
|
+
activeWorkspaceId,
|
|
1552
|
+
onSwitchWorkspace,
|
|
1553
|
+
showLogoutAction = false,
|
|
1554
|
+
onLogout
|
|
1555
|
+
}) => {
|
|
1556
|
+
const [showSwitcherDialog, setShowSwitcherDialog] = useState10(false);
|
|
1557
|
+
const [showLogoutDialog, setShowLogoutDialog] = useState10(false);
|
|
1558
|
+
const [isLoggingOut, setIsLoggingOut] = useState10(false);
|
|
1559
|
+
const handleLogoutInitiation = async () => {
|
|
1560
|
+
if (isLoggingOut || !onLogout) return;
|
|
1561
|
+
setIsLoggingOut(true);
|
|
1562
|
+
try {
|
|
1563
|
+
await onLogout();
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
setIsLoggingOut(false);
|
|
1566
|
+
setShowLogoutDialog(false);
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
return /* @__PURE__ */ React26.createElement("div", { className: "w-full shrink-0 z-50 flex flex-col relative pointer-events-none" }, /* @__PURE__ */ React26.createElement("div", { className: "w-full pointer-events-auto relative z-20" }, /* @__PURE__ */ React26.createElement("header", { className: "w-full bg-white pt-6 pb-3" }, /* @__PURE__ */ React26.createElement("div", { className: "max-w-7xl mx-auto px-6 flex justify-between items-center" }, /* @__PURE__ */ React26.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React26.createElement(
|
|
1570
|
+
"button",
|
|
1571
|
+
{
|
|
1572
|
+
className: "md:hidden text-neutral-500 hover:text-black transition-colors outline-none",
|
|
1573
|
+
onClick: onOpenMobile,
|
|
1574
|
+
"aria-label": "Open Menu"
|
|
1575
|
+
},
|
|
1576
|
+
/* @__PURE__ */ React26.createElement(HugeiconsIcon14, { icon: SidebarLeft01Icon2, size: 24 })
|
|
1577
|
+
), /* @__PURE__ */ React26.createElement(Link11, { href: homeUrl, className: "flex items-center gap-3 transition-opacity hover:opacity-70 outline-none" }, /* @__PURE__ */ React26.createElement("div", { className: "flex flex-col justify-center" }, /* @__PURE__ */ React26.createElement("span", { className: "text-[13px] text-black leading-none truncate tracking-wide mb-1" }, title), subtitle && /* @__PURE__ */ React26.createElement("span", { className: "text-[9px] text-neutral-500 truncate tracking-[0.2em] leading-none" }, subtitle)))), /* @__PURE__ */ React26.createElement("div", { className: "flex items-center gap-3" }, showWorkspaceSwitcher && workspaces.length > 0 && /* @__PURE__ */ React26.createElement(
|
|
1578
|
+
"button",
|
|
1579
|
+
{
|
|
1580
|
+
onClick: () => setShowSwitcherDialog(true),
|
|
1581
|
+
className: "w-10 h-10 rounded-full border border-neutral-200 bg-white flex items-center justify-center text-neutral-500 hover:text-black hover:border-black hover:bg-neutral-50 transition-all group outline-none",
|
|
1582
|
+
"aria-label": "Switch Workspace"
|
|
1583
|
+
},
|
|
1584
|
+
/* @__PURE__ */ React26.createElement(HugeiconsIcon14, { icon: ArrowDataTransferHorizontalIcon, size: 16, className: "transition-transform group-hover:-translate-x-0.5" })
|
|
1585
|
+
), showLogoutAction && /* @__PURE__ */ React26.createElement(
|
|
1586
|
+
"button",
|
|
1587
|
+
{
|
|
1588
|
+
onClick: () => setShowLogoutDialog(true),
|
|
1589
|
+
className: "w-10 h-10 rounded-full border border-neutral-200 bg-white flex items-center justify-center text-neutral-500 hover:text-black hover:border-black hover:bg-neutral-50 transition-all group outline-none",
|
|
1590
|
+
"aria-label": "Secure Logout"
|
|
1591
|
+
},
|
|
1592
|
+
/* @__PURE__ */ React26.createElement(HugeiconsIcon14, { icon: LogoutCircle02Icon, size: 16, className: "transition-transform group-hover:-translate-x-0.5" })
|
|
1593
|
+
), showBackButton && /* @__PURE__ */ React26.createElement(
|
|
1594
|
+
Link11,
|
|
1595
|
+
{
|
|
1596
|
+
href: backUrl,
|
|
1597
|
+
className: "flex items-center justify-center w-10 h-10 border border-neutral-200 bg-white rounded-full text-neutral-500 hover:text-black hover:border-black hover:bg-neutral-50 transition-all group outline-none",
|
|
1598
|
+
"aria-label": "Go Back"
|
|
1599
|
+
},
|
|
1600
|
+
/* @__PURE__ */ React26.createElement(HugeiconsIcon14, { icon: ArrowLeft01Icon2, size: 16, className: "transition-transform group-hover:-translate-x-0.5" })
|
|
1601
|
+
))))), showSwitcherDialog && showWorkspaceSwitcher && workspaces.length > 0 && /* @__PURE__ */ React26.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4 pointer-events-auto" }, /* @__PURE__ */ React26.createElement(
|
|
1602
|
+
"div",
|
|
1603
|
+
{
|
|
1604
|
+
className: "absolute inset-0 bg-black/30",
|
|
1605
|
+
onClick: () => setShowSwitcherDialog(false)
|
|
1606
|
+
}
|
|
1607
|
+
), /* @__PURE__ */ React26.createElement("div", { className: "relative w-full max-w-sm bg-white rounded-2xl flex flex-col overflow-hidden shadow-2xl animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React26.createElement("div", { className: "p-5 text-center w-full" }, /* @__PURE__ */ React26.createElement("h3", { className: "text-[14px] text-black tracking-tight mb-1" }, "Switch Workspace"), /* @__PURE__ */ React26.createElement("p", { className: "text-[11px] text-neutral-500 leading-snug" }, "Select an organization to switch your current context.")), /* @__PURE__ */ React26.createElement("div", { className: "max-h-75 overflow-y-auto w-full custom-scrollbar" }, workspaces.map((org) => /* @__PURE__ */ React26.createElement(
|
|
1608
|
+
"button",
|
|
1609
|
+
{
|
|
1610
|
+
key: org.id,
|
|
1611
|
+
onClick: () => {
|
|
1612
|
+
if (onSwitchWorkspace) onSwitchWorkspace(org.id);
|
|
1613
|
+
setShowSwitcherDialog(false);
|
|
1614
|
+
},
|
|
1615
|
+
className: "w-full text-left px-5 py-4 flex items-center justify-between hover:bg-neutral-50 transition-colors group outline-none last:border-0"
|
|
1616
|
+
},
|
|
1617
|
+
/* @__PURE__ */ React26.createElement("div", { className: "flex flex-col truncate pr-4" }, /* @__PURE__ */ React26.createElement("span", { className: `text-[13px] truncate ${activeWorkspaceId === org.id ? "text-black" : "text-neutral-600 group-hover:text-black"}` }, org.name), /* @__PURE__ */ React26.createElement("span", { className: "text-[9px] text-neutral-400 tracking-[0.2em] mt-1" }, "Role: ", org.role)),
|
|
1618
|
+
activeWorkspaceId === org.id && /* @__PURE__ */ React26.createElement(HugeiconsIcon14, { icon: CheckmarkCircle01Icon, size: 18, className: "text-black shrink-0" })
|
|
1619
|
+
))), /* @__PURE__ */ React26.createElement("div", { className: "w-full flex mt-auto" }, /* @__PURE__ */ React26.createElement(
|
|
1620
|
+
"button",
|
|
1621
|
+
{
|
|
1622
|
+
onClick: () => setShowSwitcherDialog(false),
|
|
1623
|
+
className: "w-full py-2.5 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors outline-none"
|
|
1624
|
+
},
|
|
1625
|
+
"Cancel"
|
|
1626
|
+
)))), showLogoutDialog && showLogoutAction && /* @__PURE__ */ React26.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4 pointer-events-auto" }, /* @__PURE__ */ React26.createElement(
|
|
1627
|
+
"div",
|
|
1628
|
+
{
|
|
1629
|
+
className: "absolute inset-0 bg-black/30",
|
|
1630
|
+
onClick: () => !isLoggingOut && setShowLogoutDialog(false)
|
|
1631
|
+
}
|
|
1632
|
+
), /* @__PURE__ */ React26.createElement("div", { className: "relative w-72 bg-white rounded-2xl flex flex-col items-center overflow-hidden shadow-2xl animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React26.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React26.createElement("h3", { className: "text-[14px] text-black tracking-tight mb-1" }, "Secure Logout"), /* @__PURE__ */ React26.createElement("p", { className: "text-[12px] text-neutral-500 leading-snug mt-2" }, "Are you sure you want to log out? You will need to authenticate to return.")), /* @__PURE__ */ React26.createElement("div", { className: "w-full flex" }, /* @__PURE__ */ React26.createElement(
|
|
1633
|
+
"button",
|
|
1634
|
+
{
|
|
1635
|
+
onClick: () => setShowLogoutDialog(false),
|
|
1636
|
+
disabled: isLoggingOut,
|
|
1637
|
+
className: "flex-1 py-2 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 outline-none"
|
|
1638
|
+
},
|
|
1639
|
+
"Cancel"
|
|
1640
|
+
), /* @__PURE__ */ React26.createElement(
|
|
1641
|
+
"button",
|
|
1642
|
+
{
|
|
1643
|
+
onClick: handleLogoutInitiation,
|
|
1644
|
+
disabled: isLoggingOut,
|
|
1645
|
+
className: "flex-1 py-2 text-[13px] text-[#F16A50] hover:bg-neutral-50 transition-colors disabled:opacity-50 flex justify-center items-center outline-none"
|
|
1646
|
+
},
|
|
1647
|
+
isLoggingOut ? /* @__PURE__ */ React26.createElement(ButtonSpinner, null) : "Log Out"
|
|
1648
|
+
)))));
|
|
1649
|
+
};
|
|
1650
|
+
|
|
1651
|
+
// src/components/UniversalOrganizationPage.tsx
|
|
1652
|
+
import React27, { useState as useState11, useEffect as useEffect8 } from "react";
|
|
1653
|
+
import toast2 from "react-hot-toast";
|
|
1654
|
+
var InputSpinner2 = () => /* @__PURE__ */ React27.createElement("svg", { className: "animate-spin h-4 w-4 text-neutral-400", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24" }, /* @__PURE__ */ React27.createElement("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), /* @__PURE__ */ React27.createElement("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" }));
|
|
1655
|
+
var UniversalOrganizationPage = ({
|
|
1656
|
+
initialOrgName,
|
|
1657
|
+
initialSlug,
|
|
1658
|
+
orgId,
|
|
1659
|
+
isReadOnly = false,
|
|
1660
|
+
slugPrefixUrl = "ecosystem.com/org/",
|
|
1661
|
+
onSaveConfiguration,
|
|
1662
|
+
onCheckSlugAvailability
|
|
1663
|
+
}) => {
|
|
1664
|
+
const [orgName, setOrgName] = useState11(initialOrgName);
|
|
1665
|
+
const [slug, setSlug] = useState11(initialSlug);
|
|
1666
|
+
const [isCheckingSlug, setIsCheckingSlug] = useState11(false);
|
|
1667
|
+
const [slugAvailable, setSlugAvailable] = useState11(null);
|
|
1668
|
+
const [isSubmitting, setIsSubmitting] = useState11(false);
|
|
1669
|
+
useEffect8(() => {
|
|
1670
|
+
setOrgName(initialOrgName || "");
|
|
1671
|
+
setSlug(initialSlug || "");
|
|
1672
|
+
}, [initialOrgName, initialSlug]);
|
|
1673
|
+
const handleOrgNameChange = (val) => {
|
|
1674
|
+
setOrgName(val.replace(/[^a-zA-Z0-9\s-]/g, "").substring(0, 50));
|
|
1675
|
+
};
|
|
1676
|
+
const handleSlugChange = (val) => {
|
|
1677
|
+
setSlug(val.toLowerCase().replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").substring(0, 50));
|
|
1678
|
+
};
|
|
1679
|
+
useEffect8(() => {
|
|
1680
|
+
if (!slug || slug === initialSlug) {
|
|
1681
|
+
setSlugAvailable(null);
|
|
1682
|
+
setIsCheckingSlug(false);
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
if (slug.length < 3) {
|
|
1686
|
+
setSlugAvailable(false);
|
|
1687
|
+
setIsCheckingSlug(false);
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
setIsCheckingSlug(true);
|
|
1691
|
+
setSlugAvailable(null);
|
|
1692
|
+
const checkTimer = setTimeout(async () => {
|
|
1693
|
+
try {
|
|
1694
|
+
const res = await onCheckSlugAvailability(slug);
|
|
1695
|
+
setSlugAvailable(res.available);
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
setSlugAvailable(null);
|
|
1698
|
+
} finally {
|
|
1699
|
+
setIsCheckingSlug(false);
|
|
1700
|
+
}
|
|
1701
|
+
}, 1500);
|
|
1702
|
+
return () => clearTimeout(checkTimer);
|
|
1703
|
+
}, [slug, initialSlug, onCheckSlugAvailability]);
|
|
1704
|
+
const handleSave = async (e) => {
|
|
1705
|
+
e.preventDefault();
|
|
1706
|
+
if (isSubmitting || isCheckingSlug || isReadOnly) return;
|
|
1707
|
+
if (slug !== initialSlug && slugAvailable === false) {
|
|
1708
|
+
toast2.error("Please select an available workspace URL.");
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
setIsSubmitting(true);
|
|
1712
|
+
try {
|
|
1713
|
+
const payload = {
|
|
1714
|
+
organizationId: orgId,
|
|
1715
|
+
organizationName: orgName !== initialOrgName ? orgName : void 0,
|
|
1716
|
+
slug: slug !== initialSlug ? slug : void 0
|
|
1717
|
+
};
|
|
1718
|
+
const responseData = await onSaveConfiguration(payload);
|
|
1719
|
+
if (responseData.success) {
|
|
1720
|
+
toast2.success("Organization updated successfully.");
|
|
1721
|
+
setTimeout(() => window.location.reload(), 1e3);
|
|
1722
|
+
} else {
|
|
1723
|
+
toast2.error(responseData.error || "Failed to update organization.");
|
|
1724
|
+
setIsSubmitting(false);
|
|
1725
|
+
}
|
|
1726
|
+
} catch (error) {
|
|
1727
|
+
toast2.error("Service unavailable. Try again later.");
|
|
1728
|
+
setIsSubmitting(false);
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
const hasChanges = orgName !== initialOrgName || slug !== initialSlug;
|
|
1732
|
+
const isSaveDisabled = isSubmitting || isReadOnly || isCheckingSlug || !hasChanges || orgName.length < 3 || slug.length < 3 || slug !== initialSlug && slugAvailable === false;
|
|
1733
|
+
return /* @__PURE__ */ React27.createElement("div", { className: "flex flex-col gap-8 animate-in max-w-3xl rounded-2xl p-6 bg-white fade-in duration-300 min-h-full" }, /* @__PURE__ */ React27.createElement(ManagedToaster, null), /* @__PURE__ */ React27.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React27.createElement("div", null, /* @__PURE__ */ React27.createElement("h1", { className: "text-xl text-black mb-1 tracking-tight" }, "Organization"), /* @__PURE__ */ React27.createElement("p", { className: "text-xs text-neutral-500" }, "Manage your tenant workspace details and identity.")), isReadOnly && /* @__PURE__ */ React27.createElement("span", { className: "px-3 py-1 bg-neutral-50 text-neutral-500 rounded-full text-[10px] tracking-[0.2em] shrink-0" }, "Read Only Access")), /* @__PURE__ */ React27.createElement("div", { className: "w-full max-w-2xl" }, /* @__PURE__ */ React27.createElement("form", { className: "flex flex-col gap-8", onSubmit: handleSave, autoComplete: "off" }, /* @__PURE__ */ React27.createElement(
|
|
1734
|
+
TextInput,
|
|
1735
|
+
{
|
|
1736
|
+
label: "Organization Name",
|
|
1737
|
+
value: orgName,
|
|
1738
|
+
onChange: handleOrgNameChange,
|
|
1739
|
+
disabled: isReadOnly || isSubmitting,
|
|
1740
|
+
placeholder: "Acme Corporation",
|
|
1741
|
+
maxLength: 50
|
|
1742
|
+
}
|
|
1743
|
+
), /* @__PURE__ */ React27.createElement("div", { className: "space-y-2 relative w-full" }, /* @__PURE__ */ React27.createElement("label", { className: "text-[10px] text-neutral-400 tracking-[0.2em] block uppercase" }, "Organization Slug"), /* @__PURE__ */ React27.createElement("div", { className: "flex items-center relative w-full" }, /* @__PURE__ */ React27.createElement("span", { className: "text-neutral-400 text-sm py-3 pr-2 border-b border-neutral-200 shrink-0" }, slugPrefixUrl), /* @__PURE__ */ React27.createElement(
|
|
1744
|
+
"input",
|
|
1745
|
+
{
|
|
1746
|
+
type: "text",
|
|
1747
|
+
value: slug,
|
|
1748
|
+
disabled: isReadOnly || isSubmitting,
|
|
1749
|
+
onChange: (e) => handleSlugChange(e.target.value),
|
|
1750
|
+
spellCheck: "false",
|
|
1751
|
+
autoComplete: "off",
|
|
1752
|
+
className: "w-full px-2 py-3 text-sm bg-transparent border-b border-neutral-200 text-black transition-all outline-none focus:border-black pr-8 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1753
|
+
placeholder: "acme-corp"
|
|
1754
|
+
}
|
|
1755
|
+
), /* @__PURE__ */ React27.createElement("div", { className: "absolute right-2 top-1/2 -translate-y-1/2" }, isCheckingSlug && /* @__PURE__ */ React27.createElement(InputSpinner2, null), !isCheckingSlug && !isReadOnly && slug !== initialSlug && slug.length >= 3 && slugAvailable === false && /* @__PURE__ */ React27.createElement("span", { className: "inline-flex items-center justify-center w-4 h-4 rounded-full bg-red-100" }, /* @__PURE__ */ React27.createElement("svg", { className: "w-2 h-2 text-red-600", viewBox: "0 0 20 20", fill: "currentColor" }, /* @__PURE__ */ React27.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" }))), !isCheckingSlug && !isReadOnly && slug !== initialSlug && slug.length >= 3 && slugAvailable === true && /* @__PURE__ */ React27.createElement("span", { className: "inline-flex items-center justify-center w-4 h-4 rounded-full bg-green-100" }, /* @__PURE__ */ React27.createElement("svg", { className: "w-2 h-2 text-green-600", viewBox: "0 0 20 20", fill: "currentColor" }, /* @__PURE__ */ React27.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M16.707 5.293a1 1 0 00-1.414 0L8 12.586 4.707 9.293a1 1 0 10-1.414 1.414l4 4a1 1 0 001.414 0l8-8a1 1 0 000-1.414z" })))))), /* @__PURE__ */ React27.createElement("div", { className: "pt-8 mt-2 flex items-center gap-4" }, /* @__PURE__ */ React27.createElement(
|
|
1756
|
+
ThreeDActionButton,
|
|
1757
|
+
{
|
|
1758
|
+
type: "submit",
|
|
1759
|
+
disabled: isSaveDisabled,
|
|
1760
|
+
isLoading: isSubmitting,
|
|
1761
|
+
className: "min-w-32"
|
|
1762
|
+
},
|
|
1763
|
+
"Save Changes"
|
|
1764
|
+
), hasChanges && !isSubmitting && !isReadOnly && /* @__PURE__ */ React27.createElement(
|
|
1765
|
+
"button",
|
|
1766
|
+
{
|
|
1767
|
+
type: "button",
|
|
1768
|
+
onClick: () => {
|
|
1769
|
+
setOrgName(initialOrgName);
|
|
1770
|
+
setSlug(initialSlug);
|
|
1771
|
+
},
|
|
1772
|
+
className: "text-[11px] tracking-widest text-neutral-500 hover:text-black transition-colors outline-none"
|
|
1773
|
+
},
|
|
1774
|
+
"Cancel"
|
|
1775
|
+
)))));
|
|
1776
|
+
};
|
|
1777
|
+
|
|
1778
|
+
// src/components/UniversalIdentityPage.tsx
|
|
1779
|
+
import React28, { useState as useState12, useEffect as useEffect9 } from "react";
|
|
1780
|
+
import toast3 from "react-hot-toast";
|
|
1781
|
+
var ButtonSpinner2 = () => /* @__PURE__ */ React28.createElement("svg", { className: "animate-spin h-4 w-4 text-neutral-400", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24" }, /* @__PURE__ */ React28.createElement("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), /* @__PURE__ */ React28.createElement("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" }));
|
|
1782
|
+
var UniversalIdentityPage = ({
|
|
1783
|
+
headerTitle,
|
|
1784
|
+
headerDescription,
|
|
1785
|
+
primaryInputLabel,
|
|
1786
|
+
secondaryInputLabel,
|
|
1787
|
+
initialPrimaryValue,
|
|
1788
|
+
initialSecondaryValue = "",
|
|
1789
|
+
resourceId,
|
|
1790
|
+
isReadOnly = false,
|
|
1791
|
+
allowDeletion = false,
|
|
1792
|
+
deleteWarningText = "This action will permanently delete all records and data for this entity.",
|
|
1793
|
+
onSaveIdentity,
|
|
1794
|
+
onDeleteResource,
|
|
1795
|
+
onSuccessfulDeleteRedirect = "/app"
|
|
1796
|
+
}) => {
|
|
1797
|
+
const [primaryValue, setPrimaryValue] = useState12(initialPrimaryValue);
|
|
1798
|
+
const [secondaryValue, setSecondaryValue] = useState12(initialSecondaryValue);
|
|
1799
|
+
const [isSubmitting, setIsSubmitting] = useState12(false);
|
|
1800
|
+
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState12(false);
|
|
1801
|
+
const [isDeleting, setIsDeleting] = useState12(false);
|
|
1802
|
+
useEffect9(() => {
|
|
1803
|
+
setPrimaryValue(initialPrimaryValue || "");
|
|
1804
|
+
setSecondaryValue(initialSecondaryValue || "");
|
|
1805
|
+
}, [initialPrimaryValue, initialSecondaryValue]);
|
|
1806
|
+
const handlePrimaryChange = (val) => {
|
|
1807
|
+
setPrimaryValue(val.replace(/[^a-zA-Z0-9\s-]/g, "").substring(0, 50));
|
|
1808
|
+
};
|
|
1809
|
+
const handleSecondaryChange = (val) => {
|
|
1810
|
+
setSecondaryValue(val.replace(/[^a-zA-Z0-9\s.,!-]/g, "").substring(0, 150));
|
|
1811
|
+
};
|
|
1812
|
+
const handleSave = async (e) => {
|
|
1813
|
+
e.preventDefault();
|
|
1814
|
+
if (isSubmitting || isReadOnly) return;
|
|
1815
|
+
setIsSubmitting(true);
|
|
1816
|
+
try {
|
|
1817
|
+
const res = await onSaveIdentity({
|
|
1818
|
+
resourceId,
|
|
1819
|
+
primaryValue: primaryValue !== initialPrimaryValue ? primaryValue : void 0,
|
|
1820
|
+
secondaryValue: secondaryValue !== initialSecondaryValue ? secondaryValue : void 0
|
|
1821
|
+
});
|
|
1822
|
+
if (res.success) {
|
|
1823
|
+
toast3.success("Update successful.");
|
|
1824
|
+
} else {
|
|
1825
|
+
toast3.error(res.error || "Update failed.");
|
|
1826
|
+
}
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
toast3.error("Service unavailable.");
|
|
1829
|
+
} finally {
|
|
1830
|
+
setIsSubmitting(false);
|
|
1831
|
+
}
|
|
1832
|
+
};
|
|
1833
|
+
const handleDelete = async () => {
|
|
1834
|
+
if (isDeleting || isReadOnly || !onDeleteResource) return;
|
|
1835
|
+
setIsDeleting(true);
|
|
1836
|
+
try {
|
|
1837
|
+
const res = await onDeleteResource(resourceId);
|
|
1838
|
+
if (res.success) {
|
|
1839
|
+
toast3.success("Deletion permanent.");
|
|
1840
|
+
setIsDeleteModalOpen(false);
|
|
1841
|
+
window.location.href = onSuccessfulDeleteRedirect;
|
|
1842
|
+
} else {
|
|
1843
|
+
toast3.error(res.error || "Failed to delete.");
|
|
1844
|
+
setIsDeleting(false);
|
|
1845
|
+
}
|
|
1846
|
+
} catch (error) {
|
|
1847
|
+
toast3.error("Network error occurred.");
|
|
1848
|
+
setIsDeleting(false);
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
const hasChanges = primaryValue !== initialPrimaryValue || secondaryValue !== initialSecondaryValue;
|
|
1852
|
+
const isSaveDisabled = isSubmitting || isReadOnly || !hasChanges || primaryValue.trim().length < 3;
|
|
1853
|
+
return /* @__PURE__ */ React28.createElement("div", { className: "flex flex-col gap-8 animate-in max-w-3xl rounded-2xl p-6 bg-white fade-in duration-300 min-h-full" }, /* @__PURE__ */ React28.createElement(ManagedToaster, null), /* @__PURE__ */ React28.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React28.createElement("div", null, /* @__PURE__ */ React28.createElement("h1", { className: "text-xl text-black mb-1 tracking-tight" }, headerTitle), /* @__PURE__ */ React28.createElement("p", { className: "text-xs text-neutral-500" }, headerDescription)), isReadOnly && /* @__PURE__ */ React28.createElement("span", { className: "px-3 py-1 bg-neutral-50 text-neutral-500 rounded-full text-[10px] tracking-[0.2em] shrink-0" }, "Read Only Access")), /* @__PURE__ */ React28.createElement("div", { className: "w-full max-w-2xl" }, /* @__PURE__ */ React28.createElement("form", { className: "flex flex-col gap-8", onSubmit: handleSave, autoComplete: "off" }, /* @__PURE__ */ React28.createElement(
|
|
1854
|
+
TextInput,
|
|
1855
|
+
{
|
|
1856
|
+
label: primaryInputLabel,
|
|
1857
|
+
value: primaryValue,
|
|
1858
|
+
onChange: handlePrimaryChange,
|
|
1859
|
+
disabled: isReadOnly || isSubmitting,
|
|
1860
|
+
maxLength: 50
|
|
1861
|
+
}
|
|
1862
|
+
), secondaryInputLabel && /* @__PURE__ */ React28.createElement(
|
|
1863
|
+
TextInput,
|
|
1864
|
+
{
|
|
1865
|
+
label: secondaryInputLabel,
|
|
1866
|
+
value: secondaryValue,
|
|
1867
|
+
onChange: handleSecondaryChange,
|
|
1868
|
+
disabled: isReadOnly || isSubmitting,
|
|
1869
|
+
maxLength: 150
|
|
1870
|
+
}
|
|
1871
|
+
), !isReadOnly && /* @__PURE__ */ React28.createElement("div", { className: "pt-8 mt-2 flex flex-col sm:flex-row sm:items-center justify-between gap-6" }, /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-4 w-full sm:w-auto" }, /* @__PURE__ */ React28.createElement(
|
|
1872
|
+
ThreeDActionButton,
|
|
1873
|
+
{
|
|
1874
|
+
type: "submit",
|
|
1875
|
+
disabled: isSaveDisabled,
|
|
1876
|
+
isLoading: isSubmitting,
|
|
1877
|
+
className: "min-w-32 w-full sm:w-auto"
|
|
1878
|
+
},
|
|
1879
|
+
"Save Changes"
|
|
1880
|
+
), hasChanges && !isSubmitting && /* @__PURE__ */ React28.createElement(
|
|
1881
|
+
"button",
|
|
1882
|
+
{
|
|
1883
|
+
type: "button",
|
|
1884
|
+
onClick: () => {
|
|
1885
|
+
setPrimaryValue(initialPrimaryValue);
|
|
1886
|
+
setSecondaryValue(initialSecondaryValue);
|
|
1887
|
+
},
|
|
1888
|
+
className: "text-[11px] tracking-widest text-neutral-500 hover:text-black transition-colors w-full sm:w-auto py-2 sm:py-0 outline-none"
|
|
1889
|
+
},
|
|
1890
|
+
"Cancel"
|
|
1891
|
+
)), allowDeletion && /* @__PURE__ */ React28.createElement(
|
|
1892
|
+
"button",
|
|
1893
|
+
{
|
|
1894
|
+
type: "button",
|
|
1895
|
+
onClick: () => setIsDeleteModalOpen(true),
|
|
1896
|
+
className: "px-6 py-2 text-[11px] tracking-widest text-red-400 border border-red-200 hover:border-red-400 rounded-full transition-all w-full sm:w-auto outline-none"
|
|
1897
|
+
},
|
|
1898
|
+
"Delete ",
|
|
1899
|
+
headerTitle.split(" ")[0]
|
|
1900
|
+
)))), isDeleteModalOpen && !isReadOnly && allowDeletion && /* @__PURE__ */ React28.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React28.createElement("div", { className: "absolute inset-0 bg-black/30 shadow-2xl", onClick: () => !isDeleting && setIsDeleteModalOpen(false) }), /* @__PURE__ */ React28.createElement("div", { className: "relative w-72 bg-white shadow-2xl rounded-2xl flex flex-col items-center overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React28.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React28.createElement("h3", { className: "text-[14px] text-black tracking-tight mb-1" }, "Confirm Deletion"), /* @__PURE__ */ React28.createElement("p", { className: "text-[12px] text-neutral-500 leading-snug mt-2" }, deleteWarningText)), /* @__PURE__ */ React28.createElement("div", { className: "w-full flex" }, /* @__PURE__ */ React28.createElement(
|
|
1901
|
+
"button",
|
|
1902
|
+
{
|
|
1903
|
+
type: "button",
|
|
1904
|
+
onClick: () => setIsDeleteModalOpen(false),
|
|
1905
|
+
disabled: isDeleting,
|
|
1906
|
+
className: "flex-1 py-2 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 outline-none"
|
|
1907
|
+
},
|
|
1908
|
+
"Cancel"
|
|
1909
|
+
), /* @__PURE__ */ React28.createElement(
|
|
1910
|
+
"button",
|
|
1911
|
+
{
|
|
1912
|
+
type: "button",
|
|
1913
|
+
onClick: handleDelete,
|
|
1914
|
+
disabled: isDeleting,
|
|
1915
|
+
className: "flex-1 py-2 text-[13px] text-red-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 flex justify-center items-center outline-none"
|
|
1916
|
+
},
|
|
1917
|
+
isDeleting ? /* @__PURE__ */ React28.createElement(ButtonSpinner2, null) : "Delete"
|
|
1918
|
+
)))));
|
|
1919
|
+
};
|
|
1920
|
+
|
|
1921
|
+
// src/components/UniversalMembersPage.tsx
|
|
1922
|
+
import React29, { useState as useState13, useEffect as useEffect10, useRef as useRef4 } from "react";
|
|
1923
|
+
import toast4 from "react-hot-toast";
|
|
1924
|
+
import { HugeiconsIcon as HugeiconsIcon15 } from "@hugeicons/react";
|
|
1925
|
+
import {
|
|
1926
|
+
UserAdd01Icon,
|
|
1927
|
+
ArrowLeft01Icon as ArrowLeft01Icon3,
|
|
1928
|
+
ArrowRight01Icon as ArrowRight01Icon3,
|
|
1929
|
+
Delete01Icon,
|
|
1930
|
+
ArrowDown01Icon as ArrowDown01Icon2,
|
|
1931
|
+
Loading03Icon as Loading03Icon6
|
|
1932
|
+
} from "@hugeicons/core-free-icons";
|
|
1933
|
+
var ButtonSpinner3 = () => /* @__PURE__ */ React29.createElement(HugeiconsIcon15, { icon: Loading03Icon6, size: 16, className: "animate-spin text-white" });
|
|
1934
|
+
var PageSpinner2 = () => /* @__PURE__ */ React29.createElement("div", { className: "flex justify-center items-center py-12" }, /* @__PURE__ */ React29.createElement(HugeiconsIcon15, { icon: Loading03Icon6, size: 32, className: "animate-spin mb-4 text-black" }));
|
|
1935
|
+
var getInitials = (name) => {
|
|
1936
|
+
if (!name) return "U";
|
|
1937
|
+
const parts = name.trim().split(" ");
|
|
1938
|
+
if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
1939
|
+
return parts[0].substring(0, 2).toUpperCase();
|
|
1940
|
+
};
|
|
1941
|
+
var resolveThemeColor = (colorCode) => {
|
|
1942
|
+
switch (colorCode) {
|
|
1943
|
+
case "COLOR_A":
|
|
1944
|
+
return "bg-blue-100 border-blue-200";
|
|
1945
|
+
case "COLOR_B":
|
|
1946
|
+
return "bg-emerald-100 border-emerald-200";
|
|
1947
|
+
case "COLOR_C":
|
|
1948
|
+
return "bg-rose-100 border-rose-200";
|
|
1949
|
+
case "COLOR_D":
|
|
1950
|
+
return "bg-amber-100 border-amber-200";
|
|
1951
|
+
case "COLOR_E":
|
|
1952
|
+
return "bg-purple-100 border-purple-200";
|
|
1953
|
+
case "COLOR_F":
|
|
1954
|
+
return "bg-pink-100 border-pink-200";
|
|
1955
|
+
default:
|
|
1956
|
+
return "bg-neutral-100 border-neutral-200";
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
var cleanName = (val) => val.replace(/[^a-zA-Z\s-]/g, "").substring(0, 50);
|
|
1960
|
+
var UniversalMembersPage = ({
|
|
1961
|
+
headerTitle,
|
|
1962
|
+
headerDescription,
|
|
1963
|
+
currentUserId,
|
|
1964
|
+
members,
|
|
1965
|
+
isLoading,
|
|
1966
|
+
currentPage,
|
|
1967
|
+
totalPages,
|
|
1968
|
+
onPageChange,
|
|
1969
|
+
canManage,
|
|
1970
|
+
canChangeRoles,
|
|
1971
|
+
availableRoles,
|
|
1972
|
+
requireNamesForInvite,
|
|
1973
|
+
onInviteMember,
|
|
1974
|
+
onRemoveMember,
|
|
1975
|
+
onUpdateRole
|
|
1976
|
+
}) => {
|
|
1977
|
+
const [currentView, setCurrentView] = useState13("list");
|
|
1978
|
+
const [selectedMember, setSelectedMember] = useState13(null);
|
|
1979
|
+
const [inviteEmail, setInviteEmail] = useState13("");
|
|
1980
|
+
const [inviteFirst, setInviteFirst] = useState13("");
|
|
1981
|
+
const [inviteLast, setInviteLast] = useState13("");
|
|
1982
|
+
const [isInviting, setIsInviting] = useState13(false);
|
|
1983
|
+
const [isRoleModalOpen, setIsRoleModalOpen] = useState13(false);
|
|
1984
|
+
const [isUpdatingRole, setIsUpdatingRole] = useState13(false);
|
|
1985
|
+
const [memberToDelete, setMemberToDelete] = useState13(null);
|
|
1986
|
+
const [isDeleting, setIsDeleting] = useState13(false);
|
|
1987
|
+
const dropdownRef = useRef4(null);
|
|
1988
|
+
useEffect10(() => {
|
|
1989
|
+
function handleClickOutside(event) {
|
|
1990
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
1991
|
+
setIsRoleModalOpen(false);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1995
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
1996
|
+
}, []);
|
|
1997
|
+
const handleInvite = async (e) => {
|
|
1998
|
+
e.preventDefault();
|
|
1999
|
+
if (isInviting || !canManage) return;
|
|
2000
|
+
setIsInviting(true);
|
|
2001
|
+
try {
|
|
2002
|
+
const res = await onInviteMember(inviteEmail, inviteFirst, inviteLast);
|
|
2003
|
+
if (res.success) {
|
|
2004
|
+
toast4.success("Member added successfully.");
|
|
2005
|
+
setInviteEmail("");
|
|
2006
|
+
setInviteFirst("");
|
|
2007
|
+
setInviteLast("");
|
|
2008
|
+
setCurrentView("list");
|
|
2009
|
+
} else {
|
|
2010
|
+
toast4.error(res.error || "Failed to add member.");
|
|
2011
|
+
}
|
|
2012
|
+
} catch (error) {
|
|
2013
|
+
toast4.error("Service unavailable.");
|
|
2014
|
+
} finally {
|
|
2015
|
+
setIsInviting(false);
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
const handleDelete = async () => {
|
|
2019
|
+
if (!memberToDelete || isDeleting || !canManage) return;
|
|
2020
|
+
setIsDeleting(true);
|
|
2021
|
+
try {
|
|
2022
|
+
const res = await onRemoveMember(memberToDelete);
|
|
2023
|
+
if (res.success) {
|
|
2024
|
+
toast4.success("Member removed successfully.");
|
|
2025
|
+
setMemberToDelete(null);
|
|
2026
|
+
setCurrentView("list");
|
|
2027
|
+
} else {
|
|
2028
|
+
toast4.error(res.error || "Failed to remove member.");
|
|
2029
|
+
}
|
|
2030
|
+
} catch (error) {
|
|
2031
|
+
toast4.error("Service unavailable.");
|
|
2032
|
+
} finally {
|
|
2033
|
+
setIsDeleting(false);
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
const handleRoleUpdate = async (newRole) => {
|
|
2037
|
+
if (!selectedMember || isUpdatingRole || selectedMember.role === newRole || !canChangeRoles) {
|
|
2038
|
+
setIsRoleModalOpen(false);
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
setIsRoleModalOpen(false);
|
|
2042
|
+
setIsUpdatingRole(true);
|
|
2043
|
+
try {
|
|
2044
|
+
const res = await onUpdateRole(selectedMember, newRole);
|
|
2045
|
+
if (res.success) {
|
|
2046
|
+
toast4.success("Role updated successfully.");
|
|
2047
|
+
setSelectedMember({ ...selectedMember, role: newRole });
|
|
2048
|
+
} else {
|
|
2049
|
+
toast4.error(res.error || "Failed to update role.");
|
|
2050
|
+
}
|
|
2051
|
+
} catch (error) {
|
|
2052
|
+
toast4.error("Service unavailable.");
|
|
2053
|
+
} finally {
|
|
2054
|
+
setIsUpdatingRole(false);
|
|
2055
|
+
}
|
|
2056
|
+
};
|
|
2057
|
+
const goBack = () => {
|
|
2058
|
+
setCurrentView("list");
|
|
2059
|
+
setSelectedMember(null);
|
|
2060
|
+
setIsRoleModalOpen(false);
|
|
2061
|
+
};
|
|
2062
|
+
return /* @__PURE__ */ React29.createElement("div", { className: "flex flex-col gap-8 animate-in max-w-3xl fade-in duration-300 p-6 rounded-2xl bg-white min-h-full" }, /* @__PURE__ */ React29.createElement(ManagedToaster, null), /* @__PURE__ */ React29.createElement("div", { className: "flex flex-col sm:flex-row sm:items-start justify-between gap-4" }, currentView === "list" && /* @__PURE__ */ React29.createElement(React29.Fragment, null, /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("h1", { className: "text-xl text-black mb-1 tracking-tight" }, headerTitle), /* @__PURE__ */ React29.createElement("p", { className: "text-xs text-neutral-500" }, headerDescription)), canManage && /* @__PURE__ */ React29.createElement(
|
|
2063
|
+
ThreeDActionButton,
|
|
2064
|
+
{
|
|
2065
|
+
onClick: () => setCurrentView("invite"),
|
|
2066
|
+
className: "w-fit shrink-0 gap-2"
|
|
2067
|
+
},
|
|
2068
|
+
/* @__PURE__ */ React29.createElement(HugeiconsIcon15, { icon: UserAdd01Icon, size: 12 }),
|
|
2069
|
+
"Add Member"
|
|
2070
|
+
)), currentView !== "list" && /* @__PURE__ */ React29.createElement("div", { className: "flex flex-col items-start gap-3" }, /* @__PURE__ */ React29.createElement("button", { onClick: goBack, className: "text-[10px] text-neutral-400 hover:text-black tracking-[0.2em] flex items-center gap-1.5 transition-colors outline-none" }, /* @__PURE__ */ React29.createElement(HugeiconsIcon15, { icon: ArrowLeft01Icon3, size: 12 }), " Back"), /* @__PURE__ */ React29.createElement("h1", { className: "text-lg text-black tracking-tight" }, currentView === "invite" ? "Add New Member" : "Member Profile"))), currentView === "list" && /* @__PURE__ */ React29.createElement("div", { className: "w-full overflow-hidden" }, isLoading ? /* @__PURE__ */ React29.createElement(PageSpinner2, null) : /* @__PURE__ */ React29.createElement("div", { className: "flex flex-col min-w-0" }, /* @__PURE__ */ React29.createElement("div", { className: "divide-y divide-neutral-100" }, members.map((member) => /* @__PURE__ */ React29.createElement(
|
|
2071
|
+
"div",
|
|
2072
|
+
{
|
|
2073
|
+
key: member.id,
|
|
2074
|
+
onClick: () => {
|
|
2075
|
+
setSelectedMember(member);
|
|
2076
|
+
setCurrentView("details");
|
|
2077
|
+
},
|
|
2078
|
+
className: "flex items-center justify-between p-4 sm:p-5 hover:bg-neutral-50/50 transition-colors cursor-pointer group min-w-0"
|
|
2079
|
+
},
|
|
2080
|
+
/* @__PURE__ */ React29.createElement("div", { className: "flex items-center gap-3 sm:gap-4 min-w-0 flex-1" }, member.displayImage && member.displayImage !== "NO_IMAGE" ? /* @__PURE__ */ React29.createElement("div", { className: "w-10 h-10 shrink-0 rounded-full overflow-hidden bg-neutral-100" }, /* @__PURE__ */ React29.createElement(
|
|
2081
|
+
"img",
|
|
2082
|
+
{
|
|
2083
|
+
src: member.displayImage,
|
|
2084
|
+
alt: member.fullName,
|
|
2085
|
+
className: "w-full h-full object-cover blur-sm transition-all duration-300",
|
|
2086
|
+
onLoad: (e) => e.currentTarget.classList.remove("blur-sm")
|
|
2087
|
+
}
|
|
2088
|
+
)) : /* @__PURE__ */ React29.createElement("div", { className: `w-10 h-10 shrink-0 rounded-full flex items-center justify-center text-black text-xs ${resolveThemeColor(member.profileColor)}` }, getInitials(member.fullName)), /* @__PURE__ */ React29.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React29.createElement("p", { className: "text-sm text-black truncate pr-2 sm:pr-4" }, member.fullName, " ", member.userId === currentUserId && /* @__PURE__ */ React29.createElement("span", { className: "text-neutral-400 ml-1 sm:ml-2" }, "(You)")), /* @__PURE__ */ React29.createElement("p", { className: "text-xs text-neutral-500 truncate pr-2 sm:pr-4 mt-0.5" }, member.email))),
|
|
2089
|
+
/* @__PURE__ */ React29.createElement("div", { className: `shrink-0 pl-2 sm:pl-4 transition-opacity hidden sm:block ${canManage ? "opacity-0 group-hover:opacity-100" : "opacity-100"}` }, /* @__PURE__ */ React29.createElement("span", { className: "text-[10px] tracking-[0.2em] text-black border border-neutral-200 px-4 py-2 rounded-full bg-white whitespace-nowrap" }, canManage ? "Manage" : "View"))
|
|
2090
|
+
))), /* @__PURE__ */ React29.createElement("div", { className: "flex items-center justify-between p-4 sm:p-5" }, /* @__PURE__ */ React29.createElement("span", { className: "text-[10px] text-neutral-400 tracking-[0.2em]" }, "Page ", currentPage, " of ", totalPages), /* @__PURE__ */ React29.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React29.createElement(
|
|
2091
|
+
"button",
|
|
2092
|
+
{
|
|
2093
|
+
onClick: () => onPageChange(currentPage - 1),
|
|
2094
|
+
disabled: currentPage === 1 || isLoading,
|
|
2095
|
+
className: "p-2 border border-neutral-200 rounded-full bg-white text-neutral-500 hover:text-black hover:border-black disabled:opacity-30 disabled:cursor-not-allowed transition-all outline-none"
|
|
2096
|
+
},
|
|
2097
|
+
/* @__PURE__ */ React29.createElement(HugeiconsIcon15, { icon: ArrowLeft01Icon3, size: 14 })
|
|
2098
|
+
), /* @__PURE__ */ React29.createElement(
|
|
2099
|
+
"button",
|
|
2100
|
+
{
|
|
2101
|
+
onClick: () => onPageChange(currentPage + 1),
|
|
2102
|
+
disabled: currentPage >= totalPages || isLoading,
|
|
2103
|
+
className: "p-2 border border-neutral-200 rounded-full bg-white text-neutral-500 hover:text-black hover:border-black disabled:opacity-30 disabled:cursor-not-allowed transition-all outline-none"
|
|
2104
|
+
},
|
|
2105
|
+
/* @__PURE__ */ React29.createElement(HugeiconsIcon15, { icon: ArrowRight01Icon3, size: 14 })
|
|
2106
|
+
))))), currentView === "details" && selectedMember && /* @__PURE__ */ React29.createElement("div", { className: "w-full max-w-2xl text-left" }, /* @__PURE__ */ React29.createElement("div", { className: "flex flex-col gap-8" }, /* @__PURE__ */ React29.createElement("div", { className: "flex items-center gap-5" }, selectedMember.displayImage && selectedMember.displayImage !== "NO_IMAGE" ? /* @__PURE__ */ React29.createElement("div", { className: "w-16 h-16 shrink-0 rounded-full overflow-hidden bg-neutral-100" }, /* @__PURE__ */ React29.createElement(
|
|
2107
|
+
"img",
|
|
2108
|
+
{
|
|
2109
|
+
src: selectedMember.displayImage,
|
|
2110
|
+
alt: selectedMember.fullName,
|
|
2111
|
+
className: "w-full h-full object-cover blur-sm transition-all duration-300",
|
|
2112
|
+
onLoad: (e) => e.currentTarget.classList.remove("blur-sm")
|
|
2113
|
+
}
|
|
2114
|
+
)) : /* @__PURE__ */ React29.createElement("div", { className: `w-16 h-16 shrink-0 rounded-full flex items-center justify-center text-black text-sm ${resolveThemeColor(selectedMember.profileColor)}` }, getInitials(selectedMember.fullName)), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("h2", { className: "text-lg text-black" }, selectedMember.fullName), /* @__PURE__ */ React29.createElement("p", { className: "text-sm text-neutral-500 mt-1" }, selectedMember.email))), /* @__PURE__ */ React29.createElement("div", { className: "grid grid-cols-2 gap-6 border-t border-neutral-200 pt-8" }, /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("span", { className: "text-[10px] tracking-[0.2em] text-neutral-400 block mb-3" }, "Role"), canChangeRoles && selectedMember.userId !== currentUserId ? /* @__PURE__ */ React29.createElement(
|
|
2115
|
+
"button",
|
|
2116
|
+
{
|
|
2117
|
+
onClick: () => !isUpdatingRole && setIsRoleModalOpen(true),
|
|
2118
|
+
disabled: isUpdatingRole,
|
|
2119
|
+
className: `flex items-center gap-3 text-xs text-black pl-4 pr-3 py-2 border rounded-full transition-colors disabled:opacity-50 outline-none ${isRoleModalOpen ? "bg-neutral-50 border-neutral-300" : "bg-white border-neutral-200 hover:bg-neutral-50"}`
|
|
2120
|
+
},
|
|
2121
|
+
selectedMember.role,
|
|
2122
|
+
isUpdatingRole ? /* @__PURE__ */ React29.createElement(ButtonSpinner3, null) : /* @__PURE__ */ React29.createElement(HugeiconsIcon15, { icon: ArrowDown01Icon2, size: 14, className: "text-neutral-400" })
|
|
2123
|
+
) : /* @__PURE__ */ React29.createElement("span", { className: "text-xs text-black bg-neutral-50 px-4 py-2 rounded-full inline-block" }, selectedMember.role)), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("span", { className: "text-[10px] tracking-[0.2em] text-neutral-400 block mb-3" }, "Joined Date"), /* @__PURE__ */ React29.createElement("span", { className: "text-sm text-black" }, new Date(selectedMember.joinedAt).toLocaleDateString()))), canManage && selectedMember.userId !== currentUserId && /* @__PURE__ */ React29.createElement("div", { className: "pt-8 mt-2 border-t border-neutral-200" }, /* @__PURE__ */ React29.createElement(
|
|
2124
|
+
"button",
|
|
2125
|
+
{
|
|
2126
|
+
onClick: () => setMemberToDelete(selectedMember),
|
|
2127
|
+
className: "flex items-center gap-2 text-[11px] tracking-widest text-red-600 hover:text-red-700 transition-colors w-fit outline-none"
|
|
2128
|
+
},
|
|
2129
|
+
/* @__PURE__ */ React29.createElement(HugeiconsIcon15, { icon: Delete01Icon, size: 14 }),
|
|
2130
|
+
" Remove Member"
|
|
2131
|
+
)))), currentView === "invite" && canManage && /* @__PURE__ */ React29.createElement("div", { className: "w-full max-w-2xl text-left" }, /* @__PURE__ */ React29.createElement("form", { onSubmit: handleInvite, className: "space-y-8", autoComplete: "off" }, /* @__PURE__ */ React29.createElement(
|
|
2132
|
+
TextInput,
|
|
2133
|
+
{
|
|
2134
|
+
label: "Email Address",
|
|
2135
|
+
value: inviteEmail,
|
|
2136
|
+
onChange: (val) => setInviteEmail(val.toLowerCase()),
|
|
2137
|
+
disabled: isInviting,
|
|
2138
|
+
placeholder: "developer@acme.com"
|
|
2139
|
+
}
|
|
2140
|
+
), requireNamesForInvite && /* @__PURE__ */ React29.createElement("div", { className: "flex flex-col sm:flex-row gap-6" }, /* @__PURE__ */ React29.createElement(
|
|
2141
|
+
TextInput,
|
|
2142
|
+
{
|
|
2143
|
+
label: "First Name",
|
|
2144
|
+
value: inviteFirst,
|
|
2145
|
+
onChange: (val) => setInviteFirst(cleanName(val)),
|
|
2146
|
+
disabled: isInviting,
|
|
2147
|
+
placeholder: "Jane"
|
|
2148
|
+
}
|
|
2149
|
+
), /* @__PURE__ */ React29.createElement(
|
|
2150
|
+
TextInput,
|
|
2151
|
+
{
|
|
2152
|
+
label: "Last Name",
|
|
2153
|
+
value: inviteLast,
|
|
2154
|
+
onChange: (val) => setInviteLast(cleanName(val)),
|
|
2155
|
+
disabled: isInviting,
|
|
2156
|
+
placeholder: "Doe"
|
|
2157
|
+
}
|
|
2158
|
+
)), /* @__PURE__ */ React29.createElement("div", { className: "pt-8 mt-2" }, /* @__PURE__ */ React29.createElement(
|
|
2159
|
+
ThreeDActionButton,
|
|
2160
|
+
{
|
|
2161
|
+
type: "submit",
|
|
2162
|
+
disabled: isInviting || !inviteEmail || requireNamesForInvite && (!inviteFirst || !inviteLast),
|
|
2163
|
+
isLoading: isInviting,
|
|
2164
|
+
className: "min-w-40"
|
|
2165
|
+
},
|
|
2166
|
+
"Send Invitation"
|
|
2167
|
+
)))), isRoleModalOpen && canChangeRoles && /* @__PURE__ */ React29.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React29.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => !isUpdatingRole && setIsRoleModalOpen(false) }), /* @__PURE__ */ React29.createElement("div", { ref: dropdownRef, className: "relative w-72 bg-white shadow-2xl rounded-2xl flex flex-col items-center overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React29.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React29.createElement("h3", { className: "text-[14px] text-black tracking-tight" }, "Update Role")), /* @__PURE__ */ React29.createElement("div", { className: "w-full flex flex-col pl-2 pr-2" }, availableRoles.map((roleOption) => /* @__PURE__ */ React29.createElement(
|
|
2168
|
+
"button",
|
|
2169
|
+
{
|
|
2170
|
+
key: roleOption,
|
|
2171
|
+
onClick: () => handleRoleUpdate(roleOption),
|
|
2172
|
+
disabled: isUpdatingRole,
|
|
2173
|
+
className: `text-left px-4 py-3 text-[12px] tracking-wide transition-colors rounded-full flex items-center justify-between outline-none ${selectedMember?.role === roleOption ? "bg-neutral-100 text-black" : "text-neutral-500 hover:bg-neutral-50 hover:text-black"}`
|
|
2174
|
+
},
|
|
2175
|
+
/* @__PURE__ */ React29.createElement("span", { className: "truncate pr-2" }, roleOption),
|
|
2176
|
+
selectedMember?.role === roleOption && /* @__PURE__ */ React29.createElement("div", { className: "w-1.5 h-1.5 rounded-full shrink-0 bg-black" })
|
|
2177
|
+
))), /* @__PURE__ */ React29.createElement("div", { className: "w-full flex" }, /* @__PURE__ */ React29.createElement(
|
|
2178
|
+
"button",
|
|
2179
|
+
{
|
|
2180
|
+
onClick: () => setIsRoleModalOpen(false),
|
|
2181
|
+
disabled: isUpdatingRole,
|
|
2182
|
+
className: "w-full py-2.5 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 outline-none"
|
|
2183
|
+
},
|
|
2184
|
+
"Cancel"
|
|
2185
|
+
)))), memberToDelete && canManage && /* @__PURE__ */ React29.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React29.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => !isDeleting && setMemberToDelete(null) }), /* @__PURE__ */ React29.createElement("div", { className: "relative w-72 bg-white shadow-2xl rounded-2xl flex flex-col items-center overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React29.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React29.createElement("h3", { className: "text-[14px] text-black tracking-tight mb-1" }, "Remove Member"), /* @__PURE__ */ React29.createElement("p", { className: "text-[12px] text-neutral-500 leading-snug mt-2" }, "Are you sure you want to remove this member? They will lose access instantly.")), /* @__PURE__ */ React29.createElement("div", { className: "w-full flex" }, /* @__PURE__ */ React29.createElement("button", { onClick: () => setMemberToDelete(null), disabled: isDeleting, className: "flex-1 py-2 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 outline-none" }, "Cancel"), /* @__PURE__ */ React29.createElement("button", { onClick: handleDelete, disabled: isDeleting, className: "flex-1 py-2 text-[13px] text-red-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 flex justify-center outline-none" }, isDeleting ? /* @__PURE__ */ React29.createElement(ButtonSpinner3, null) : "Remove")))));
|
|
2186
|
+
};
|
|
1430
2187
|
export {
|
|
1431
2188
|
AITranscriptionFeature,
|
|
1432
2189
|
AppBento2,
|
|
@@ -1452,5 +2209,10 @@ export {
|
|
|
1452
2209
|
TextInput,
|
|
1453
2210
|
ThreeDActionButton,
|
|
1454
2211
|
ThreeDButton,
|
|
2212
|
+
UniversalHeader,
|
|
2213
|
+
UniversalIdentityPage,
|
|
2214
|
+
UniversalMembersPage,
|
|
2215
|
+
UniversalOrganizationPage,
|
|
2216
|
+
UniversalSidebar,
|
|
1455
2217
|
ZairusAuth
|
|
1456
2218
|
};
|