@rsc-labs/medusa-rbac
Version:
The RBAC functionality for MedusaJS
1,096 lines • 118 kB
JavaScript
"use strict";
const jsxRuntime = require("react/jsx-runtime");
const adminSdk = require("@medusajs/admin-sdk");
const ui = require("@medusajs/ui");
const React = require("react");
const material = require("@mui/material");
const icons = require("@medusajs/icons");
const reactRouterDom = require("react-router-dom");
const reactHookForm = require("react-hook-form");
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
const React__default = /* @__PURE__ */ _interopDefault(React);
var PermissionType = /* @__PURE__ */ ((PermissionType2) => {
PermissionType2["PREDEFINED"] = "predefined";
PermissionType2["CUSTOM"] = "custom";
return PermissionType2;
})(PermissionType || {});
var PermissionMatcherType = /* @__PURE__ */ ((PermissionMatcherType2) => {
PermissionMatcherType2["API"] = "api";
return PermissionMatcherType2;
})(PermissionMatcherType || {});
var PermissionActionType = /* @__PURE__ */ ((PermissionActionType2) => {
PermissionActionType2["READ"] = "read";
PermissionActionType2["WRITE"] = "write";
PermissionActionType2["DELETE"] = "delete";
return PermissionActionType2;
})(PermissionActionType || {});
var AdminRbacPolicyType = /* @__PURE__ */ ((AdminRbacPolicyType2) => {
AdminRbacPolicyType2["DENY"] = "deny";
AdminRbacPolicyType2["ALLOW"] = "allow";
return AdminRbacPolicyType2;
})(AdminRbacPolicyType || {});
function getActionMessage(actionType) {
switch (actionType) {
case PermissionActionType.READ:
return `read, so you cannot view the content`;
case PermissionActionType.WRITE:
return `write, so you are not able to create the content`;
case PermissionActionType.DELETE:
return `delete, so you are not able to delete the content`;
default:
return "unknown action";
}
}
const ActionsList = ({ actionTypes }) => {
return /* @__PURE__ */ jsxRuntime.jsx("ul", { children: actionTypes.map((actionType) => {
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: ` - ${getActionMessage(actionType)}` });
}) });
};
const TestMyAuthorization = ({ urlToTest }) => {
const [isLoading, setLoading] = React.useState(true);
const [authorizationResult, setAuthorizationResult] = React.useState(void 0);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/check`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
urlToTest
})
}).then((res) => res.json()).then((responseJson) => {
if (JSON.stringify(responseJson) === "{}") {
setAuthorizationResult(void 0);
} else {
setAuthorizationResult(responseJson);
}
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !isLoading && authorizationResult && authorizationResult.denied.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Alert, { variant: "error", children: [
`You are unauthorized to:`,
/* @__PURE__ */ jsxRuntime.jsx(ActionsList, { actionTypes: authorizationResult.denied })
] }) }) });
};
const CustomersUnAuthorized = () => {
return /* @__PURE__ */ jsxRuntime.jsx(TestMyAuthorization, { urlToTest: "/admin/customers" });
};
adminSdk.defineWidgetConfig({
zone: "customer.list.before"
});
const OrdersUnauthorized = () => {
return /* @__PURE__ */ jsxRuntime.jsx(TestMyAuthorization, { urlToTest: "/admin/orders" });
};
adminSdk.defineWidgetConfig({
zone: "order.list.before"
});
const PriceListsUnauthorized = () => {
return /* @__PURE__ */ jsxRuntime.jsx(TestMyAuthorization, { urlToTest: "/admin/price-lists" });
};
adminSdk.defineWidgetConfig({
zone: "price_list.list.before"
});
const ProductsUnauthorized = () => {
return /* @__PURE__ */ jsxRuntime.jsx(TestMyAuthorization, { urlToTest: "/admin/products" });
};
adminSdk.defineWidgetConfig({
zone: "product.list.before"
});
const DashboardMembersCard = () => {
const [members, setMembers] = React.useState([]);
const [isLoading, setLoading] = React.useState(true);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/members`, {
credentials: "include"
}).then((res) => res.json()).then((result) => {
setMembers(result);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 3, children: [
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, alignItems: "center", children: [
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Heading, { children: [
members.length,
" ",
members.length === 1 ? "member" : "members"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: !isLoading && /* @__PURE__ */ jsxRuntime.jsx(icons.Users, {}) })
] }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
members.filter((member) => member.role !== void 0).length,
" assigned"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
members.filter((member) => member.role === void 0).length,
" unassigned"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(
reactRouterDom.Link,
{
to: `/rbac/members`,
style: { display: "contents" },
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { children: "Configure" })
}
) })
] }) });
};
const DashboardRolesCard = () => {
const [roles, setRoles] = React.useState([]);
const [isLoading, setLoading] = React.useState(true);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/roles`, {
credentials: "include"
}).then((res) => res.json()).then((roles2) => {
setRoles(roles2);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 3, children: [
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, alignItems: "center", children: [
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Heading, { children: [
roles.length,
" ",
roles.length === 1 ? "role" : "roles"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: !isLoading && /* @__PURE__ */ jsxRuntime.jsx(icons.AcademicCap, {}) })
] }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
roles.filter((role) => role.users !== void 0 && role.users.length > 0).length,
" used"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
roles.filter((role) => role.users === void 0 || role.users.length === 0).length,
" not used"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(
reactRouterDom.Link,
{
to: `/rbac/roles`,
style: { display: "contents" },
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { children: "Configure" })
}
) })
] }) });
};
const AssignedRolesList = ({ sortedRoles }) => {
return /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", rowSpacing: 2, children: [
sortedRoles.map((sortedRole) => {
return /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, columnSpacing: 1, alignItems: "center", justifyContent: "space-between", children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: sortedRole.name }) }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, columnSpacing: 1, alignItems: "center", children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: sortedRole.users.length }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Users, {}) })
] })
] }, sortedRole.id);
}),
sortedRoles.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: `-` }) })
] });
};
const DashboardAssignedRolesCard = () => {
const [roles, setRoles] = React.useState([]);
const [isLoading, setLoading] = React.useState(true);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/roles`, {
credentials: "include"
}).then((res) => res.json()).then((roles2) => {
const sorted = roles2.sort((a, b) => b.users.length - a.users.length);
setRoles(sorted);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 3, children: [
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: `Most used roles` })
] }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsx(AssignedRolesList, { sortedRoles: roles })
] })
] }) });
};
const DashboardPermissionsCard = () => {
const [permissions, setPermissions] = React.useState([]);
const [isLoading, setLoading] = React.useState(true);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/permissions`, {
credentials: "include"
}).then((res) => res.json()).then((result) => {
setPermissions(result);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 3, children: [
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, alignItems: "center", children: [
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Heading, { children: [
permissions.length,
" ",
permissions.length === 1 ? "permission" : "permissions"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: !isLoading && /* @__PURE__ */ jsxRuntime.jsx(icons.LockClosedSolid, {}) })
] }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
permissions.filter((perm) => perm.type == PermissionType.PREDEFINED).length,
" predefined"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 10 }),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
permissions.filter((perm) => perm.type == PermissionType.CUSTOM).length,
" custom"
] })
] }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(
reactRouterDom.Link,
{
to: `/rbac/permissions`,
style: { display: "contents" },
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { children: "Configure" })
}
) })
] }) });
};
const Dashboard = () => {
return /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, rowSpacing: 10, columnSpacing: 10, style: { marginTop: 15 }, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { size: 4, children: /* @__PURE__ */ jsxRuntime.jsx(DashboardPermissionsCard, {}) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { size: 4, children: /* @__PURE__ */ jsxRuntime.jsx(DashboardRolesCard, {}) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { size: 4, children: /* @__PURE__ */ jsxRuntime.jsx(DashboardMembersCard, {}) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { size: 4, children: /* @__PURE__ */ jsxRuntime.jsx(DashboardAssignedRolesCard, {}) })
] });
};
const RbacAuthorizationCheck = ({ children }) => {
const [isLoading, setLoading] = React.useState(true);
const [authorizationResult, setAuthorizationResult] = React.useState(void 0);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/check`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
urlToTest: `/admin/rbac`
})
}).then((res) => res.json()).then((responseJson) => {
setAuthorizationResult(responseJson);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
if (isLoading) {
return /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 12 });
}
if (authorizationResult.denied.length > 0) {
return /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: "You are unauthorized to manage RBAC" }) });
}
return /* @__PURE__ */ jsxRuntime.jsx("div", { children });
};
const RbacLicenceCheck = ({ children }) => {
const [licenceStatus, setLicenceStatus] = React.useState(void 0);
const [isLoading, setLoading] = React.useState(true);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/licence`, {
credentials: "include"
}).then((res) => res.json()).then((result) => {
setLicenceStatus(result.licence);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
if (isLoading) {
return /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 12 });
}
if (licenceStatus == void 0) {
return /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: "Cannot get licence status" }) });
}
switch (licenceStatus) {
case "EXPIRED":
return /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: "Licence is expired" }) });
case "INVALID":
return /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: "Licence is invalid" }) });
}
if (licenceStatus !== "VALID") {
return /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: "Licence is in unknown state. Please contact us." }) });
}
return /* @__PURE__ */ jsxRuntime.jsx("div", { children });
};
const MainPage = () => {
return /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", rowSpacing: 3, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "RBAC system" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(Dashboard, {}) })
] });
};
const RbacPage = () => {
return /* @__PURE__ */ jsxRuntime.jsx(RbacLicenceCheck, { children: /* @__PURE__ */ jsxRuntime.jsx(RbacAuthorizationCheck, { children: /* @__PURE__ */ jsxRuntime.jsx(MainPage, {}) }) });
};
const config$3 = adminSdk.defineRouteConfig({
label: "RBAC",
icon: icons.Users
});
const SelectRole = ({ currentRole, roles, setChosenRole }) => {
const [value, setValue] = React.useState(currentRole ? currentRole.id : void 0);
const handleChange = (roleId) => {
setValue(roleId);
setChosenRole(roles.find((role) => role.id == roleId));
};
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[256px]", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { onValueChange: handleChange, value, children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select a role" }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: roles && roles.map((item) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: item.id, children: `${item.name}` }, item.id)) })
] }) });
};
const AvailableRolesList = ({ currentRole, setChosenRole }) => {
const [isLoading, setLoading] = React.useState(true);
const [roles, setRoles] = React.useState([]);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/roles`, {
credentials: "include"
}).then((res) => res.json()).then((roles2) => {
setRoles(roles2);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, {}),
!isLoading && /* @__PURE__ */ jsxRuntime.jsx(SelectRole, { currentRole, roles, setChosenRole })
] });
};
const DrawerEditUser = ({ currentRole, setRole }) => {
const [chosenRole, setChosenRole] = React.useState(currentRole);
const [drawerIsOpen, setDrawerIsOpen] = React.useState(void 0);
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer, { open: drawerIsOpen, onOpenChange: setDrawerIsOpen, children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { children: `Assign` }) }),
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: "Select role" }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", columnSpacing: 10, rowSpacing: 3, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Choose role" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(AvailableRolesList, { currentRole, setChosenRole }) })
] }) }),
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Cancel" }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => {
setRole(chosenRole);
setDrawerIsOpen(false);
}, children: "Save" })
] })
] })
] });
};
const RoleBadge = ({ role }) => {
if (role) {
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "small", color: "green", children: role.name });
}
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "small", children: "Unassigned" });
};
function MembersTable() {
const [members, setMembers] = React.useState([]);
const [isLoading, setLoading] = React.useState(true);
const assignRole = (roleId, userId) => {
fetch(`/admin/rbac/members/assignments`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
userId,
roleId
})
}).then((res) => res.json()).then(({ message }) => {
setLoading(true);
if (message) {
throw message;
}
}).catch((e) => {
setLoading(true);
console.error(e);
});
};
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/members`, {
credentials: "include"
}).then((res) => res.json()).then((result) => {
setMembers(result);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
const [currentPage, setCurrentPage] = React.useState(0);
const pageSize = 3;
const pageCount = Math.ceil(members.length / pageSize);
const canNextPage = React.useMemo(
() => currentPage < pageCount - 1,
[currentPage, pageCount]
);
const canPreviousPage = React.useMemo(() => currentPage - 1 >= 0, [currentPage]);
const nextPage = () => {
if (canNextPage) {
setCurrentPage(currentPage + 1);
}
};
const previousPage = () => {
if (canPreviousPage) {
setCurrentPage(currentPage - 1);
}
};
const currentMembers = React.useMemo(() => {
if (isLoading) {
return [];
}
const offset = currentPage * pageSize;
const limit = Math.min(offset + pageSize, members.length);
return members.slice(offset, limit);
}, [currentPage, pageSize, members, isLoading]);
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 flex-col", children: [
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Name" }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Email" }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Role" }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, {})
] }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: currentMembers.map((member) => {
const name = member.user.first_name !== null && member.user.last_name !== null ? `${member.user.first_name} ${member.user.last_name}` : "-";
return /* @__PURE__ */ jsxRuntime.jsxs(
ui.Table.Row,
{
className: "[&_td:last-child]:w-[1%] [&_td:last-child]:whitespace-nowrap",
children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: name }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: member.user.email }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(RoleBadge, { role: member.role }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(DrawerEditUser, { currentRole: member.role, setRole: (role) => assignRole(role.id, member.user.id) }) })
]
},
member.user.id
);
}) })
] }),
/* @__PURE__ */ jsxRuntime.jsx(
ui.Table.Pagination,
{
count: members.length,
pageSize,
pageIndex: currentPage,
pageCount,
canPreviousPage,
canNextPage,
previousPage,
nextPage
}
)
] });
}
const MembersTable$1 = React__default.default.memo(MembersTable);
const MembersPage = () => {
return /* @__PURE__ */ jsxRuntime.jsx(RbacLicenceCheck, { children: /* @__PURE__ */ jsxRuntime.jsx(RbacAuthorizationCheck, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", rowSpacing: 3, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Members" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsx(MembersTable$1, {}) }) })
] }) }) });
};
const config$2 = adminSdk.defineRouteConfig({
label: "Members"
});
const InputCreateCategory = ({ category, setCategory }) => {
const [error, setError] = React.useState(void 0);
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
/* @__PURE__ */ jsxRuntime.jsx(
ui.Input,
{
value: category,
onChange: (e) => setCategory(e.target.value),
"aria-invalid": error !== void 0
}
),
error !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: error })
] });
};
const DrawerCreateCategory = ({ reload }) => {
const [categoryName, setCategoryName] = React.useState(void 0);
const [drawerIsOpen, setDrawerIsOpen] = React.useState(void 0);
const onSubmit = () => {
fetch(`/admin/rbac/categories`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
name: categoryName
})
}).then(async (response) => {
if (response.ok) {
reload();
ui.toast.info("New category has been created", {
description: "You can now select it from the list."
});
} else {
const error = await response.json();
ui.toast.error("Error", {
description: `New category cannot be created. ${error.message}`
});
}
}).catch((e) => {
ui.toast.error("Error", {
description: `New category cannot be created. ${e.toString()}`
});
console.error(e);
});
};
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer, { open: drawerIsOpen, onOpenChange: setDrawerIsOpen, children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: `Create` }) }),
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: "New category" }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", columnSpacing: 10, rowSpacing: 3, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Name" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(InputCreateCategory, { setCategory: setCategoryName }) })
] }) }),
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Cancel" }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => {
onSubmit();
reload();
setDrawerIsOpen(false);
}, children: "Save" })
] })
] })
] });
};
const SelectCategory = ({ currentCategory, setChosenCategory }) => {
const [value, setValue] = React.useState(void 0);
const [categories, setCategories] = React.useState([]);
const handleChange = (categoryId) => {
if (categoryId !== "None") {
setValue(categories.find((cat) => cat.id == categoryId));
setChosenCategory(categories.find((cat) => cat.id == categoryId));
} else {
setValue(void 0);
setChosenCategory("None");
}
};
const [isLoading, setLoading] = React.useState(true);
React.useEffect(() => {
if (!isLoading) {
return;
}
fetch(`/admin/rbac/categories`, {
credentials: "include"
}).then((res) => res.json()).then((result) => {
setCategories(result);
setLoading(false);
}).catch((error) => {
console.error(error);
});
}, [isLoading]);
return /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", rowSpacing: 2, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { onValueChange: handleChange, value: value ? value.id : "None", children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select a category" }) }),
isLoading && /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, {}),
!isLoading && /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "None", children: `None` }, "None"),
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Separator, {}),
categories && categories.map((item) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: item.id, children: `${item.name}` }, item.id))
] })
] }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", children: "You can create new category" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(DrawerCreateCategory, { reload: () => setLoading(true) }) })
] });
};
const validateName$2 = (value) => {
if (value && value.length > 0) {
return true;
}
return false;
};
const CreatePermissionGeneralStep = ({ register, errors, setCategory, currentCategory }) => {
return /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { size: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 1, marginTop: 2, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Create permission" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Set a name which will describe permission" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, children: /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 1, marginTop: 2, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", children: "Name" }) }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
/* @__PURE__ */ jsxRuntime.jsx(
ui.Input,
{
placeholder: "Reading products",
...register("name", {
validateName: validateName$2
}),
"aria-invalid": errors["name"] !== void 0
}
),
errors["name"] !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: errors["name"].message })
] })
] }) }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, children: /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 1, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", children: "Category (optional)" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(SelectCategory, { currentCategory, setChosenCategory: setCategory }) })
] }) }) })
] }) }) });
};
const SelectMatcherType = ({ currentMatcherType, matcherTypes, setChosenMatcherType }) => {
const [value, setValue] = React.useState(currentMatcherType);
const handleChange = (matcherType) => {
setValue(matcherType);
setChosenMatcherType(matcherType);
};
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[256px]", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { onValueChange: handleChange, value, children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select a matcher type" }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: matcherTypes && matcherTypes.map((item) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: item, children: `${item}` }, item)) })
] }) });
};
const SelectActionType = ({ currentActionType, actionTypes, setChosenActionType }) => {
const [value, setValue] = React.useState(currentActionType);
const handleChange = (actionType) => {
setValue(actionType);
setChosenActionType(actionType);
};
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[256px]", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { onValueChange: handleChange, value, children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select an action type" }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: actionTypes && actionTypes.map((item) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: item, children: `${item}` }, item)) })
] }) });
};
const validateName$1 = (value) => {
if (value && value.length > 0) {
return true;
}
return false;
};
const InputMatcher = ({ register, errors }) => {
return /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, direction: "column", spacing: 1, children: /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, children: /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 1, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", children: "Matcher" }) }),
/* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { children: [
/* @__PURE__ */ jsxRuntime.jsx(
ui.Input,
{
placeholder: "/admin/products",
...register("matcher", {
validateName: validateName$1
}),
"aria-invalid": errors["matcher"] !== void 0
}
),
errors["matcher"] !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: errors["matcher"].message })
] })
] }) }) }) });
};
const CreatePermissionConfigurationStep = ({ register, errors, currentMatcherType, currentActionType, setMatcherType, setActionType }) => {
return /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { size: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, direction: "column", spacing: 1, marginTop: 2, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Configure permission" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", children: "Choose matcher type" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(SelectMatcherType, { currentMatcherType, matcherTypes: [PermissionMatcherType.API], setChosenMatcherType: setMatcherType }) }),
errors["matcherType"] !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: errors["matcherType"].message }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", children: "Choose action type" }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(SelectActionType, { currentActionType, actionTypes: Object.values(PermissionActionType), setChosenActionType: setActionType }) }),
errors["actionType"] !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: errors["actionType"].message }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(InputMatcher, { register, errors }) })
] }) }) });
};
const tabOrder$1 = [
"general",
"configuration"
/* CONFIGURATION */
];
const initialTabState$1 = {
[
"general"
/* GENERAL */
]: "in-progress",
[
"configuration"
/* CONFIGURATION */
]: "not-started"
};
const PrimaryButton$1 = ({ tab, next, isLoading, handleSubmit }) => {
if (tab === "general") {
return /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => next(tab), children: "Continue" });
}
return /* @__PURE__ */ jsxRuntime.jsx(
ui.Button,
{
type: "submit",
isLoading,
onClick: handleSubmit,
children: "Create"
},
"submit-button"
);
};
const CreatePermissionModal = ({ reloadTable }) => {
const { register, handleSubmit, formState: { errors }, getValues, setError, clearErrors, reset } = reactHookForm.useForm({
defaultValues: {
name: "",
matcher: ""
}
});
const [activeTab, setActiveTab] = React.useState(
"general"
/* GENERAL */
);
const [tabState, setTabState] = React.useState(initialTabState$1);
const [isOpen, setIsOpen] = React.useState(false);
const [matcherType, setMatcherType] = React.useState(void 0);
const [actionType, setActionType] = React.useState(void 0);
const [category, setCategory] = React.useState(void 0);
const partialFormValidation = (tab) => {
let result = true;
switch (tab) {
case "general":
if (getValues("name") && getValues.length > 0) {
clearErrors("name");
} else {
setError("name", { type: "custom", message: "Please fill the name" });
result = false;
}
return result;
case "configuration":
if (getValues("matcher") && getValues.length > 0) {
clearErrors("matcher");
} else {
setError("matcher", { type: "custom", message: "Please fill the matcher" });
result = false;
}
if (matcherType) {
clearErrors("matcherType");
} else {
setError("matcherType", { type: "custom", message: "Please fill the matcher type" });
result = false;
}
if (actionType) {
clearErrors("actionType");
} else {
setError("actionType", { type: "custom", message: "Please fill the action type" });
result = false;
}
return result;
default:
return false;
}
};
React.useEffect(() => {
if (!isOpen) {
reset();
setActiveTab(
"general"
/* GENERAL */
);
setMatcherType(void 0);
setActionType(void 0);
setCategory(void 0);
setTabState(initialTabState$1);
}
}, [isOpen]);
const isTabDirty = (tab) => {
return partialFormValidation(tab);
};
const onSubmit = (data) => {
fetch(`/admin/rbac/permissions`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
name: data.name,
type: PermissionType.CUSTOM,
matcherType,
matcher: data.matcher,
actionType,
category
})
}).then(async (response) => {
if (response.ok) {
ui.toast.info("Permission", {
description: "New permission has been created"
});
reloadTable();
setIsOpen(false);
} else {
const error = await response.json();
ui.toast.error("Permission", {
description: `New permission cannot be created. ${error.message}`
});
}
}).catch((e) => {
ui.toast.error("Permission", {
description: `New permission cannot be created. ${e.toString()}`
});
console.error(e);
});
};
function handleChangeTab(newTab) {
if (activeTab === newTab) {
return;
}
if (tabOrder$1.indexOf(newTab) < tabOrder$1.indexOf(activeTab)) {
const isCurrentTabDirty = isTabDirty(activeTab);
setTabState((prev) => ({
...prev,
[activeTab]: isCurrentTabDirty ? prev[activeTab] : "not-started",
[newTab]: "in-progress"
}));
setActiveTab(newTab);
return;
}
const tabs = tabOrder$1.slice(0, tabOrder$1.indexOf(newTab));
for (const tab of tabs) {
if (tab === "general") {
if (!partialFormValidation(tab)) {
setTabState((prev) => ({
...prev,
[tab]: "in-progress"
}));
setActiveTab(tab);
return;
}
setTabState((prev) => ({
...prev,
[tab]: "completed"
}));
} else if (tab === "configuration") {
if (!partialFormValidation(tab)) {
setTabState((prev) => ({
...prev,
[tab]: "in-progress"
}));
setActiveTab(tab);
return;
}
setTabState((prev) => ({
...prev,
[tab]: "completed"
}));
}
}
setTabState((prev) => ({
...prev,
[activeTab]: "completed",
[newTab]: "in-progress"
}));
setActiveTab(newTab);
}
const handleNextTab = (tab) => {
if (tabOrder$1.indexOf(tab) + 1 >= tabOrder$1.length) {
return;
}
const nextTab = tabOrder$1[tabOrder$1.indexOf(tab) + 1];
handleChangeTab(nextTab);
};
return /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal, { open: isOpen, onOpenChange: setIsOpen, children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Create" }) }),
/* @__PURE__ */ jsxRuntime.jsx("form", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(
ui.ProgressTabs,
{
value: activeTab,
onValueChange: (tab) => handleChangeTab(tab),
className: "flex h-full flex-col overflow-hidden",
children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-between gap-x-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-my-2 w-full max-w-[600px] border-l", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.ProgressTabs.List, { children: [
/* @__PURE__ */ jsxRuntime.jsx(
ui.ProgressTabs.Trigger,
{
value: "general",
status: tabState.general,
children: "General"
}
),
/* @__PURE__ */ jsxRuntime.jsx(
ui.ProgressTabs.Trigger,
{
value: "configuration",
status: tabState.configuration,
children: "Configuration"
}
)
] }) }) }) }),
/* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Body, { className: "size-full overflow-hidden", children: [
/* @__PURE__ */ jsxRuntime.jsx(
ui.ProgressTabs.Content,
{
className: "size-full overflow-y-auto",
value: "general",
children: /* @__PURE__ */ jsxRuntime.jsx(CreatePermissionGeneralStep, { register, errors, setCategory, currentCategory: category })
}
),
/* @__PURE__ */ jsxRuntime.jsx(
ui.ProgressTabs.Content,
{
className: "size-full overflow-y-auto",
value: "configuration",
children: /* @__PURE__ */ jsxRuntime.jsx(
CreatePermissionConfigurationStep,
{
currentMatcherType: matcherType,
currentActionType: actionType,
register,
errors,
setActionType,
setMatcherType
}
)
}
)
] }),
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { container: true, justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.Grid2, { container: true, columnSpacing: 2, rowSpacing: 5, children: [
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Close, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Cancel" }) }) }),
/* @__PURE__ */ jsxRuntime.jsx(material.Grid2, { children: /* @__PURE__ */ jsxRuntime.jsx(
PrimaryButton$1,
{
tab: activeTab,
next: handleNextTab,
isLoading: false,
handleSubmit: () => {
if (partialFormValidation(activeTab)) {
return handleSubmit(onSubmit)();
}
}
}
) })
] }) }) }) })
]
}
) }) })
] });
};
const ActionMenu = ({ groups }) => {
return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Content, { children: groups.map((group, index) => {
if (!group.actions.length) {
return null;
}
const isLast = index === groups.length - 1;
return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Group, { children: [
group.actions.map((action, index2) => {
if (action.onClick) {
return /* @__PURE__ */ jsxRuntime.jsxs(
ui.DropdownMenu.Item,
{
disabled: action.disabled,
onClick: (e) => {
e.stopPropagation();
action.onClick();
},
className: ui.clx(
"[&_svg]:text-ui-fg-subtle flex items-center gap-x-2",
{
"[&_svg]:text-ui-fg-disabled": action.disabled
}
),
children: [
action.icon,
/* @__PURE__ */ jsxRuntime.jsx("span", { children: action.label })
]
},
index2
);
}
return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
ui.DropdownMenu.Item,
{
className: ui.clx(
"[&_svg]:text-ui-fg-subtle flex items-center gap-x-2",
{
"[&_svg]:text-ui-fg-disabled": action.disabled
}
),
asChild: true,
disabled: action.disabled,
children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Link, { to: action.to, onClick: (e) => e.stopPropagation(), children: [
action.icon,
/* @__PURE__ */ jsxRuntime.jsx("span", { children: action.label })
] })
}
) }, index2);
}),
!isLast && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {})
] }, index);
}) })
] });
};
const Header = ({
title,
subtitle,
actions = []
}) => {
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: title }),
subtitle && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", size: "small", children: subtitle })
] }),
actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center gap-x-2", children: actions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
action.type === "button" && /* @__PURE__ */ React.createElement(
ui.Button,
{
...action.props,
size: action.props.size || "small",
key: index
},
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
action.props.childr