mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Continue progress on project role page
This commit is contained in:
@@ -9,7 +9,7 @@ export default function Role() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
|
||||
<title>{t("common.head-title", { title: "Project Settings" })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<RolePage />
|
||||
@@ -5,8 +5,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
// import { OrgPermissionCan } from "@app/components/permissions";
|
||||
// import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
@@ -18,11 +16,12 @@ import {
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
// import { withPermission } from "@app/hoc";
|
||||
import { withProjectPermission } from "@app/hoc";
|
||||
import { useDeleteOrgRole, useGetOrgRole, useGetProjectRoleBySlug } from "@app/hooks/api";
|
||||
import { useGetProjectRoleBySlug } from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { RoleDetailsSection, RolePermissionsSection } from "./components";
|
||||
|
||||
// import { RoleDetailsSection, RoleModal, RolePermissionsSection } from "./components";
|
||||
|
||||
export const RolePage = withProjectPermission(
|
||||
@@ -32,24 +31,17 @@ export const RolePage = withProjectPermission(
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const projectId = currentWorkspace?.id || "";
|
||||
|
||||
console.log("RolePage currentWorkspace: ", currentWorkspace);
|
||||
|
||||
// const { currentOrg } = useOrganization();
|
||||
// const orgId = currentOrg?.id || "";
|
||||
|
||||
const { data } = useGetProjectRoleBySlug(currentWorkspace?.slug ?? "", roleSlug as string);
|
||||
|
||||
console.log("useGetProjectRoleBySlug data: ", data);
|
||||
|
||||
// const { data } = useGetOrgRole(orgId, roleId); // TODO: get project role
|
||||
const { mutateAsync: deleteOrgRole } = useDeleteOrgRole();
|
||||
// const { mutateAsync: deleteOrgRole } = useDeleteOrgRole();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"role",
|
||||
"deleteOrgRole"
|
||||
"deleteRole"
|
||||
] as const);
|
||||
|
||||
const onDeleteOrgRoleSubmit = async () => {
|
||||
const onDeleteRoleSubmit = async () => {
|
||||
try {
|
||||
// if (!orgId || !roleId) return;
|
||||
// await deleteOrgRole({
|
||||
@@ -65,7 +57,7 @@ export const RolePage = withProjectPermission(
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
const error = err as any;
|
||||
const text = error?.response?.data?.message ?? "Failed to delete organization role";
|
||||
const text = error?.response?.data?.message ?? "Failed to delete project role";
|
||||
|
||||
createNotification({
|
||||
text,
|
||||
@@ -136,7 +128,7 @@ export const RolePage = withProjectPermission(
|
||||
: "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () => {
|
||||
handlePopUpOpen("deleteOrgRole");
|
||||
handlePopUpOpen("deleteRole");
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
@@ -150,21 +142,19 @@ export const RolePage = withProjectPermission(
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="mr-4 w-96">
|
||||
RoleDetailsSection here
|
||||
{/* <RoleDetailsSection roleId={roleId} handlePopUpOpen={handlePopUpOpen} /> */}
|
||||
<RoleDetailsSection roleSlug={roleSlug} handlePopUpOpen={handlePopUpOpen} />
|
||||
</div>
|
||||
RolePermissionsSection here
|
||||
{/* <RolePermissionsSection roleId={roleId} /> */}
|
||||
<RolePermissionsSection roleSlug={roleSlug} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* <RoleModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> */}
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteOrgRole.isOpen}
|
||||
isOpen={popUp.deleteRole.isOpen}
|
||||
title={`Are you sure want to delete the project role ${data?.name ?? ""}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteOrgRole", isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteRole", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() => onDeleteOrgRoleSubmit()}
|
||||
onDeleteApproved={() => onDeleteRoleSubmit()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { faCheck, faCopy, faPencil } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { IconButton, Tooltip } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { useTimedReset } from "@app/hooks";
|
||||
import { useGetProjectRoleBySlug } from "@app/hooks/api";
|
||||
// import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
roleSlug: string;
|
||||
// handlePopUpOpen: (popUpName: keyof UsePopUpState<["role"]>, data?: {}) => void;
|
||||
};
|
||||
|
||||
export const RoleDetailsSection = ({
|
||||
roleSlug
|
||||
}: // handlePopUpOpen
|
||||
Props) => {
|
||||
const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({
|
||||
initialState: "Copy ID to clipboard"
|
||||
});
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { data } = useGetProjectRoleBySlug(currentWorkspace?.slug ?? "", roleSlug as string);
|
||||
|
||||
const isCustomRole = !["admin", "member", "viewer", "no-access"].includes(data?.slug ?? "");
|
||||
|
||||
return data ? (
|
||||
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||
<h3 className="text-lg font-semibold text-mineshaft-100">Details</h3>
|
||||
{isCustomRole && (
|
||||
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Role}>
|
||||
{(isAllowed) => {
|
||||
return (
|
||||
<Tooltip content="Edit Role">
|
||||
<IconButton
|
||||
isDisabled={!isAllowed}
|
||||
ariaLabel="copy icon"
|
||||
variant="plain"
|
||||
className="group relative"
|
||||
onClick={() => {
|
||||
// TODO
|
||||
// handlePopUpOpen("role", {
|
||||
// roleId
|
||||
// })
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPencil} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
}}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
</div>
|
||||
<div className="pt-4">
|
||||
<div className="mb-4">
|
||||
<p className="text-sm font-semibold text-mineshaft-300">Role ID</p>
|
||||
<div className="group flex align-top">
|
||||
<p className="text-sm text-mineshaft-300">{data.id}</p>
|
||||
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
||||
<Tooltip content={copyTextId}>
|
||||
<IconButton
|
||||
ariaLabel="copy icon"
|
||||
variant="plain"
|
||||
className="group relative ml-2"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(data.id);
|
||||
setCopyTextId("Copied");
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={isCopyingId ? faCheck : faCopy} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<p className="text-sm font-semibold text-mineshaft-300">Name</p>
|
||||
<p className="text-sm text-mineshaft-300">{data.name}</p>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<p className="text-sm font-semibold text-mineshaft-300">Slug</p>
|
||||
<p className="text-sm text-mineshaft-300">{data.slug}</p>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<p className="text-sm font-semibold text-mineshaft-300">Description</p>
|
||||
<p className="text-sm text-mineshaft-300">
|
||||
{data.description?.length ? data.description : "-"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div />
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,232 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { Control, Controller, UseFormSetValue, useWatch } from "react-hook-form";
|
||||
import { faChevronDown, faChevronRight } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Checkbox, Select, SelectItem, Td, Tr } from "@app/components/v2";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { TFormSchema } from "@app/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils";
|
||||
|
||||
const GENERAL_PERMISSIONS = [
|
||||
{ action: "read", label: "View" },
|
||||
{ action: "create", label: "Create" },
|
||||
{ action: "edit", label: "Modify" },
|
||||
{ action: "delete", label: "Remove" }
|
||||
] as const;
|
||||
|
||||
const WORKSPACE_PERMISSIONS = [
|
||||
{ action: "edit", label: "Update project details" },
|
||||
{ action: "delete", label: "Delete projects" }
|
||||
] as const;
|
||||
|
||||
const MEMBERS_PERMISSIONS = [
|
||||
{ action: "read", label: "View all members" },
|
||||
{ action: "create", label: "Invite members" },
|
||||
{ action: "edit", label: "Edit members" },
|
||||
{ action: "delete", label: "Remove members" }
|
||||
] as const;
|
||||
|
||||
const SECRET_ROLLBACK_PERMISSIONS = [
|
||||
{ action: "create", label: "Perform Rollback" },
|
||||
{ action: "read", label: "View" }
|
||||
] as const;
|
||||
|
||||
const getPermissionList = (option: Props["formName"]) => {
|
||||
switch (option) {
|
||||
case "workspace":
|
||||
return WORKSPACE_PERMISSIONS;
|
||||
case "member":
|
||||
return MEMBERS_PERMISSIONS;
|
||||
case "secret-rollback":
|
||||
return SECRET_ROLLBACK_PERMISSIONS;
|
||||
default:
|
||||
return GENERAL_PERMISSIONS;
|
||||
}
|
||||
};
|
||||
|
||||
type PermissionName =
|
||||
| `permissions.workspace.${"edit" | "delete"}`
|
||||
| `permissions.secret-rollback.${"create" | "read"}`
|
||||
| `permissions.${Exclude<
|
||||
keyof NonNullable<TFormSchema["permissions"]>,
|
||||
"workspace" | "secret-rollback" | "secrets"
|
||||
>}.${"read" | "create" | "edit" | "delete"}`;
|
||||
|
||||
type Props = {
|
||||
isEditable: boolean;
|
||||
title: string;
|
||||
formName: keyof Omit<Exclude<TFormSchema["permissions"], undefined>, "secrets">;
|
||||
setValue: UseFormSetValue<TFormSchema>;
|
||||
control: Control<TFormSchema>;
|
||||
};
|
||||
|
||||
enum Permission {
|
||||
NoAccess = "no-access",
|
||||
ReadOnly = "read-only",
|
||||
FullAccess = "full-acess",
|
||||
Custom = "custom"
|
||||
}
|
||||
|
||||
export const RolePermissionRow = ({ isEditable, title, formName, control, setValue }: Props) => {
|
||||
const [isRowExpanded, setIsRowExpanded] = useToggle();
|
||||
const [isCustom, setIsCustom] = useToggle();
|
||||
|
||||
const rule = useWatch({
|
||||
control,
|
||||
name: `permissions.${formName}`
|
||||
});
|
||||
|
||||
const selectedPermissionCategory = useMemo(() => {
|
||||
// TODO: re-check
|
||||
const actions = Object.keys(rule || {}) as Array<keyof typeof rule>;
|
||||
|
||||
switch (formName) {
|
||||
case "secret-rollback":
|
||||
case "workspace": {
|
||||
const totalActions = WORKSPACE_PERMISSIONS.length;
|
||||
const score = actions
|
||||
.map((key) => (rule?.[key] ? 1 : 0))
|
||||
.reduce((a, b) => a + b, 0 as number);
|
||||
if (isCustom) return Permission.Custom;
|
||||
if (score === 0) return Permission.NoAccess;
|
||||
if (score === totalActions) return Permission.FullAccess;
|
||||
|
||||
return Permission.Custom; // TODO: update
|
||||
}
|
||||
default: {
|
||||
const totalActions = GENERAL_PERMISSIONS.length;
|
||||
const score = actions
|
||||
.map((key) => (rule?.[key] ? 1 : 0))
|
||||
.reduce((a, b) => a + b, 0 as number);
|
||||
if (isCustom) return Permission.Custom;
|
||||
if (score === 0) return Permission.NoAccess;
|
||||
if (score === totalActions) return Permission.FullAccess;
|
||||
if (rule && "read" in rule) {
|
||||
if (score === 1 && rule?.read) return Permission.ReadOnly;
|
||||
}
|
||||
|
||||
return Permission.Custom;
|
||||
}
|
||||
}
|
||||
}, [rule, isCustom]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedPermissionCategory === Permission.Custom) setIsCustom.on();
|
||||
else setIsCustom.off();
|
||||
}, [selectedPermissionCategory]);
|
||||
|
||||
useEffect(() => {
|
||||
const isRowCustom = selectedPermissionCategory === Permission.Custom;
|
||||
if (isRowCustom) {
|
||||
setIsRowExpanded.on();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handlePermissionChange = (val: Permission) => {
|
||||
if (val === Permission.Custom) {
|
||||
setIsRowExpanded.on();
|
||||
setIsCustom.on();
|
||||
return;
|
||||
}
|
||||
setIsCustom.off();
|
||||
|
||||
switch (val) {
|
||||
case Permission.NoAccess:
|
||||
setValue(
|
||||
`permissions.${formName}`,
|
||||
{ read: false, edit: false, create: false, delete: false },
|
||||
{ shouldDirty: true }
|
||||
);
|
||||
break;
|
||||
case Permission.FullAccess:
|
||||
setValue(
|
||||
`permissions.${formName}`,
|
||||
{ read: true, edit: true, create: true, delete: true },
|
||||
{ shouldDirty: true }
|
||||
);
|
||||
break;
|
||||
case Permission.ReadOnly:
|
||||
setValue(
|
||||
`permissions.${formName}`,
|
||||
{ read: true, edit: false, create: false, delete: false },
|
||||
{ shouldDirty: true }
|
||||
);
|
||||
break;
|
||||
default:
|
||||
setValue(
|
||||
`permissions.${formName}`,
|
||||
{ read: false, edit: false, create: false, delete: false },
|
||||
{ shouldDirty: true }
|
||||
);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tr
|
||||
className="h-10 cursor-pointer transition-colors duration-300 hover:bg-mineshaft-700"
|
||||
onClick={() => setIsRowExpanded.toggle()}
|
||||
>
|
||||
<Td>
|
||||
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
|
||||
</Td>
|
||||
<Td>{title}</Td>
|
||||
<Td>
|
||||
<Select
|
||||
value={selectedPermissionCategory}
|
||||
className="w-40 bg-mineshaft-600"
|
||||
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
|
||||
onValueChange={handlePermissionChange}
|
||||
isDisabled={!isEditable}
|
||||
>
|
||||
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
|
||||
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
|
||||
<SelectItem value={Permission.FullAccess}>Full Access</SelectItem>
|
||||
<SelectItem value={Permission.Custom}>Custom</SelectItem>
|
||||
</Select>
|
||||
</Td>
|
||||
</Tr>
|
||||
{isRowExpanded && (
|
||||
<Tr>
|
||||
<Td
|
||||
colSpan={3}
|
||||
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && " border-mineshaft-500 p-8"}`}
|
||||
>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{getPermissionList(formName).map(({ action, label }) => {
|
||||
const permissionName = `permissions.${formName}.${action}` as PermissionName;
|
||||
return (
|
||||
<Controller
|
||||
name={permissionName}
|
||||
key={permissionName}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
isChecked={field.value}
|
||||
onCheckedChange={(e) => {
|
||||
if (!isEditable) {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Failed to update default role"
|
||||
});
|
||||
return;
|
||||
}
|
||||
field.onChange(e);
|
||||
}}
|
||||
id={permissionName}
|
||||
>
|
||||
{label}
|
||||
</Checkbox>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
Control,
|
||||
// Controller,
|
||||
UseFormGetValues,
|
||||
UseFormSetValue,
|
||||
useWatch
|
||||
} from "react-hook-form";
|
||||
import { faChevronDown, faChevronRight } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Select, SelectItem,Td, Tr } from "@app/components/v2";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { TFormSchema } from "@app/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
formName: "secrets";
|
||||
isEditable: boolean;
|
||||
setValue: UseFormSetValue<TFormSchema>;
|
||||
getValue: UseFormGetValues<TFormSchema>;
|
||||
control: Control<TFormSchema>;
|
||||
};
|
||||
|
||||
enum Permission {
|
||||
NoAccess = "no-access",
|
||||
ReadOnly = "read-only",
|
||||
FullAccess = "full-acess",
|
||||
Custom = "custom"
|
||||
}
|
||||
|
||||
export const RowPermissionSecretsRow = ({
|
||||
title,
|
||||
formName,
|
||||
isEditable,
|
||||
setValue,
|
||||
getValue,
|
||||
control
|
||||
}: Props) => {
|
||||
const [isRowExpanded, setIsRowExpanded] = useToggle();
|
||||
// const [isCustom, setIsCustom] = useToggle();
|
||||
|
||||
const allRule = useWatch({ control, name: `permissions.${formName}.all` });
|
||||
|
||||
const selectedPermissionCategory = useMemo(() => {
|
||||
const { read, delete: del, edit, create } = allRule || {};
|
||||
if (read && del && edit && create) return Permission.FullAccess;
|
||||
if (read) return Permission.ReadOnly;
|
||||
return Permission.NoAccess;
|
||||
}, [allRule]);
|
||||
|
||||
const handlePermissionChange = (val: Permission) => {
|
||||
if (!val) return;
|
||||
switch (val) {
|
||||
case Permission.NoAccess: {
|
||||
const permissions = getValue("permissions");
|
||||
if (permissions) delete permissions[formName];
|
||||
setValue("permissions", permissions, { shouldDirty: true });
|
||||
break;
|
||||
}
|
||||
case Permission.FullAccess:
|
||||
setValue(
|
||||
`permissions.${formName}`,
|
||||
{ all: { read: true, edit: true, create: true, delete: true } },
|
||||
{ shouldDirty: true }
|
||||
);
|
||||
break;
|
||||
case Permission.ReadOnly:
|
||||
setValue(
|
||||
`permissions.${formName}`,
|
||||
{ all: { read: true, edit: false, create: false, delete: false } },
|
||||
{ shouldDirty: true }
|
||||
);
|
||||
break;
|
||||
default:
|
||||
setValue(
|
||||
`permissions.${formName}`,
|
||||
{ custom: { read: false, edit: false, create: false, delete: false } },
|
||||
{ shouldDirty: true }
|
||||
);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tr
|
||||
className="h-10 cursor-pointer transition-colors duration-300 hover:bg-mineshaft-700"
|
||||
onClick={() => setIsRowExpanded.toggle()}
|
||||
>
|
||||
<Td>
|
||||
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
|
||||
</Td>
|
||||
<Td>{title}</Td>
|
||||
<Td>
|
||||
<Select
|
||||
value={selectedPermissionCategory}
|
||||
className="w-40 bg-mineshaft-600"
|
||||
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
|
||||
onValueChange={handlePermissionChange}
|
||||
isDisabled={!isEditable}
|
||||
>
|
||||
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
|
||||
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
|
||||
<SelectItem value={Permission.FullAccess}>Full Access</SelectItem>
|
||||
<SelectItem value={Permission.Custom}>Custom</SelectItem>
|
||||
</Select>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,199 @@
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import {
|
||||
Button,
|
||||
Table,
|
||||
TableContainer,
|
||||
TBody,
|
||||
Th,
|
||||
THead,
|
||||
Tr
|
||||
// Td
|
||||
} from "@app/components/v2";
|
||||
import { ProjectPermissionSub,useWorkspace } from "@app/context";
|
||||
import {
|
||||
// useUpdateOrgRole,
|
||||
useGetProjectRoleBySlug
|
||||
} from "@app/hooks/api";
|
||||
import {
|
||||
formSchema,
|
||||
rolePermission2Form,
|
||||
TFormSchema} from "@app/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils";
|
||||
|
||||
import { RolePermissionRow } from "./RolePermissionRow";
|
||||
|
||||
const SINGLE_PERMISSION_LIST = [
|
||||
{
|
||||
title: "Project",
|
||||
formName: "workspace"
|
||||
},
|
||||
{
|
||||
title: "Integrations",
|
||||
formName: "integrations"
|
||||
},
|
||||
{
|
||||
title: "Secret Protect policy",
|
||||
formName: ProjectPermissionSub.SecretApproval
|
||||
},
|
||||
{
|
||||
title: "Roles",
|
||||
formName: "role"
|
||||
},
|
||||
{
|
||||
title: "User management",
|
||||
formName: "member"
|
||||
},
|
||||
{
|
||||
title: "Group management",
|
||||
formName: "groups"
|
||||
},
|
||||
{
|
||||
title: "Machine identity management",
|
||||
formName: "identity"
|
||||
},
|
||||
{
|
||||
title: "Webhooks",
|
||||
formName: "webhooks"
|
||||
},
|
||||
{
|
||||
title: "Service Tokens",
|
||||
formName: "service-tokens"
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
formName: "settings"
|
||||
},
|
||||
{
|
||||
title: "Environments",
|
||||
formName: "environments"
|
||||
},
|
||||
{
|
||||
title: "Tags",
|
||||
formName: "tags"
|
||||
},
|
||||
{
|
||||
title: "Audit Logs",
|
||||
formName: "audit-logs"
|
||||
},
|
||||
{
|
||||
title: "IP Allowlist",
|
||||
formName: "ip-allowlist"
|
||||
},
|
||||
{
|
||||
title: "Certificate Authorities",
|
||||
formName: "certificate-authorities"
|
||||
},
|
||||
{
|
||||
title: "Certificates",
|
||||
formName: "certificates"
|
||||
},
|
||||
{
|
||||
title: "Secret Rollback",
|
||||
formName: "secret-rollback"
|
||||
}
|
||||
] as const;
|
||||
|
||||
type Props = {
|
||||
roleSlug: string;
|
||||
};
|
||||
|
||||
// TODO: fill table
|
||||
|
||||
export const RolePermissionsSection = ({ roleSlug }: Props) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { data: role } = useGetProjectRoleBySlug(currentWorkspace?.slug ?? "", roleSlug as string);
|
||||
|
||||
console.log("RolePermissionsSection role data: ", role);
|
||||
|
||||
const {
|
||||
setValue,
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { isDirty, isSubmitting },
|
||||
reset
|
||||
} = useForm<TFormSchema>({
|
||||
defaultValues: role ? { ...role, permissions: rolePermission2Form(role.permissions) } : {},
|
||||
resolver: zodResolver(formSchema)
|
||||
});
|
||||
|
||||
// const { mutateAsync: updateRole } = useUpdateOrgRole();
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
// await updateRole({
|
||||
// orgId,
|
||||
// id: roleId,
|
||||
// ...el,
|
||||
// permissions: formRolePermission2API(el.permissions)
|
||||
// });
|
||||
createNotification({ type: "success", text: "Successfully updated role" });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
createNotification({ type: "error", text: "Failed to update role" });
|
||||
}
|
||||
};
|
||||
|
||||
const isCustomRole = !["admin", "member", "viewer", "no-access"].includes(role?.slug ?? "");
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||
>
|
||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||
<h3 className="text-lg font-semibold text-mineshaft-100">Permissions</h3>
|
||||
{isCustomRole && (
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
type="submit"
|
||||
isDisabled={isSubmitting || !isDirty}
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-4 text-mineshaft-300"
|
||||
variant="link"
|
||||
isDisabled={isSubmitting || !isDirty}
|
||||
isLoading={isSubmitting}
|
||||
onClick={() => reset()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="py-4">
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th className="w-5" />
|
||||
<Th>Resource</Th>
|
||||
<Th>Permission</Th>
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
{SINGLE_PERMISSION_LIST.map((permission) => {
|
||||
console.log("permission: ", permission);
|
||||
return (
|
||||
<RolePermissionRow
|
||||
title={permission.title}
|
||||
formName={permission.formName}
|
||||
control={control}
|
||||
setValue={setValue}
|
||||
key={`project-role-${roleSlug}-permission-${permission.formName}`}
|
||||
isEditable={isCustomRole}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { RolePermissionsSection } from "./RolePermissionsSection";
|
||||
2
frontend/src/views/Project/RolePage/components/index.tsx
Normal file
2
frontend/src/views/Project/RolePage/components/index.tsx
Normal file
@@ -0,0 +1,2 @@
|
||||
export { RoleDetailsSection } from "./RoleDetailsSection";
|
||||
export { RolePermissionsSection } from "./RolePermissionsSection";
|
||||
Reference in New Issue
Block a user