mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 18:27:36 +00:00
feat: updated frontend for review changes
This commit is contained in:
+23
-18
@@ -1,4 +1,5 @@
|
|||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
|
import { subject } from "@casl/ability";
|
||||||
import {
|
import {
|
||||||
faCaretDown,
|
faCaretDown,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
@@ -28,7 +29,8 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
Tag,
|
Tag,
|
||||||
Tooltip} from "@app/components/v2";
|
Tooltip
|
||||||
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
@@ -48,7 +50,8 @@ import {
|
|||||||
isConditionalSubjects,
|
isConditionalSubjects,
|
||||||
PROJECT_PERMISSION_OBJECT,
|
PROJECT_PERMISSION_OBJECT,
|
||||||
projectRoleFormSchema,
|
projectRoleFormSchema,
|
||||||
rolePermission2Form} from "@app/views/Project/RolePage/components/RolePermissionsSection/ProjectRoleModifySection.utils";
|
rolePermission2Form
|
||||||
|
} from "@app/views/Project/RolePage/components/RolePermissionsSection/ProjectRoleModifySection.utils";
|
||||||
import { renderConditionalComponents } from "@app/views/Project/RolePage/components/RolePermissionsSection/RolePermissionsSection";
|
import { renderConditionalComponents } from "@app/views/Project/RolePage/components/RolePermissionsSection/RolePermissionsSection";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -95,7 +98,7 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
|
|||||||
const { permission } = useProjectPermission();
|
const { permission } = useProjectPermission();
|
||||||
const isIdentityEditDisabled = permission.cannot(
|
const isIdentityEditDisabled = permission.cannot(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
ProjectPermissionSub.Identity
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
const form = useForm<TFormSchema>({
|
const form = useForm<TFormSchema>({
|
||||||
@@ -275,13 +278,13 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
|
|||||||
].title.toLowerCase()
|
].title.toLowerCase()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.map((subject) => (
|
.map((permissionSubject) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={`permission-create-${subject}`}
|
key={`permission-create-${permissionSubject}`}
|
||||||
className="py-3"
|
className="py-3"
|
||||||
onClick={() => onNewPolicy(subject as ProjectPermissionSub)}
|
onClick={() => onNewPolicy(permissionSubject as ProjectPermissionSub)}
|
||||||
>
|
>
|
||||||
{PROJECT_PERMISSION_OBJECT[subject as ProjectPermissionSub].title}
|
{PROJECT_PERMISSION_OBJECT[permissionSubject as ProjectPermissionSub].title}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
))}
|
))}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
@@ -412,17 +415,19 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="mb-2 text-lg">Policies</div>
|
<div className="mb-2 text-lg">Policies</div>
|
||||||
{(isCreate || !isLoading) && <PermissionEmptyState />}
|
{(isCreate || !isLoading) && <PermissionEmptyState />}
|
||||||
{(Object.keys(PROJECT_PERMISSION_OBJECT) as ProjectPermissionSub[]).map((subject) => (
|
{(Object.keys(PROJECT_PERMISSION_OBJECT) as ProjectPermissionSub[]).map(
|
||||||
<GeneralPermissionPolicies
|
(permissionSubject) => (
|
||||||
subject={subject}
|
<GeneralPermissionPolicies
|
||||||
actions={PROJECT_PERMISSION_OBJECT[subject].actions}
|
subject={permissionSubject}
|
||||||
title={PROJECT_PERMISSION_OBJECT[subject].title}
|
actions={PROJECT_PERMISSION_OBJECT[permissionSubject].actions}
|
||||||
key={`project-permission-${subject}`}
|
title={PROJECT_PERMISSION_OBJECT[permissionSubject].title}
|
||||||
isDisabled={isDisabled}
|
key={`project-permission-${permissionSubject}`}
|
||||||
>
|
isDisabled={isDisabled}
|
||||||
{renderConditionalComponents(subject, isDisabled)}
|
>
|
||||||
</GeneralPermissionPolicies>
|
{renderConditionalComponents(permissionSubject, isDisabled)}
|
||||||
))}
|
</GeneralPermissionPolicies>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+7
-2
@@ -1,3 +1,4 @@
|
|||||||
|
import { subject } from "@casl/ability";
|
||||||
import { faFolder, faPencil, faTrash } from "@fortawesome/free-solid-svg-icons";
|
import { faFolder, faPencil, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { format, formatDistance } from "date-fns";
|
import { format, formatDistance } from "date-fns";
|
||||||
@@ -93,7 +94,9 @@ export const IdentityRoleDetailsSection = ({
|
|||||||
<h3 className="text-lg font-semibold text-mineshaft-100">Project Roles</h3>
|
<h3 className="text-lg font-semibold text-mineshaft-100">Project Roles</h3>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Edit}
|
I={ProjectPermissionActions.Edit}
|
||||||
a={ProjectPermissionSub.Identity}
|
a={subject(ProjectPermissionSub.Identity, {
|
||||||
|
identityId: identityMembershipDetails.identity.id
|
||||||
|
})}
|
||||||
renderTooltip
|
renderTooltip
|
||||||
allowedLabel="Edit Role(s)"
|
allowedLabel="Edit Role(s)"
|
||||||
>
|
>
|
||||||
@@ -175,7 +178,9 @@ export const IdentityRoleDetailsSection = ({
|
|||||||
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Edit}
|
I={ProjectPermissionActions.Edit}
|
||||||
a={ProjectPermissionSub.Member}
|
a={subject(ProjectPermissionSub.Identity, {
|
||||||
|
identityId: identityMembershipDetails.identity.id
|
||||||
|
})}
|
||||||
renderTooltip
|
renderTooltip
|
||||||
allowedLabel="Remove Role"
|
allowedLabel="Remove Role"
|
||||||
>
|
>
|
||||||
|
|||||||
-354
@@ -1,354 +0,0 @@
|
|||||||
/* eslint-disable no-nested-ternary */
|
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
|
||||||
import { faCaretDown, faClock, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { format, formatDistance } from "date-fns";
|
|
||||||
import ms from "ms";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
FormControl,
|
|
||||||
IconButton,
|
|
||||||
Input,
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
Select,
|
|
||||||
SelectItem,
|
|
||||||
Spinner,
|
|
||||||
Tag,
|
|
||||||
Tooltip
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import {
|
|
||||||
ProjectPermissionActions,
|
|
||||||
ProjectPermissionSub,
|
|
||||||
useProjectPermission,
|
|
||||||
useSubscription,
|
|
||||||
useWorkspace
|
|
||||||
} from "@app/context";
|
|
||||||
import { useGetProjectRoles, useUpdateIdentityWorkspaceRole } from "@app/hooks/api";
|
|
||||||
import { IdentityMembership } from "@app/hooks/api/identities/types";
|
|
||||||
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
|
||||||
import { ProjectUserMembershipTemporaryMode } from "@app/hooks/api/workspace/types";
|
|
||||||
|
|
||||||
const roleFormSchema = z.object({
|
|
||||||
roles: z
|
|
||||||
.object({
|
|
||||||
slug: z.string(),
|
|
||||||
temporaryAccess: z.discriminatedUnion("isTemporary", [
|
|
||||||
z.object({
|
|
||||||
isTemporary: z.literal(true),
|
|
||||||
temporaryRange: z.string().min(1),
|
|
||||||
temporaryAccessStartTime: z.string().datetime(),
|
|
||||||
temporaryAccessEndTime: z.string().datetime().nullable().optional()
|
|
||||||
}),
|
|
||||||
z.object({
|
|
||||||
isTemporary: z.literal(false)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
});
|
|
||||||
type TRoleForm = z.infer<typeof roleFormSchema>;
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
identityProjectMember: IdentityMembership;
|
|
||||||
onOpenUpgradeModal: (title: string) => void;
|
|
||||||
};
|
|
||||||
export const IdentityRbacSection = ({ identityProjectMember, onOpenUpgradeModal }: Props) => {
|
|
||||||
const { subscription } = useSubscription();
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
|
||||||
const workspaceId = currentWorkspace?.id || "";
|
|
||||||
const { data: projectRoles, isLoading: isRolesLoading } = useGetProjectRoles(workspaceId);
|
|
||||||
const { permission } = useProjectPermission();
|
|
||||||
const isMemberEditDisabled = permission.cannot(
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
);
|
|
||||||
|
|
||||||
const roleForm = useForm<TRoleForm>({
|
|
||||||
resolver: zodResolver(roleFormSchema),
|
|
||||||
values: {
|
|
||||||
roles: identityProjectMember?.roles?.map(({ customRoleSlug, role, ...dto }) => ({
|
|
||||||
slug: customRoleSlug || role,
|
|
||||||
temporaryAccess: dto.isTemporary
|
|
||||||
? {
|
|
||||||
isTemporary: true,
|
|
||||||
temporaryRange: dto.temporaryRange,
|
|
||||||
temporaryAccessEndTime: dto.temporaryAccessEndTime,
|
|
||||||
temporaryAccessStartTime: dto.temporaryAccessStartTime
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
isTemporary: dto.isTemporary
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const selectedRoleList = useFieldArray({
|
|
||||||
name: "roles",
|
|
||||||
control: roleForm.control
|
|
||||||
});
|
|
||||||
|
|
||||||
const formRoleField = roleForm.watch("roles");
|
|
||||||
|
|
||||||
const updateMembershipRole = useUpdateIdentityWorkspaceRole();
|
|
||||||
|
|
||||||
const handleRoleUpdate = async (data: TRoleForm) => {
|
|
||||||
if (updateMembershipRole.isLoading) return;
|
|
||||||
|
|
||||||
const sanitizedRoles = data.roles.map((el) => {
|
|
||||||
const { isTemporary } = el.temporaryAccess;
|
|
||||||
if (!isTemporary) {
|
|
||||||
return { role: el.slug, isTemporary: false as const };
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
role: el.slug,
|
|
||||||
isTemporary: true as const,
|
|
||||||
temporaryMode: ProjectUserMembershipTemporaryMode.Relative,
|
|
||||||
temporaryRange: el.temporaryAccess.temporaryRange,
|
|
||||||
temporaryAccessStartTime: el.temporaryAccess.temporaryAccessStartTime
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const hasCustomRoleSelected = sanitizedRoles.some(
|
|
||||||
(el) => !Object.values(ProjectMembershipRole).includes(el.role as ProjectMembershipRole)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (hasCustomRoleSelected && subscription && !subscription?.rbac) {
|
|
||||||
onOpenUpgradeModal(
|
|
||||||
"You can assign custom roles to members if you upgrade your Infisical plan."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await updateMembershipRole.mutateAsync({
|
|
||||||
workspaceId,
|
|
||||||
identityId: identityProjectMember.identity.id,
|
|
||||||
roles: sanitizedRoles
|
|
||||||
});
|
|
||||||
createNotification({ text: "Successfully updated roles", type: "success" });
|
|
||||||
roleForm.reset(undefined, { keepValues: true });
|
|
||||||
} catch (err) {
|
|
||||||
createNotification({ text: "Failed to update role", type: "error" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isRolesLoading)
|
|
||||||
return (
|
|
||||||
<div className="flex w-full items-center justify-center p-8">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="text-lg font-medium">Roles</div>
|
|
||||||
<p className="text-sm text-mineshaft-400">Select one of the pre-defined or custom roles.</p>
|
|
||||||
<div>
|
|
||||||
<form onSubmit={roleForm.handleSubmit(handleRoleUpdate)}>
|
|
||||||
<div className="mt-2 flex flex-col space-y-2">
|
|
||||||
{selectedRoleList.fields.map(({ id }, index) => {
|
|
||||||
const { temporaryAccess } = formRoleField[index];
|
|
||||||
const isTemporary = temporaryAccess?.isTemporary;
|
|
||||||
const isExpired =
|
|
||||||
temporaryAccess.isTemporary &&
|
|
||||||
new Date() > new Date(temporaryAccess.temporaryAccessEndTime || "");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={id} className="flex items-center space-x-2">
|
|
||||||
<Controller
|
|
||||||
control={roleForm.control}
|
|
||||||
name={`roles.${index}.slug`}
|
|
||||||
render={({ field: { onChange, ...field } }) => (
|
|
||||||
<Select
|
|
||||||
defaultValue={field.value}
|
|
||||||
{...field}
|
|
||||||
isDisabled={isMemberEditDisabled}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
className="w-full bg-mineshaft-600"
|
|
||||||
>
|
|
||||||
{projectRoles?.map(({ name, slug, id: projectRoleId }) => (
|
|
||||||
<SelectItem value={slug} key={projectRoleId}>
|
|
||||||
{name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger disabled={isMemberEditDisabled}>
|
|
||||||
<div>
|
|
||||||
<Tooltip
|
|
||||||
content={
|
|
||||||
temporaryAccess?.isTemporary
|
|
||||||
? isExpired
|
|
||||||
? "Timed Access Expired"
|
|
||||||
: `Until ${format(
|
|
||||||
new Date(temporaryAccess.temporaryAccessEndTime || ""),
|
|
||||||
"yyyy-MM-dd HH:mm:ss"
|
|
||||||
)}`
|
|
||||||
: "Non expiry access"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
|
||||||
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
|
||||||
isDisabled={isMemberEditDisabled}
|
|
||||||
className={twMerge(
|
|
||||||
"border-none bg-mineshaft-600 py-2.5 text-xs capitalize hover:bg-mineshaft-500",
|
|
||||||
isTemporary && "text-primary",
|
|
||||||
isExpired && "text-red-600"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{temporaryAccess?.isTemporary
|
|
||||||
? isExpired
|
|
||||||
? "Access Expired"
|
|
||||||
: formatDistance(
|
|
||||||
new Date(temporaryAccess.temporaryAccessEndTime || ""),
|
|
||||||
new Date()
|
|
||||||
)
|
|
||||||
: "Permanent"}
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent
|
|
||||||
arrowClassName="fill-gray-600"
|
|
||||||
side="right"
|
|
||||||
sideOffset={12}
|
|
||||||
hideCloseBtn
|
|
||||||
className="border border-gray-600 pt-4"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col space-y-4">
|
|
||||||
<div className="border-b border-b-gray-700 pb-2 text-sm text-mineshaft-300">
|
|
||||||
Configure timed access
|
|
||||||
</div>
|
|
||||||
{isExpired && <Tag colorSchema="red">Expired</Tag>}
|
|
||||||
<Controller
|
|
||||||
control={roleForm.control}
|
|
||||||
defaultValue="1h"
|
|
||||||
name={`roles.${index}.temporaryAccess.temporaryRange`}
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label={<TtlFormLabel label="Validity" />}
|
|
||||||
isError={Boolean(error?.message)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Button
|
|
||||||
size="xs"
|
|
||||||
onClick={() => {
|
|
||||||
const temporaryRange = roleForm.getValues(
|
|
||||||
`roles.${index}.temporaryAccess.temporaryRange`
|
|
||||||
);
|
|
||||||
if (!temporaryRange) {
|
|
||||||
roleForm.setError(
|
|
||||||
`roles.${index}.temporaryAccess.temporaryRange`,
|
|
||||||
{ type: "required", message: "Required" },
|
|
||||||
{ shouldFocus: true }
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
roleForm.clearErrors(`roles.${index}.temporaryAccess.temporaryRange`);
|
|
||||||
roleForm.setValue(
|
|
||||||
`roles.${index}.temporaryAccess`,
|
|
||||||
{
|
|
||||||
isTemporary: true,
|
|
||||||
temporaryAccessStartTime: new Date().toISOString(),
|
|
||||||
temporaryRange,
|
|
||||||
temporaryAccessEndTime: new Date(
|
|
||||||
new Date().getTime() + ms(temporaryRange)
|
|
||||||
).toISOString()
|
|
||||||
},
|
|
||||||
{ shouldDirty: true }
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{temporaryAccess.isTemporary ? "Restart" : "Grant"}
|
|
||||||
</Button>
|
|
||||||
{temporaryAccess.isTemporary && (
|
|
||||||
<Button
|
|
||||||
size="xs"
|
|
||||||
variant="outline_bg"
|
|
||||||
colorSchema="danger"
|
|
||||||
onClick={() => {
|
|
||||||
roleForm.setValue(`roles.${index}.temporaryAccess`, {
|
|
||||||
isTemporary: false
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Revoke Access
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<IconButton
|
|
||||||
variant="outline_bg"
|
|
||||||
className="border border-mineshaft-500 bg-mineshaft-600 py-3 hover:border-red/70 hover:bg-red/20"
|
|
||||||
ariaLabel="delete-role"
|
|
||||||
isDisabled={isMemberEditDisabled || selectedRoleList.fields.length === 1}
|
|
||||||
onClick={() => {
|
|
||||||
if (selectedRoleList.fields.length > 1) {
|
|
||||||
selectedRoleList.remove(index);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faTrash} />
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 flex justify-between space-x-2">
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Edit}
|
|
||||||
a={ProjectPermissionSub.Identity}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
onClick={() =>
|
|
||||||
selectedRoleList.append({
|
|
||||||
slug: ProjectMembershipRole.Member,
|
|
||||||
temporaryAccess: { isTemporary: false }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Add Role
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
className={twMerge(
|
|
||||||
"transition-all",
|
|
||||||
"cursor-default opacity-0",
|
|
||||||
roleForm.formState.isDirty && "cursor-pointer opacity-100"
|
|
||||||
)}
|
|
||||||
isDisabled={!roleForm.formState.isDirty}
|
|
||||||
isLoading={roleForm.formState.isSubmitting}
|
|
||||||
>
|
|
||||||
Save Roles
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
import { Alert, AlertDescription } from "@app/components/v2";
|
|
||||||
import { useWorkspace } from "@app/context";
|
|
||||||
import { IdentityMembership } from "@app/hooks/api/identities/types";
|
|
||||||
|
|
||||||
import { IdentityRbacSection } from "./IdentityRbacSection";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
identityProjectMember: IdentityMembership;
|
|
||||||
onOpenUpgradeModal: (title: string) => void;
|
|
||||||
};
|
|
||||||
export const IdentityRoleForm = ({ identityProjectMember, onOpenUpgradeModal }: Props) => {
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<IdentityRbacSection
|
|
||||||
identityProjectMember={identityProjectMember}
|
|
||||||
onOpenUpgradeModal={onOpenUpgradeModal}
|
|
||||||
/>
|
|
||||||
<Alert
|
|
||||||
title="Additional privileges have been moved and now offer full permission customization."
|
|
||||||
className="mt-4 border-primary/50 bg-primary/10"
|
|
||||||
>
|
|
||||||
<AlertDescription>
|
|
||||||
<Link
|
|
||||||
href={`/project/${currentWorkspace?.id || ""}/identities/${
|
|
||||||
identityProjectMember?.identity?.id
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span className="cursor-pointer text-primary underline underline-offset-2">
|
|
||||||
Click here to access them now
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export { IdentityRoleForm } from "./IdentityRoleForm";
|
|
||||||
+1
-1
@@ -494,7 +494,7 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
|||||||
title: "Machine Identity Management",
|
title: "Machine Identity Management",
|
||||||
actions: [
|
actions: [
|
||||||
{ label: "Read", value: "read" },
|
{ label: "Read", value: "read" },
|
||||||
{ label: "Create", value: "create" },
|
{ label: "Add", value: "create" },
|
||||||
{ label: "Modify", value: "edit" },
|
{ label: "Modify", value: "edit" },
|
||||||
{ label: "Remove", value: "delete" }
|
{ label: "Remove", value: "delete" }
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user