@omnibase/shadcn 0.4.3 → 0.5.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.cjs +453 -0
- package/dist/index.d.cts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +451 -0
- package/package.json +19 -17
package/dist/index.cjs
CHANGED
|
@@ -35,9 +35,11 @@ __export(index_exports, {
|
|
|
35
35
|
PricingTable: () => PricingTable,
|
|
36
36
|
RecoveryForm: () => RecoveryForm,
|
|
37
37
|
RegistrationForm: () => RegistrationForm,
|
|
38
|
+
RoleCreator: () => RoleCreator,
|
|
38
39
|
SettingsForm: () => SettingsForm,
|
|
39
40
|
SwitchActiveTenant: () => SwitchActiveTenant,
|
|
40
41
|
TenantCreator: () => TenantCreator,
|
|
42
|
+
UserInvite: () => UserInvite,
|
|
41
43
|
VerificationForm: () => VerificationForm,
|
|
42
44
|
filterInputNodes: () => filterInputNodes,
|
|
43
45
|
findAnchorNode: () => findAnchorNode,
|
|
@@ -1567,15 +1569,466 @@ function TenantCreator({
|
|
|
1567
1569
|
] })
|
|
1568
1570
|
] });
|
|
1569
1571
|
}
|
|
1572
|
+
|
|
1573
|
+
// src/user-invite/index.tsx
|
|
1574
|
+
var import_react2 = require("react");
|
|
1575
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1576
|
+
function UserInvite({ roles, onInvite }) {
|
|
1577
|
+
const [email, setEmail] = (0, import_react2.useState)("");
|
|
1578
|
+
const [selectedRole, setSelectedRole] = (0, import_react2.useState)("");
|
|
1579
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react2.useState)(false);
|
|
1580
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
1581
|
+
const isValidEmail = (email2) => {
|
|
1582
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1583
|
+
return emailRegex.test(email2);
|
|
1584
|
+
};
|
|
1585
|
+
const isFormValid = () => {
|
|
1586
|
+
return email.trim() !== "" && isValidEmail(email) && selectedRole !== "";
|
|
1587
|
+
};
|
|
1588
|
+
const handleSubmit = async (e) => {
|
|
1589
|
+
e.preventDefault();
|
|
1590
|
+
setError(null);
|
|
1591
|
+
if (!isFormValid()) {
|
|
1592
|
+
setError("Please fill in all fields with valid data");
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
setIsSubmitting(true);
|
|
1596
|
+
try {
|
|
1597
|
+
const inviteData = {
|
|
1598
|
+
email: email.trim(),
|
|
1599
|
+
role: selectedRole
|
|
1600
|
+
};
|
|
1601
|
+
await onInvite?.(inviteData);
|
|
1602
|
+
setEmail("");
|
|
1603
|
+
setSelectedRole("");
|
|
1604
|
+
} catch (err) {
|
|
1605
|
+
setError(
|
|
1606
|
+
err instanceof Error ? err.message : "Failed to send invitation"
|
|
1607
|
+
);
|
|
1608
|
+
} finally {
|
|
1609
|
+
setIsSubmitting(false);
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
const handleReset = () => {
|
|
1613
|
+
setEmail("");
|
|
1614
|
+
setSelectedRole("");
|
|
1615
|
+
setError(null);
|
|
1616
|
+
};
|
|
1617
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(Card, { className: "w-full max-w-2xl", children: [
|
|
1618
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CardHeader, { children: [
|
|
1619
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CardTitle, { children: "Invite User" }),
|
|
1620
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CardDescription, { children: "Send an invitation to a new user to join your organization" })
|
|
1621
|
+
] }),
|
|
1622
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CardContent, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-6", children: [
|
|
1623
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "space-y-2", children: [
|
|
1624
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Label, { htmlFor: "email", children: "Email Address" }),
|
|
1625
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1626
|
+
Input,
|
|
1627
|
+
{
|
|
1628
|
+
id: "email",
|
|
1629
|
+
type: "email",
|
|
1630
|
+
placeholder: "colleague@company.com",
|
|
1631
|
+
value: email,
|
|
1632
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1633
|
+
"aria-invalid": email !== "" && !isValidEmail(email)
|
|
1634
|
+
}
|
|
1635
|
+
),
|
|
1636
|
+
email !== "" && !isValidEmail(email) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-xs text-destructive", children: "Please enter a valid email address" })
|
|
1637
|
+
] }),
|
|
1638
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "space-y-2", children: [
|
|
1639
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Label, { htmlFor: "role", children: "Role" }),
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(Select, { value: selectedRole, onValueChange: setSelectedRole, children: [
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SelectTrigger, { id: "role", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SelectValue, { placeholder: "Select a role" }) }),
|
|
1642
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SelectContent, { children: roles.length > 0 ? roles.map((role) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SelectItem, { value: role, children: role }, role)) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SelectItem, { value: "no-roles", disabled: true, children: "No roles available" }) })
|
|
1643
|
+
] }),
|
|
1644
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-xs text-muted-foreground", children: "The role determines what permissions the user will have" })
|
|
1645
|
+
] }),
|
|
1646
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "p-3 rounded-md bg-destructive/10 border border-destructive/20", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm text-destructive", children: error }) }),
|
|
1647
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex justify-end space-x-3", children: [
|
|
1648
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1649
|
+
Button,
|
|
1650
|
+
{
|
|
1651
|
+
type: "button",
|
|
1652
|
+
variant: "outline",
|
|
1653
|
+
onClick: handleReset,
|
|
1654
|
+
disabled: isSubmitting,
|
|
1655
|
+
children: "Reset"
|
|
1656
|
+
}
|
|
1657
|
+
),
|
|
1658
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Button, { type: "submit", disabled: !isFormValid() || isSubmitting, children: isSubmitting ? "Sending..." : "Send Invitation" })
|
|
1659
|
+
] })
|
|
1660
|
+
] }) })
|
|
1661
|
+
] });
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
// src/role-creator/index.tsx
|
|
1665
|
+
var import_react3 = require("react");
|
|
1666
|
+
|
|
1667
|
+
// src/components/ui/checkbox.tsx
|
|
1668
|
+
var React14 = require("react");
|
|
1669
|
+
var CheckboxPrimitive = __toESM(require("@radix-ui/react-checkbox"), 1);
|
|
1670
|
+
var import_lucide_react3 = require("lucide-react");
|
|
1671
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1672
|
+
function Checkbox({
|
|
1673
|
+
className,
|
|
1674
|
+
...props
|
|
1675
|
+
}) {
|
|
1676
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1677
|
+
CheckboxPrimitive.Root,
|
|
1678
|
+
{
|
|
1679
|
+
"data-slot": "checkbox",
|
|
1680
|
+
className: cn(
|
|
1681
|
+
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1682
|
+
className
|
|
1683
|
+
),
|
|
1684
|
+
...props,
|
|
1685
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1686
|
+
CheckboxPrimitive.Indicator,
|
|
1687
|
+
{
|
|
1688
|
+
"data-slot": "checkbox-indicator",
|
|
1689
|
+
className: "grid place-content-center text-current transition-none",
|
|
1690
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react3.CheckIcon, { className: "size-3.5" })
|
|
1691
|
+
}
|
|
1692
|
+
)
|
|
1693
|
+
}
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// src/components/ui/separator.tsx
|
|
1698
|
+
var React15 = require("react");
|
|
1699
|
+
var SeparatorPrimitive = __toESM(require("@radix-ui/react-separator"), 1);
|
|
1700
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1701
|
+
function Separator2({
|
|
1702
|
+
className,
|
|
1703
|
+
orientation = "horizontal",
|
|
1704
|
+
decorative = true,
|
|
1705
|
+
...props
|
|
1706
|
+
}) {
|
|
1707
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1708
|
+
SeparatorPrimitive.Root,
|
|
1709
|
+
{
|
|
1710
|
+
"data-slot": "separator",
|
|
1711
|
+
decorative,
|
|
1712
|
+
orientation,
|
|
1713
|
+
className: cn(
|
|
1714
|
+
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
1715
|
+
className
|
|
1716
|
+
),
|
|
1717
|
+
...props
|
|
1718
|
+
}
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
// src/role-creator/index.tsx
|
|
1723
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1724
|
+
function RoleCreator({
|
|
1725
|
+
definitions,
|
|
1726
|
+
roles,
|
|
1727
|
+
namespaceMap = {},
|
|
1728
|
+
onRoleCreate,
|
|
1729
|
+
onRoleUpdate
|
|
1730
|
+
}) {
|
|
1731
|
+
const [roleName, setRoleName] = (0, import_react3.useState)("");
|
|
1732
|
+
const [selectedPermissions, setSelectedPermissions] = (0, import_react3.useState)(
|
|
1733
|
+
/* @__PURE__ */ new Set()
|
|
1734
|
+
);
|
|
1735
|
+
const [showSuggestions, setShowSuggestions] = (0, import_react3.useState)(false);
|
|
1736
|
+
const [isEditMode, setIsEditMode] = (0, import_react3.useState)(false);
|
|
1737
|
+
const [editingRoleId, setEditingRoleId] = (0, import_react3.useState)(null);
|
|
1738
|
+
const { tenantNamespace, fineGrainedNamespaces } = (0, import_react3.useMemo)(() => {
|
|
1739
|
+
const tenant = definitions.find(
|
|
1740
|
+
(def) => def.namespace.toLowerCase() === "tenant"
|
|
1741
|
+
);
|
|
1742
|
+
const fineGrained = definitions.filter(
|
|
1743
|
+
(def) => def.namespace.toLowerCase() !== "tenant"
|
|
1744
|
+
);
|
|
1745
|
+
return {
|
|
1746
|
+
tenantNamespace: tenant,
|
|
1747
|
+
fineGrainedNamespaces: fineGrained
|
|
1748
|
+
};
|
|
1749
|
+
}, [definitions]);
|
|
1750
|
+
const roleSuggestions = (0, import_react3.useMemo)(() => {
|
|
1751
|
+
return roles.map((role) => role.role_name);
|
|
1752
|
+
}, [roles]);
|
|
1753
|
+
const handleRoleNameChange = (value) => {
|
|
1754
|
+
setRoleName(value);
|
|
1755
|
+
const existingRole = roles.find(
|
|
1756
|
+
(role) => role.role_name.toLowerCase() === value.toLowerCase()
|
|
1757
|
+
);
|
|
1758
|
+
if (existingRole) {
|
|
1759
|
+
setIsEditMode(true);
|
|
1760
|
+
setEditingRoleId(existingRole.id);
|
|
1761
|
+
const permissions = /* @__PURE__ */ new Set();
|
|
1762
|
+
existingRole.permissions.forEach((perm) => {
|
|
1763
|
+
permissions.add(perm);
|
|
1764
|
+
});
|
|
1765
|
+
setSelectedPermissions(permissions);
|
|
1766
|
+
} else {
|
|
1767
|
+
setIsEditMode(false);
|
|
1768
|
+
setEditingRoleId(null);
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
const buildPermissionString = (namespace, relation, resourceId) => {
|
|
1772
|
+
if (resourceId) {
|
|
1773
|
+
return `${namespace.toLowerCase()}:${resourceId}#${relation}`;
|
|
1774
|
+
}
|
|
1775
|
+
return `${namespace.toLowerCase()}#${relation}`;
|
|
1776
|
+
};
|
|
1777
|
+
const togglePermission = (permissionString) => {
|
|
1778
|
+
const newPermissions = new Set(selectedPermissions);
|
|
1779
|
+
if (newPermissions.has(permissionString)) {
|
|
1780
|
+
newPermissions.delete(permissionString);
|
|
1781
|
+
} else {
|
|
1782
|
+
newPermissions.add(permissionString);
|
|
1783
|
+
}
|
|
1784
|
+
setSelectedPermissions(newPermissions);
|
|
1785
|
+
};
|
|
1786
|
+
const handleSubmit = () => {
|
|
1787
|
+
if (!roleName.trim()) return;
|
|
1788
|
+
const permissionsArray = Array.from(selectedPermissions);
|
|
1789
|
+
if (isEditMode && editingRoleId) {
|
|
1790
|
+
onRoleUpdate?.({
|
|
1791
|
+
role_id: editingRoleId,
|
|
1792
|
+
role_name: roleName,
|
|
1793
|
+
permissions: permissionsArray
|
|
1794
|
+
});
|
|
1795
|
+
} else {
|
|
1796
|
+
onRoleCreate?.({
|
|
1797
|
+
role_name: roleName,
|
|
1798
|
+
permissions: permissionsArray
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
};
|
|
1802
|
+
const handleReset = () => {
|
|
1803
|
+
setRoleName("");
|
|
1804
|
+
setSelectedPermissions(/* @__PURE__ */ new Set());
|
|
1805
|
+
setIsEditMode(false);
|
|
1806
|
+
setEditingRoleId(null);
|
|
1807
|
+
};
|
|
1808
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Card, { className: "w-full max-w-4xl", children: [
|
|
1809
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CardHeader, { children: [
|
|
1810
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(CardTitle, { children: isEditMode ? `Edit Role: ${roleName}` : "Create New Role" }),
|
|
1811
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(CardDescription, { children: isEditMode ? "Update permissions for this existing role" : "Define a new role with specific permissions" })
|
|
1812
|
+
] }),
|
|
1813
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CardContent, { className: "space-y-6", children: [
|
|
1814
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "space-y-2 relative", children: [
|
|
1815
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Label, { htmlFor: "role-name", children: "Role Name" }),
|
|
1816
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1817
|
+
Input,
|
|
1818
|
+
{
|
|
1819
|
+
id: "role-name",
|
|
1820
|
+
placeholder: "Enter role name (e.g., admin, developer, viewer)",
|
|
1821
|
+
value: roleName,
|
|
1822
|
+
onChange: (e) => handleRoleNameChange(e.target.value),
|
|
1823
|
+
onFocus: () => setShowSuggestions(true),
|
|
1824
|
+
onBlur: () => setTimeout(() => setShowSuggestions(false), 200)
|
|
1825
|
+
}
|
|
1826
|
+
),
|
|
1827
|
+
showSuggestions && roleSuggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "absolute z-10 w-full mt-1 bg-background border rounded-md shadow-lg max-h-48 overflow-y-auto", children: roleSuggestions.filter(
|
|
1828
|
+
(suggestion) => suggestion.toLowerCase().includes(roleName.toLowerCase())
|
|
1829
|
+
).map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1830
|
+
"button",
|
|
1831
|
+
{
|
|
1832
|
+
className: "w-full px-4 py-2 text-left hover:bg-accent hover:text-accent-foreground text-sm",
|
|
1833
|
+
onMouseDown: () => {
|
|
1834
|
+
handleRoleNameChange(suggestion);
|
|
1835
|
+
setShowSuggestions(false);
|
|
1836
|
+
},
|
|
1837
|
+
children: suggestion
|
|
1838
|
+
},
|
|
1839
|
+
suggestion
|
|
1840
|
+
)) }),
|
|
1841
|
+
isEditMode && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-xs text-amber-600 dark:text-amber-400", children: "\u26A0 Editing existing role - changes will update all users with this role" })
|
|
1842
|
+
] }),
|
|
1843
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Separator2, {}),
|
|
1844
|
+
tenantNamespace && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "space-y-4", children: [
|
|
1845
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { children: [
|
|
1846
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h3", { className: "text-lg font-semibold", children: "Organization Permissions" }),
|
|
1847
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-muted-foreground", children: "Tenant-wide permissions that apply across the entire organization" })
|
|
1848
|
+
] }),
|
|
1849
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 pl-4", children: tenantNamespace.relations.map((relation) => {
|
|
1850
|
+
const permissionString = buildPermissionString(
|
|
1851
|
+
tenantNamespace.namespace,
|
|
1852
|
+
relation
|
|
1853
|
+
);
|
|
1854
|
+
const isChecked = selectedPermissions.has(permissionString);
|
|
1855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1856
|
+
"div",
|
|
1857
|
+
{
|
|
1858
|
+
className: "flex items-center space-x-2",
|
|
1859
|
+
children: [
|
|
1860
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1861
|
+
Checkbox,
|
|
1862
|
+
{
|
|
1863
|
+
id: permissionString,
|
|
1864
|
+
checked: isChecked,
|
|
1865
|
+
onCheckedChange: () => togglePermission(permissionString)
|
|
1866
|
+
}
|
|
1867
|
+
),
|
|
1868
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1869
|
+
Label,
|
|
1870
|
+
{
|
|
1871
|
+
htmlFor: permissionString,
|
|
1872
|
+
className: "text-sm font-normal cursor-pointer",
|
|
1873
|
+
children: relation.replace(/_/g, " ")
|
|
1874
|
+
}
|
|
1875
|
+
)
|
|
1876
|
+
]
|
|
1877
|
+
},
|
|
1878
|
+
permissionString
|
|
1879
|
+
);
|
|
1880
|
+
}) })
|
|
1881
|
+
] }),
|
|
1882
|
+
fineGrainedNamespaces.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
1883
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Separator2, {}),
|
|
1884
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "space-y-6", children: [
|
|
1885
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { children: [
|
|
1886
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h3", { className: "text-lg font-semibold", children: "Fine-Grained Permissions" }),
|
|
1887
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-muted-foreground", children: "Resource-specific permissions that require an object ID" })
|
|
1888
|
+
] }),
|
|
1889
|
+
fineGrainedNamespaces.map((namespace) => {
|
|
1890
|
+
const namespaceLower = namespace.namespace.toLowerCase();
|
|
1891
|
+
const resourceMap = namespaceMap[namespaceLower] || [];
|
|
1892
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "space-y-4", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "pl-4", children: [
|
|
1893
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h4", { className: "text-md font-medium capitalize", children: namespace.namespace }),
|
|
1894
|
+
resourceMap.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "mt-4 space-y-6", children: resourceMap.map((resource) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1895
|
+
"div",
|
|
1896
|
+
{
|
|
1897
|
+
className: "border rounded-lg p-4 space-y-3 bg-muted/30",
|
|
1898
|
+
children: [
|
|
1899
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1900
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "font-medium text-sm", children: resource.label }),
|
|
1901
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-xs text-muted-foreground font-mono", children: resource.id })
|
|
1902
|
+
] }),
|
|
1903
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: namespace.relations.map((relation) => {
|
|
1904
|
+
const permissionString = buildPermissionString(
|
|
1905
|
+
namespace.namespace,
|
|
1906
|
+
relation,
|
|
1907
|
+
resource.id
|
|
1908
|
+
);
|
|
1909
|
+
const isChecked = selectedPermissions.has(permissionString);
|
|
1910
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1911
|
+
"div",
|
|
1912
|
+
{
|
|
1913
|
+
className: "flex items-center space-x-2",
|
|
1914
|
+
children: [
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1916
|
+
Checkbox,
|
|
1917
|
+
{
|
|
1918
|
+
id: permissionString,
|
|
1919
|
+
checked: isChecked,
|
|
1920
|
+
onCheckedChange: () => togglePermission(permissionString)
|
|
1921
|
+
}
|
|
1922
|
+
),
|
|
1923
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1924
|
+
Label,
|
|
1925
|
+
{
|
|
1926
|
+
htmlFor: permissionString,
|
|
1927
|
+
className: "text-sm font-normal cursor-pointer",
|
|
1928
|
+
children: relation.replace(/_/g, " ")
|
|
1929
|
+
}
|
|
1930
|
+
)
|
|
1931
|
+
]
|
|
1932
|
+
},
|
|
1933
|
+
permissionString
|
|
1934
|
+
);
|
|
1935
|
+
}) })
|
|
1936
|
+
]
|
|
1937
|
+
},
|
|
1938
|
+
resource.id
|
|
1939
|
+
)) }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "mt-3 p-4 border-2 border-dashed rounded-lg text-center", children: [
|
|
1940
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1941
|
+
"No ",
|
|
1942
|
+
namespace.namespace.toLowerCase(),
|
|
1943
|
+
" resources available"
|
|
1944
|
+
] }),
|
|
1945
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("p", { className: "text-xs text-muted-foreground mt-1", children: [
|
|
1946
|
+
"Use wildcard permissions (e.g., ",
|
|
1947
|
+
namespaceLower,
|
|
1948
|
+
":*#permission) for all resources"
|
|
1949
|
+
] })
|
|
1950
|
+
] }),
|
|
1951
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "mt-4 space-y-2", children: [
|
|
1952
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Label, { className: "text-sm font-medium", children: [
|
|
1953
|
+
"Wildcard Permissions (All ",
|
|
1954
|
+
namespace.namespace,
|
|
1955
|
+
"s)"
|
|
1956
|
+
] }),
|
|
1957
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: namespace.relations.map((relation) => {
|
|
1958
|
+
const permissionString = buildPermissionString(
|
|
1959
|
+
namespace.namespace,
|
|
1960
|
+
relation,
|
|
1961
|
+
"*"
|
|
1962
|
+
);
|
|
1963
|
+
const isChecked = selectedPermissions.has(permissionString);
|
|
1964
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1965
|
+
"div",
|
|
1966
|
+
{
|
|
1967
|
+
className: "flex items-center space-x-2",
|
|
1968
|
+
children: [
|
|
1969
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1970
|
+
Checkbox,
|
|
1971
|
+
{
|
|
1972
|
+
id: permissionString,
|
|
1973
|
+
checked: isChecked,
|
|
1974
|
+
onCheckedChange: () => togglePermission(permissionString)
|
|
1975
|
+
}
|
|
1976
|
+
),
|
|
1977
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1978
|
+
Label,
|
|
1979
|
+
{
|
|
1980
|
+
htmlFor: permissionString,
|
|
1981
|
+
className: "text-sm font-normal cursor-pointer",
|
|
1982
|
+
children: [
|
|
1983
|
+
relation.replace(/_/g, " "),
|
|
1984
|
+
" (all)"
|
|
1985
|
+
]
|
|
1986
|
+
}
|
|
1987
|
+
)
|
|
1988
|
+
]
|
|
1989
|
+
},
|
|
1990
|
+
permissionString
|
|
1991
|
+
);
|
|
1992
|
+
}) })
|
|
1993
|
+
] })
|
|
1994
|
+
] }) }, namespace.id);
|
|
1995
|
+
})
|
|
1996
|
+
] })
|
|
1997
|
+
] }),
|
|
1998
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Separator2, {}),
|
|
1999
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "space-y-2", children: [
|
|
2000
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Label, { className: "text-sm font-medium", children: [
|
|
2001
|
+
"Selected Permissions (",
|
|
2002
|
+
selectedPermissions.size,
|
|
2003
|
+
")"
|
|
2004
|
+
] }),
|
|
2005
|
+
selectedPermissions.size > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "p-4 bg-muted rounded-md max-h-40 overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("ul", { className: "space-y-1 text-xs font-mono", children: Array.from(selectedPermissions).map((perm) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("li", { className: "text-muted-foreground", children: perm }, perm)) }) }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-muted-foreground italic", children: "No permissions selected" })
|
|
2006
|
+
] }),
|
|
2007
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex justify-end space-x-3", children: [
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Button, { variant: "outline", onClick: handleReset, children: "Reset" }),
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2010
|
+
Button,
|
|
2011
|
+
{
|
|
2012
|
+
onClick: handleSubmit,
|
|
2013
|
+
disabled: !roleName.trim() || selectedPermissions.size === 0,
|
|
2014
|
+
children: isEditMode ? "Update Role" : "Create Role"
|
|
2015
|
+
}
|
|
2016
|
+
)
|
|
2017
|
+
] })
|
|
2018
|
+
] })
|
|
2019
|
+
] });
|
|
2020
|
+
}
|
|
1570
2021
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1571
2022
|
0 && (module.exports = {
|
|
1572
2023
|
LoginForm,
|
|
1573
2024
|
PricingTable,
|
|
1574
2025
|
RecoveryForm,
|
|
1575
2026
|
RegistrationForm,
|
|
2027
|
+
RoleCreator,
|
|
1576
2028
|
SettingsForm,
|
|
1577
2029
|
SwitchActiveTenant,
|
|
1578
2030
|
TenantCreator,
|
|
2031
|
+
UserInvite,
|
|
1579
2032
|
VerificationForm,
|
|
1580
2033
|
filterInputNodes,
|
|
1581
2034
|
findAnchorNode,
|
package/dist/index.d.cts
CHANGED
|
@@ -3,6 +3,7 @@ import { LoginFlow, RegistrationFlow, VerificationFlow, RecoveryFlow, SettingsFl
|
|
|
3
3
|
import * as React$1 from 'react';
|
|
4
4
|
import { Tenant } from '@omnibase/core-js/tenants';
|
|
5
5
|
import { Product } from '@omnibase/core-js/payments';
|
|
6
|
+
import { NamespaceDefinition, Role } from '@omnibase/core-js';
|
|
6
7
|
|
|
7
8
|
type LoginFormProps = {
|
|
8
9
|
flow: LoginFlow;
|
|
@@ -126,4 +127,34 @@ interface TenantCreatorProps {
|
|
|
126
127
|
}
|
|
127
128
|
declare function TenantCreator({ config, formActions, className, }: TenantCreatorProps): react_jsx_runtime.JSX.Element;
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
interface UserInviteData {
|
|
131
|
+
email: string;
|
|
132
|
+
role: string;
|
|
133
|
+
}
|
|
134
|
+
interface UserInviteProps {
|
|
135
|
+
roles: string[];
|
|
136
|
+
onInvite?: (inviteData: UserInviteData) => void | Promise<void>;
|
|
137
|
+
}
|
|
138
|
+
declare function UserInvite({ roles, onInvite }: UserInviteProps): react_jsx_runtime.JSX.Element;
|
|
139
|
+
|
|
140
|
+
interface NamespaceMapEntry {
|
|
141
|
+
id: string;
|
|
142
|
+
label: string;
|
|
143
|
+
}
|
|
144
|
+
interface RoleCreatorProps {
|
|
145
|
+
definitions: NamespaceDefinition[];
|
|
146
|
+
roles: Role[];
|
|
147
|
+
namespaceMap?: Record<string, NamespaceMapEntry[]>;
|
|
148
|
+
onRoleCreate?: (roleData: {
|
|
149
|
+
role_name: string;
|
|
150
|
+
permissions: string[];
|
|
151
|
+
}) => void;
|
|
152
|
+
onRoleUpdate?: (roleData: {
|
|
153
|
+
role_id: string;
|
|
154
|
+
role_name: string;
|
|
155
|
+
permissions: string[];
|
|
156
|
+
}) => void;
|
|
157
|
+
}
|
|
158
|
+
declare function RoleCreator({ definitions, roles, namespaceMap, onRoleCreate, onRoleUpdate, }: RoleCreatorProps): react_jsx_runtime.JSX.Element;
|
|
159
|
+
|
|
160
|
+
export { type CustomFormProps, type FlowType, LoginForm, type LoginFormProps, type NodesByGroup, PricingTable, type PricingTableProps, RecoveryForm, type RecoveryFormProps, RegistrationForm, type RegistrationFormProps, RoleCreator, SettingsForm, type SettingsFormProps, SwitchActiveTenant, type SwitchActiveTenantProps, TenantCreator, type TenantCreatorConfig, type TenantCreatorFormActions, type TenantCreatorProps, UserInvite, VerificationForm, type VerificationFormProps, filterInputNodes, findAnchorNode, findCsrfToken, findSubmitButton, groupNodesByGroup, isUiNodeInputAttributes, sortNodes };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { LoginFlow, RegistrationFlow, VerificationFlow, RecoveryFlow, SettingsFl
|
|
|
3
3
|
import * as React$1 from 'react';
|
|
4
4
|
import { Tenant } from '@omnibase/core-js/tenants';
|
|
5
5
|
import { Product } from '@omnibase/core-js/payments';
|
|
6
|
+
import { NamespaceDefinition, Role } from '@omnibase/core-js';
|
|
6
7
|
|
|
7
8
|
type LoginFormProps = {
|
|
8
9
|
flow: LoginFlow;
|
|
@@ -126,4 +127,34 @@ interface TenantCreatorProps {
|
|
|
126
127
|
}
|
|
127
128
|
declare function TenantCreator({ config, formActions, className, }: TenantCreatorProps): react_jsx_runtime.JSX.Element;
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
interface UserInviteData {
|
|
131
|
+
email: string;
|
|
132
|
+
role: string;
|
|
133
|
+
}
|
|
134
|
+
interface UserInviteProps {
|
|
135
|
+
roles: string[];
|
|
136
|
+
onInvite?: (inviteData: UserInviteData) => void | Promise<void>;
|
|
137
|
+
}
|
|
138
|
+
declare function UserInvite({ roles, onInvite }: UserInviteProps): react_jsx_runtime.JSX.Element;
|
|
139
|
+
|
|
140
|
+
interface NamespaceMapEntry {
|
|
141
|
+
id: string;
|
|
142
|
+
label: string;
|
|
143
|
+
}
|
|
144
|
+
interface RoleCreatorProps {
|
|
145
|
+
definitions: NamespaceDefinition[];
|
|
146
|
+
roles: Role[];
|
|
147
|
+
namespaceMap?: Record<string, NamespaceMapEntry[]>;
|
|
148
|
+
onRoleCreate?: (roleData: {
|
|
149
|
+
role_name: string;
|
|
150
|
+
permissions: string[];
|
|
151
|
+
}) => void;
|
|
152
|
+
onRoleUpdate?: (roleData: {
|
|
153
|
+
role_id: string;
|
|
154
|
+
role_name: string;
|
|
155
|
+
permissions: string[];
|
|
156
|
+
}) => void;
|
|
157
|
+
}
|
|
158
|
+
declare function RoleCreator({ definitions, roles, namespaceMap, onRoleCreate, onRoleUpdate, }: RoleCreatorProps): react_jsx_runtime.JSX.Element;
|
|
159
|
+
|
|
160
|
+
export { type CustomFormProps, type FlowType, LoginForm, type LoginFormProps, type NodesByGroup, PricingTable, type PricingTableProps, RecoveryForm, type RecoveryFormProps, RegistrationForm, type RegistrationFormProps, RoleCreator, SettingsForm, type SettingsFormProps, SwitchActiveTenant, type SwitchActiveTenantProps, TenantCreator, type TenantCreatorConfig, type TenantCreatorFormActions, type TenantCreatorProps, UserInvite, VerificationForm, type VerificationFormProps, filterInputNodes, findAnchorNode, findCsrfToken, findSubmitButton, groupNodesByGroup, isUiNodeInputAttributes, sortNodes };
|
package/dist/index.js
CHANGED
|
@@ -1518,14 +1518,465 @@ function TenantCreator({
|
|
|
1518
1518
|
] })
|
|
1519
1519
|
] });
|
|
1520
1520
|
}
|
|
1521
|
+
|
|
1522
|
+
// src/user-invite/index.tsx
|
|
1523
|
+
import { useState as useState5 } from "react";
|
|
1524
|
+
import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1525
|
+
function UserInvite({ roles, onInvite }) {
|
|
1526
|
+
const [email, setEmail] = useState5("");
|
|
1527
|
+
const [selectedRole, setSelectedRole] = useState5("");
|
|
1528
|
+
const [isSubmitting, setIsSubmitting] = useState5(false);
|
|
1529
|
+
const [error, setError] = useState5(null);
|
|
1530
|
+
const isValidEmail = (email2) => {
|
|
1531
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1532
|
+
return emailRegex.test(email2);
|
|
1533
|
+
};
|
|
1534
|
+
const isFormValid = () => {
|
|
1535
|
+
return email.trim() !== "" && isValidEmail(email) && selectedRole !== "";
|
|
1536
|
+
};
|
|
1537
|
+
const handleSubmit = async (e) => {
|
|
1538
|
+
e.preventDefault();
|
|
1539
|
+
setError(null);
|
|
1540
|
+
if (!isFormValid()) {
|
|
1541
|
+
setError("Please fill in all fields with valid data");
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
setIsSubmitting(true);
|
|
1545
|
+
try {
|
|
1546
|
+
const inviteData = {
|
|
1547
|
+
email: email.trim(),
|
|
1548
|
+
role: selectedRole
|
|
1549
|
+
};
|
|
1550
|
+
await onInvite?.(inviteData);
|
|
1551
|
+
setEmail("");
|
|
1552
|
+
setSelectedRole("");
|
|
1553
|
+
} catch (err) {
|
|
1554
|
+
setError(
|
|
1555
|
+
err instanceof Error ? err.message : "Failed to send invitation"
|
|
1556
|
+
);
|
|
1557
|
+
} finally {
|
|
1558
|
+
setIsSubmitting(false);
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1561
|
+
const handleReset = () => {
|
|
1562
|
+
setEmail("");
|
|
1563
|
+
setSelectedRole("");
|
|
1564
|
+
setError(null);
|
|
1565
|
+
};
|
|
1566
|
+
return /* @__PURE__ */ jsxs14(Card, { className: "w-full max-w-2xl", children: [
|
|
1567
|
+
/* @__PURE__ */ jsxs14(CardHeader, { children: [
|
|
1568
|
+
/* @__PURE__ */ jsx23(CardTitle, { children: "Invite User" }),
|
|
1569
|
+
/* @__PURE__ */ jsx23(CardDescription, { children: "Send an invitation to a new user to join your organization" })
|
|
1570
|
+
] }),
|
|
1571
|
+
/* @__PURE__ */ jsx23(CardContent, { children: /* @__PURE__ */ jsxs14("form", { onSubmit: handleSubmit, className: "space-y-6", children: [
|
|
1572
|
+
/* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
|
|
1573
|
+
/* @__PURE__ */ jsx23(Label, { htmlFor: "email", children: "Email Address" }),
|
|
1574
|
+
/* @__PURE__ */ jsx23(
|
|
1575
|
+
Input,
|
|
1576
|
+
{
|
|
1577
|
+
id: "email",
|
|
1578
|
+
type: "email",
|
|
1579
|
+
placeholder: "colleague@company.com",
|
|
1580
|
+
value: email,
|
|
1581
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1582
|
+
"aria-invalid": email !== "" && !isValidEmail(email)
|
|
1583
|
+
}
|
|
1584
|
+
),
|
|
1585
|
+
email !== "" && !isValidEmail(email) && /* @__PURE__ */ jsx23("p", { className: "text-xs text-destructive", children: "Please enter a valid email address" })
|
|
1586
|
+
] }),
|
|
1587
|
+
/* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
|
|
1588
|
+
/* @__PURE__ */ jsx23(Label, { htmlFor: "role", children: "Role" }),
|
|
1589
|
+
/* @__PURE__ */ jsxs14(Select, { value: selectedRole, onValueChange: setSelectedRole, children: [
|
|
1590
|
+
/* @__PURE__ */ jsx23(SelectTrigger, { id: "role", children: /* @__PURE__ */ jsx23(SelectValue, { placeholder: "Select a role" }) }),
|
|
1591
|
+
/* @__PURE__ */ jsx23(SelectContent, { children: roles.length > 0 ? roles.map((role) => /* @__PURE__ */ jsx23(SelectItem, { value: role, children: role }, role)) : /* @__PURE__ */ jsx23(SelectItem, { value: "no-roles", disabled: true, children: "No roles available" }) })
|
|
1592
|
+
] }),
|
|
1593
|
+
/* @__PURE__ */ jsx23("p", { className: "text-xs text-muted-foreground", children: "The role determines what permissions the user will have" })
|
|
1594
|
+
] }),
|
|
1595
|
+
error && /* @__PURE__ */ jsx23("div", { className: "p-3 rounded-md bg-destructive/10 border border-destructive/20", children: /* @__PURE__ */ jsx23("p", { className: "text-sm text-destructive", children: error }) }),
|
|
1596
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex justify-end space-x-3", children: [
|
|
1597
|
+
/* @__PURE__ */ jsx23(
|
|
1598
|
+
Button,
|
|
1599
|
+
{
|
|
1600
|
+
type: "button",
|
|
1601
|
+
variant: "outline",
|
|
1602
|
+
onClick: handleReset,
|
|
1603
|
+
disabled: isSubmitting,
|
|
1604
|
+
children: "Reset"
|
|
1605
|
+
}
|
|
1606
|
+
),
|
|
1607
|
+
/* @__PURE__ */ jsx23(Button, { type: "submit", disabled: !isFormValid() || isSubmitting, children: isSubmitting ? "Sending..." : "Send Invitation" })
|
|
1608
|
+
] })
|
|
1609
|
+
] }) })
|
|
1610
|
+
] });
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
// src/role-creator/index.tsx
|
|
1614
|
+
import { useState as useState6, useMemo as useMemo2 } from "react";
|
|
1615
|
+
|
|
1616
|
+
// src/components/ui/checkbox.tsx
|
|
1617
|
+
import "react";
|
|
1618
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
1619
|
+
import { CheckIcon } from "lucide-react";
|
|
1620
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
1621
|
+
function Checkbox({
|
|
1622
|
+
className,
|
|
1623
|
+
...props
|
|
1624
|
+
}) {
|
|
1625
|
+
return /* @__PURE__ */ jsx24(
|
|
1626
|
+
CheckboxPrimitive.Root,
|
|
1627
|
+
{
|
|
1628
|
+
"data-slot": "checkbox",
|
|
1629
|
+
className: cn(
|
|
1630
|
+
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1631
|
+
className
|
|
1632
|
+
),
|
|
1633
|
+
...props,
|
|
1634
|
+
children: /* @__PURE__ */ jsx24(
|
|
1635
|
+
CheckboxPrimitive.Indicator,
|
|
1636
|
+
{
|
|
1637
|
+
"data-slot": "checkbox-indicator",
|
|
1638
|
+
className: "grid place-content-center text-current transition-none",
|
|
1639
|
+
children: /* @__PURE__ */ jsx24(CheckIcon, { className: "size-3.5" })
|
|
1640
|
+
}
|
|
1641
|
+
)
|
|
1642
|
+
}
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// src/components/ui/separator.tsx
|
|
1647
|
+
import "react";
|
|
1648
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
1649
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
1650
|
+
function Separator2({
|
|
1651
|
+
className,
|
|
1652
|
+
orientation = "horizontal",
|
|
1653
|
+
decorative = true,
|
|
1654
|
+
...props
|
|
1655
|
+
}) {
|
|
1656
|
+
return /* @__PURE__ */ jsx25(
|
|
1657
|
+
SeparatorPrimitive.Root,
|
|
1658
|
+
{
|
|
1659
|
+
"data-slot": "separator",
|
|
1660
|
+
decorative,
|
|
1661
|
+
orientation,
|
|
1662
|
+
className: cn(
|
|
1663
|
+
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
1664
|
+
className
|
|
1665
|
+
),
|
|
1666
|
+
...props
|
|
1667
|
+
}
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
// src/role-creator/index.tsx
|
|
1672
|
+
import { Fragment as Fragment2, jsx as jsx26, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1673
|
+
function RoleCreator({
|
|
1674
|
+
definitions,
|
|
1675
|
+
roles,
|
|
1676
|
+
namespaceMap = {},
|
|
1677
|
+
onRoleCreate,
|
|
1678
|
+
onRoleUpdate
|
|
1679
|
+
}) {
|
|
1680
|
+
const [roleName, setRoleName] = useState6("");
|
|
1681
|
+
const [selectedPermissions, setSelectedPermissions] = useState6(
|
|
1682
|
+
/* @__PURE__ */ new Set()
|
|
1683
|
+
);
|
|
1684
|
+
const [showSuggestions, setShowSuggestions] = useState6(false);
|
|
1685
|
+
const [isEditMode, setIsEditMode] = useState6(false);
|
|
1686
|
+
const [editingRoleId, setEditingRoleId] = useState6(null);
|
|
1687
|
+
const { tenantNamespace, fineGrainedNamespaces } = useMemo2(() => {
|
|
1688
|
+
const tenant = definitions.find(
|
|
1689
|
+
(def) => def.namespace.toLowerCase() === "tenant"
|
|
1690
|
+
);
|
|
1691
|
+
const fineGrained = definitions.filter(
|
|
1692
|
+
(def) => def.namespace.toLowerCase() !== "tenant"
|
|
1693
|
+
);
|
|
1694
|
+
return {
|
|
1695
|
+
tenantNamespace: tenant,
|
|
1696
|
+
fineGrainedNamespaces: fineGrained
|
|
1697
|
+
};
|
|
1698
|
+
}, [definitions]);
|
|
1699
|
+
const roleSuggestions = useMemo2(() => {
|
|
1700
|
+
return roles.map((role) => role.role_name);
|
|
1701
|
+
}, [roles]);
|
|
1702
|
+
const handleRoleNameChange = (value) => {
|
|
1703
|
+
setRoleName(value);
|
|
1704
|
+
const existingRole = roles.find(
|
|
1705
|
+
(role) => role.role_name.toLowerCase() === value.toLowerCase()
|
|
1706
|
+
);
|
|
1707
|
+
if (existingRole) {
|
|
1708
|
+
setIsEditMode(true);
|
|
1709
|
+
setEditingRoleId(existingRole.id);
|
|
1710
|
+
const permissions = /* @__PURE__ */ new Set();
|
|
1711
|
+
existingRole.permissions.forEach((perm) => {
|
|
1712
|
+
permissions.add(perm);
|
|
1713
|
+
});
|
|
1714
|
+
setSelectedPermissions(permissions);
|
|
1715
|
+
} else {
|
|
1716
|
+
setIsEditMode(false);
|
|
1717
|
+
setEditingRoleId(null);
|
|
1718
|
+
}
|
|
1719
|
+
};
|
|
1720
|
+
const buildPermissionString = (namespace, relation, resourceId) => {
|
|
1721
|
+
if (resourceId) {
|
|
1722
|
+
return `${namespace.toLowerCase()}:${resourceId}#${relation}`;
|
|
1723
|
+
}
|
|
1724
|
+
return `${namespace.toLowerCase()}#${relation}`;
|
|
1725
|
+
};
|
|
1726
|
+
const togglePermission = (permissionString) => {
|
|
1727
|
+
const newPermissions = new Set(selectedPermissions);
|
|
1728
|
+
if (newPermissions.has(permissionString)) {
|
|
1729
|
+
newPermissions.delete(permissionString);
|
|
1730
|
+
} else {
|
|
1731
|
+
newPermissions.add(permissionString);
|
|
1732
|
+
}
|
|
1733
|
+
setSelectedPermissions(newPermissions);
|
|
1734
|
+
};
|
|
1735
|
+
const handleSubmit = () => {
|
|
1736
|
+
if (!roleName.trim()) return;
|
|
1737
|
+
const permissionsArray = Array.from(selectedPermissions);
|
|
1738
|
+
if (isEditMode && editingRoleId) {
|
|
1739
|
+
onRoleUpdate?.({
|
|
1740
|
+
role_id: editingRoleId,
|
|
1741
|
+
role_name: roleName,
|
|
1742
|
+
permissions: permissionsArray
|
|
1743
|
+
});
|
|
1744
|
+
} else {
|
|
1745
|
+
onRoleCreate?.({
|
|
1746
|
+
role_name: roleName,
|
|
1747
|
+
permissions: permissionsArray
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
const handleReset = () => {
|
|
1752
|
+
setRoleName("");
|
|
1753
|
+
setSelectedPermissions(/* @__PURE__ */ new Set());
|
|
1754
|
+
setIsEditMode(false);
|
|
1755
|
+
setEditingRoleId(null);
|
|
1756
|
+
};
|
|
1757
|
+
return /* @__PURE__ */ jsxs15(Card, { className: "w-full max-w-4xl", children: [
|
|
1758
|
+
/* @__PURE__ */ jsxs15(CardHeader, { children: [
|
|
1759
|
+
/* @__PURE__ */ jsx26(CardTitle, { children: isEditMode ? `Edit Role: ${roleName}` : "Create New Role" }),
|
|
1760
|
+
/* @__PURE__ */ jsx26(CardDescription, { children: isEditMode ? "Update permissions for this existing role" : "Define a new role with specific permissions" })
|
|
1761
|
+
] }),
|
|
1762
|
+
/* @__PURE__ */ jsxs15(CardContent, { className: "space-y-6", children: [
|
|
1763
|
+
/* @__PURE__ */ jsxs15("div", { className: "space-y-2 relative", children: [
|
|
1764
|
+
/* @__PURE__ */ jsx26(Label, { htmlFor: "role-name", children: "Role Name" }),
|
|
1765
|
+
/* @__PURE__ */ jsx26(
|
|
1766
|
+
Input,
|
|
1767
|
+
{
|
|
1768
|
+
id: "role-name",
|
|
1769
|
+
placeholder: "Enter role name (e.g., admin, developer, viewer)",
|
|
1770
|
+
value: roleName,
|
|
1771
|
+
onChange: (e) => handleRoleNameChange(e.target.value),
|
|
1772
|
+
onFocus: () => setShowSuggestions(true),
|
|
1773
|
+
onBlur: () => setTimeout(() => setShowSuggestions(false), 200)
|
|
1774
|
+
}
|
|
1775
|
+
),
|
|
1776
|
+
showSuggestions && roleSuggestions.length > 0 && /* @__PURE__ */ jsx26("div", { className: "absolute z-10 w-full mt-1 bg-background border rounded-md shadow-lg max-h-48 overflow-y-auto", children: roleSuggestions.filter(
|
|
1777
|
+
(suggestion) => suggestion.toLowerCase().includes(roleName.toLowerCase())
|
|
1778
|
+
).map((suggestion) => /* @__PURE__ */ jsx26(
|
|
1779
|
+
"button",
|
|
1780
|
+
{
|
|
1781
|
+
className: "w-full px-4 py-2 text-left hover:bg-accent hover:text-accent-foreground text-sm",
|
|
1782
|
+
onMouseDown: () => {
|
|
1783
|
+
handleRoleNameChange(suggestion);
|
|
1784
|
+
setShowSuggestions(false);
|
|
1785
|
+
},
|
|
1786
|
+
children: suggestion
|
|
1787
|
+
},
|
|
1788
|
+
suggestion
|
|
1789
|
+
)) }),
|
|
1790
|
+
isEditMode && /* @__PURE__ */ jsx26("p", { className: "text-xs text-amber-600 dark:text-amber-400", children: "\u26A0 Editing existing role - changes will update all users with this role" })
|
|
1791
|
+
] }),
|
|
1792
|
+
/* @__PURE__ */ jsx26(Separator2, {}),
|
|
1793
|
+
tenantNamespace && /* @__PURE__ */ jsxs15("div", { className: "space-y-4", children: [
|
|
1794
|
+
/* @__PURE__ */ jsxs15("div", { children: [
|
|
1795
|
+
/* @__PURE__ */ jsx26("h3", { className: "text-lg font-semibold", children: "Organization Permissions" }),
|
|
1796
|
+
/* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground", children: "Tenant-wide permissions that apply across the entire organization" })
|
|
1797
|
+
] }),
|
|
1798
|
+
/* @__PURE__ */ jsx26("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 pl-4", children: tenantNamespace.relations.map((relation) => {
|
|
1799
|
+
const permissionString = buildPermissionString(
|
|
1800
|
+
tenantNamespace.namespace,
|
|
1801
|
+
relation
|
|
1802
|
+
);
|
|
1803
|
+
const isChecked = selectedPermissions.has(permissionString);
|
|
1804
|
+
return /* @__PURE__ */ jsxs15(
|
|
1805
|
+
"div",
|
|
1806
|
+
{
|
|
1807
|
+
className: "flex items-center space-x-2",
|
|
1808
|
+
children: [
|
|
1809
|
+
/* @__PURE__ */ jsx26(
|
|
1810
|
+
Checkbox,
|
|
1811
|
+
{
|
|
1812
|
+
id: permissionString,
|
|
1813
|
+
checked: isChecked,
|
|
1814
|
+
onCheckedChange: () => togglePermission(permissionString)
|
|
1815
|
+
}
|
|
1816
|
+
),
|
|
1817
|
+
/* @__PURE__ */ jsx26(
|
|
1818
|
+
Label,
|
|
1819
|
+
{
|
|
1820
|
+
htmlFor: permissionString,
|
|
1821
|
+
className: "text-sm font-normal cursor-pointer",
|
|
1822
|
+
children: relation.replace(/_/g, " ")
|
|
1823
|
+
}
|
|
1824
|
+
)
|
|
1825
|
+
]
|
|
1826
|
+
},
|
|
1827
|
+
permissionString
|
|
1828
|
+
);
|
|
1829
|
+
}) })
|
|
1830
|
+
] }),
|
|
1831
|
+
fineGrainedNamespaces.length > 0 && /* @__PURE__ */ jsxs15(Fragment2, { children: [
|
|
1832
|
+
/* @__PURE__ */ jsx26(Separator2, {}),
|
|
1833
|
+
/* @__PURE__ */ jsxs15("div", { className: "space-y-6", children: [
|
|
1834
|
+
/* @__PURE__ */ jsxs15("div", { children: [
|
|
1835
|
+
/* @__PURE__ */ jsx26("h3", { className: "text-lg font-semibold", children: "Fine-Grained Permissions" }),
|
|
1836
|
+
/* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground", children: "Resource-specific permissions that require an object ID" })
|
|
1837
|
+
] }),
|
|
1838
|
+
fineGrainedNamespaces.map((namespace) => {
|
|
1839
|
+
const namespaceLower = namespace.namespace.toLowerCase();
|
|
1840
|
+
const resourceMap = namespaceMap[namespaceLower] || [];
|
|
1841
|
+
return /* @__PURE__ */ jsx26("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs15("div", { className: "pl-4", children: [
|
|
1842
|
+
/* @__PURE__ */ jsx26("h4", { className: "text-md font-medium capitalize", children: namespace.namespace }),
|
|
1843
|
+
resourceMap.length > 0 ? /* @__PURE__ */ jsx26("div", { className: "mt-4 space-y-6", children: resourceMap.map((resource) => /* @__PURE__ */ jsxs15(
|
|
1844
|
+
"div",
|
|
1845
|
+
{
|
|
1846
|
+
className: "border rounded-lg p-4 space-y-3 bg-muted/30",
|
|
1847
|
+
children: [
|
|
1848
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between", children: [
|
|
1849
|
+
/* @__PURE__ */ jsx26("span", { className: "font-medium text-sm", children: resource.label }),
|
|
1850
|
+
/* @__PURE__ */ jsx26("span", { className: "text-xs text-muted-foreground font-mono", children: resource.id })
|
|
1851
|
+
] }),
|
|
1852
|
+
/* @__PURE__ */ jsx26("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: namespace.relations.map((relation) => {
|
|
1853
|
+
const permissionString = buildPermissionString(
|
|
1854
|
+
namespace.namespace,
|
|
1855
|
+
relation,
|
|
1856
|
+
resource.id
|
|
1857
|
+
);
|
|
1858
|
+
const isChecked = selectedPermissions.has(permissionString);
|
|
1859
|
+
return /* @__PURE__ */ jsxs15(
|
|
1860
|
+
"div",
|
|
1861
|
+
{
|
|
1862
|
+
className: "flex items-center space-x-2",
|
|
1863
|
+
children: [
|
|
1864
|
+
/* @__PURE__ */ jsx26(
|
|
1865
|
+
Checkbox,
|
|
1866
|
+
{
|
|
1867
|
+
id: permissionString,
|
|
1868
|
+
checked: isChecked,
|
|
1869
|
+
onCheckedChange: () => togglePermission(permissionString)
|
|
1870
|
+
}
|
|
1871
|
+
),
|
|
1872
|
+
/* @__PURE__ */ jsx26(
|
|
1873
|
+
Label,
|
|
1874
|
+
{
|
|
1875
|
+
htmlFor: permissionString,
|
|
1876
|
+
className: "text-sm font-normal cursor-pointer",
|
|
1877
|
+
children: relation.replace(/_/g, " ")
|
|
1878
|
+
}
|
|
1879
|
+
)
|
|
1880
|
+
]
|
|
1881
|
+
},
|
|
1882
|
+
permissionString
|
|
1883
|
+
);
|
|
1884
|
+
}) })
|
|
1885
|
+
]
|
|
1886
|
+
},
|
|
1887
|
+
resource.id
|
|
1888
|
+
)) }) : /* @__PURE__ */ jsxs15("div", { className: "mt-3 p-4 border-2 border-dashed rounded-lg text-center", children: [
|
|
1889
|
+
/* @__PURE__ */ jsxs15("p", { className: "text-sm text-muted-foreground", children: [
|
|
1890
|
+
"No ",
|
|
1891
|
+
namespace.namespace.toLowerCase(),
|
|
1892
|
+
" resources available"
|
|
1893
|
+
] }),
|
|
1894
|
+
/* @__PURE__ */ jsxs15("p", { className: "text-xs text-muted-foreground mt-1", children: [
|
|
1895
|
+
"Use wildcard permissions (e.g., ",
|
|
1896
|
+
namespaceLower,
|
|
1897
|
+
":*#permission) for all resources"
|
|
1898
|
+
] })
|
|
1899
|
+
] }),
|
|
1900
|
+
/* @__PURE__ */ jsxs15("div", { className: "mt-4 space-y-2", children: [
|
|
1901
|
+
/* @__PURE__ */ jsxs15(Label, { className: "text-sm font-medium", children: [
|
|
1902
|
+
"Wildcard Permissions (All ",
|
|
1903
|
+
namespace.namespace,
|
|
1904
|
+
"s)"
|
|
1905
|
+
] }),
|
|
1906
|
+
/* @__PURE__ */ jsx26("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: namespace.relations.map((relation) => {
|
|
1907
|
+
const permissionString = buildPermissionString(
|
|
1908
|
+
namespace.namespace,
|
|
1909
|
+
relation,
|
|
1910
|
+
"*"
|
|
1911
|
+
);
|
|
1912
|
+
const isChecked = selectedPermissions.has(permissionString);
|
|
1913
|
+
return /* @__PURE__ */ jsxs15(
|
|
1914
|
+
"div",
|
|
1915
|
+
{
|
|
1916
|
+
className: "flex items-center space-x-2",
|
|
1917
|
+
children: [
|
|
1918
|
+
/* @__PURE__ */ jsx26(
|
|
1919
|
+
Checkbox,
|
|
1920
|
+
{
|
|
1921
|
+
id: permissionString,
|
|
1922
|
+
checked: isChecked,
|
|
1923
|
+
onCheckedChange: () => togglePermission(permissionString)
|
|
1924
|
+
}
|
|
1925
|
+
),
|
|
1926
|
+
/* @__PURE__ */ jsxs15(
|
|
1927
|
+
Label,
|
|
1928
|
+
{
|
|
1929
|
+
htmlFor: permissionString,
|
|
1930
|
+
className: "text-sm font-normal cursor-pointer",
|
|
1931
|
+
children: [
|
|
1932
|
+
relation.replace(/_/g, " "),
|
|
1933
|
+
" (all)"
|
|
1934
|
+
]
|
|
1935
|
+
}
|
|
1936
|
+
)
|
|
1937
|
+
]
|
|
1938
|
+
},
|
|
1939
|
+
permissionString
|
|
1940
|
+
);
|
|
1941
|
+
}) })
|
|
1942
|
+
] })
|
|
1943
|
+
] }) }, namespace.id);
|
|
1944
|
+
})
|
|
1945
|
+
] })
|
|
1946
|
+
] }),
|
|
1947
|
+
/* @__PURE__ */ jsx26(Separator2, {}),
|
|
1948
|
+
/* @__PURE__ */ jsxs15("div", { className: "space-y-2", children: [
|
|
1949
|
+
/* @__PURE__ */ jsxs15(Label, { className: "text-sm font-medium", children: [
|
|
1950
|
+
"Selected Permissions (",
|
|
1951
|
+
selectedPermissions.size,
|
|
1952
|
+
")"
|
|
1953
|
+
] }),
|
|
1954
|
+
selectedPermissions.size > 0 ? /* @__PURE__ */ jsx26("div", { className: "p-4 bg-muted rounded-md max-h-40 overflow-y-auto", children: /* @__PURE__ */ jsx26("ul", { className: "space-y-1 text-xs font-mono", children: Array.from(selectedPermissions).map((perm) => /* @__PURE__ */ jsx26("li", { className: "text-muted-foreground", children: perm }, perm)) }) }) : /* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground italic", children: "No permissions selected" })
|
|
1955
|
+
] }),
|
|
1956
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex justify-end space-x-3", children: [
|
|
1957
|
+
/* @__PURE__ */ jsx26(Button, { variant: "outline", onClick: handleReset, children: "Reset" }),
|
|
1958
|
+
/* @__PURE__ */ jsx26(
|
|
1959
|
+
Button,
|
|
1960
|
+
{
|
|
1961
|
+
onClick: handleSubmit,
|
|
1962
|
+
disabled: !roleName.trim() || selectedPermissions.size === 0,
|
|
1963
|
+
children: isEditMode ? "Update Role" : "Create Role"
|
|
1964
|
+
}
|
|
1965
|
+
)
|
|
1966
|
+
] })
|
|
1967
|
+
] })
|
|
1968
|
+
] });
|
|
1969
|
+
}
|
|
1521
1970
|
export {
|
|
1522
1971
|
LoginForm,
|
|
1523
1972
|
PricingTable,
|
|
1524
1973
|
RecoveryForm,
|
|
1525
1974
|
RegistrationForm,
|
|
1975
|
+
RoleCreator,
|
|
1526
1976
|
SettingsForm,
|
|
1527
1977
|
SwitchActiveTenant,
|
|
1528
1978
|
TenantCreator,
|
|
1979
|
+
UserInvite,
|
|
1529
1980
|
VerificationForm,
|
|
1530
1981
|
filterInputNodes,
|
|
1531
1982
|
findAnchorNode,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnibase/shadcn",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "OmniBase ShadCN UI Package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -36,37 +36,39 @@
|
|
|
36
36
|
"react-dom": "^19.1.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
+
"@radix-ui/react-checkbox": "^1.3.3",
|
|
39
40
|
"@radix-ui/react-label": "^2.1.7",
|
|
40
41
|
"@radix-ui/react-select": "^2.2.6",
|
|
42
|
+
"@radix-ui/react-separator": "^1.1.7",
|
|
41
43
|
"@radix-ui/react-slot": "^1.2.3",
|
|
42
|
-
"@types/node": "^24.
|
|
44
|
+
"@types/node": "^24.9.1",
|
|
43
45
|
"class-variance-authority": "^0.7.1",
|
|
44
46
|
"clsx": "^2.1.1",
|
|
45
47
|
"lucide-react": "^0.544.0",
|
|
46
48
|
"tailwind-merge": "^2.6.0"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
|
-
"@omnibase/core-js": "
|
|
50
|
-
"@storybook/addon-docs": "^9.1.
|
|
51
|
-
"@storybook/react-vite": "^9.1.
|
|
52
|
-
"@tailwindcss/vite": "^4.1.
|
|
53
|
-
"@types/react": "^
|
|
54
|
-
"@types/react-dom": "^
|
|
51
|
+
"@omnibase/core-js": "0.7.4",
|
|
52
|
+
"@storybook/addon-docs": "^9.1.15",
|
|
53
|
+
"@storybook/react-vite": "^9.1.15",
|
|
54
|
+
"@tailwindcss/vite": "^4.1.16",
|
|
55
|
+
"@types/react": "^19.2.2",
|
|
56
|
+
"@types/react-dom": "^19.2.2",
|
|
55
57
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
56
58
|
"@typescript-eslint/parser": "^6.21.0",
|
|
57
|
-
"@vitejs/plugin-react": "^5.0
|
|
58
|
-
"ajv": "^8.
|
|
59
|
+
"@vitejs/plugin-react": "^5.1.0",
|
|
60
|
+
"ajv": "^8.17.1",
|
|
59
61
|
"autoprefixer": "^10.4.21",
|
|
60
62
|
"eslint": "^8.57.1",
|
|
61
63
|
"eslint-plugin-react-hooks": "^4.6.2",
|
|
62
|
-
"eslint-plugin-storybook": "^9.1.
|
|
63
|
-
"react": "^19.
|
|
64
|
-
"react-dom": "^19.
|
|
65
|
-
"storybook": "^9.1.
|
|
66
|
-
"tailwindcss": "^4.1.
|
|
64
|
+
"eslint-plugin-storybook": "^9.1.15",
|
|
65
|
+
"react": "^19.2.0",
|
|
66
|
+
"react-dom": "^19.2.0",
|
|
67
|
+
"storybook": "^9.1.15",
|
|
68
|
+
"tailwindcss": "^4.1.16",
|
|
67
69
|
"tsup": "^8.5.0",
|
|
68
|
-
"typescript": "^5.9.
|
|
69
|
-
"vite": "^7.1.
|
|
70
|
+
"typescript": "^5.9.3",
|
|
71
|
+
"vite": "^7.1.12",
|
|
70
72
|
"vite-plugin-dts": "^4.5.4"
|
|
71
73
|
}
|
|
72
74
|
}
|