diff --git a/frontend/src/hooks/api/identities/types.ts b/frontend/src/hooks/api/identities/types.ts index fd3e88614..b8829f798 100644 --- a/frontend/src/hooks/api/identities/types.ts +++ b/frontend/src/hooks/api/identities/types.ts @@ -41,6 +41,18 @@ export type IdentityMembership = { temporaryAccessStartTime: string | null; temporaryAccessEndTime: string | null; }[]; + additionalPrivileges: { + id: string; + name: string; + description: string | null | undefined; + slug: string; + temporaryRange: string | null | undefined; + temporaryMode: string | null | undefined; + temporaryAccessEndTime: string | null | undefined; + temporaryAccessStartTime: string | null | undefined; + isTemporary: boolean; + createdAt: string; + }[]; createdAt: string; updatedAt: string; }; diff --git a/frontend/src/hooks/api/identityProjectAdditionalPrivilege/index.tsx b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/index.tsx new file mode 100644 index 000000000..401a06a72 --- /dev/null +++ b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/index.tsx @@ -0,0 +1,7 @@ +export { + useCreateIdentityProjectAdditionalPrivilege, + useDeleteIdentityProjectAdditionalPrivilege, + useUpdateIdentityProjectAdditionalPrivilege +} from "./mutation"; +export { useGetIdentityProjectPrivilegeDetails } from "./queries"; +export type { TIdentityProjectPrivilege } from "./types"; diff --git a/frontend/src/hooks/api/identityProjectAdditionalPrivilege/mutation.tsx b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/mutation.tsx new file mode 100644 index 000000000..420d29485 --- /dev/null +++ b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/mutation.tsx @@ -0,0 +1,74 @@ +import { packRules } from "@casl/ability/extra"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +import { workspaceKeys } from "../workspace/queries"; +import { + TCreateIdentityProjectPrivilegeDTO, + TDeleteIdentityProjectPrivilegeDTO, + TIdentityProjectPrivilege, + TUpdateIdentityProjectPrivlegeDTO +} from "./types"; + +export const useCreateIdentityProjectAdditionalPrivilege = () => { + const queryClient = useQueryClient(); + + return useMutation< + { privilege: TIdentityProjectPrivilege }, + {}, + TCreateIdentityProjectPrivilegeDTO + >({ + mutationFn: async (dto) => { + const { data } = await apiRequest.post("/api/v1/additional-privilege/identity", { + ...dto, + permissions: packRules(dto.permissions) + }); + return data.privilege; + }, + onSuccess: (_, { projectId }) => { + queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(projectId)); + } + }); +}; + +export const useUpdateIdentityProjectAdditionalPrivilege = () => { + const queryClient = useQueryClient(); + + return useMutation< + { privilege: TIdentityProjectPrivilege }, + {}, + TUpdateIdentityProjectPrivlegeDTO + >({ + mutationFn: async (dto) => { + const { data } = await apiRequest.patch( + `/api/v1/additional-privilege/identity/${dto.privilegeId}`, + { ...dto, permissions: dto.permissions ? packRules(dto.permissions) : undefined } + ); + return data.privilege; + }, + onSuccess: (_, { projectId }) => { + queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(projectId)); + } + }); +}; + +export const useDeleteIdentityProjectAdditionalPrivilege = () => { + const queryClient = useQueryClient(); + + return useMutation< + { privilege: TIdentityProjectPrivilege }, + {}, + TDeleteIdentityProjectPrivilegeDTO + >({ + mutationFn: async (dto) => { + const { data } = await apiRequest.delete( + `/api/v1/additional-privilege/identity/${dto.privilegeId}` + ); + return data.privilege; + }, + onSuccess: (_, { projectId }) => { + queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(projectId)); + } + }); +}; diff --git a/frontend/src/hooks/api/identityProjectAdditionalPrivilege/queries.tsx b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/queries.tsx new file mode 100644 index 000000000..2bbd71c70 --- /dev/null +++ b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/queries.tsx @@ -0,0 +1,31 @@ +import { PackRule, unpackRules } from "@casl/ability/extra"; +import { useQuery } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +import { TProjectPermission } from "../roles/types"; +import { TIdentityProjectPrivilege } from "./types"; + +export const identitiyProjectPrivilegeKeys = { + details: (privilegeId: string) => ["project-user-privilege", { privilegeId }] as const +}; + +const fetchIdentityProjectPrivilegeDetails = async (privilegeId: string) => { + const { + data: { privilege } + } = await apiRequest.get<{ + privilege: Omit & { permissions: unknown }; + }>(`/api/v1/additional-privilege/identity/${privilegeId}`); + return { + ...privilege, + permissions: unpackRules(privilege.permissions as PackRule[]) + }; +}; + +export const useGetIdentityProjectPrivilegeDetails = (privilegeId: string) => { + return useQuery({ + enabled: Boolean(privilegeId), + queryKey: identitiyProjectPrivilegeKeys.details(privilegeId), + queryFn: () => fetchIdentityProjectPrivilegeDetails(privilegeId) + }); +}; diff --git a/frontend/src/hooks/api/identityProjectAdditionalPrivilege/types.tsx b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/types.tsx new file mode 100644 index 000000000..3362e3301 --- /dev/null +++ b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/types.tsx @@ -0,0 +1,48 @@ +import { TProjectPermission } from "../roles/types"; + +export enum IdentityProjectAdditionalPrivilegeTemporaryMode { + Relative = "relative" +} + +export type TIdentityProjectPrivilege = { + projectMembershipId: string; + slug: string; + name: string; + isTemporary: boolean; + id: string; + createdAt: Date; + updatedAt: Date; + description?: string | null | undefined; + temporaryMode?: string | null | undefined; + temporaryRange?: string | null | undefined; + temporaryAccessStartTime?: string | null | undefined; + temporaryAccessEndTime?: Date | null | undefined; + permissions?: TProjectPermission[]; +}; + +export type TCreateIdentityProjectPrivilegeDTO = { + identityId: string; + projectId: string; + slug: string; + name: string; + description?: string; + isTemporary?: boolean; + temporaryMode?: IdentityProjectAdditionalPrivilegeTemporaryMode; + temporaryRange?: string; + temporaryAccessStartTime?: string; + permissions: TProjectPermission[]; +}; + +export type TUpdateIdentityProjectPrivlegeDTO = { + privilegeId: string; + projectId: string; +} & Partial>; + +export type TDeleteIdentityProjectPrivilegeDTO = { + privilegeId: string; + projectId: string; +}; + +export type TGetIdentityProejctPrivilegeDetails = { + privilegeId: string; +}; diff --git a/frontend/src/hooks/api/index.tsx b/frontend/src/hooks/api/index.tsx index 8ea1985df..ed89b51ba 100644 --- a/frontend/src/hooks/api/index.tsx +++ b/frontend/src/hooks/api/index.tsx @@ -6,6 +6,7 @@ export * from "./bots"; export * from "./dynamicSecret"; export * from "./dynamicSecretLease"; export * from "./identities"; +export * from "./identityProjectAdditionalPrivilege"; export * from "./incidentContacts"; export * from "./integrationAuth"; export * from "./integrations"; diff --git a/frontend/src/views/Project/MembersPage/components/AdditionalPrivilegeSection/AdditionalPrivilegeForm.tsx b/frontend/src/views/Project/MembersPage/components/AdditionalPrivilegeSection/AdditionalPrivilegeForm.tsx index c0d649942..06d4f4b71 100644 --- a/frontend/src/views/Project/MembersPage/components/AdditionalPrivilegeSection/AdditionalPrivilegeForm.tsx +++ b/frontend/src/views/Project/MembersPage/components/AdditionalPrivilegeSection/AdditionalPrivilegeForm.tsx @@ -22,8 +22,11 @@ import { useNotificationContext } from "@app/components/context/Notifications/No import { Button, FormControl, Input } from "@app/components/v2"; import { ProjectPermissionSub } from "@app/context"; import { + useCreateIdentityProjectAdditionalPrivilege, useCreateProjectUserAdditionalPrivilege, + useGetIdentityProjectPrivilegeDetails, useGetProjectUserPrivilegeDetails, + useUpdateIdentityProjectAdditionalPrivilege, useUpdateProjectUserAdditionalPrivilege } from "@app/hooks/api"; @@ -32,7 +35,6 @@ import { formRolePermission2API, formSchema, rolePermission2Form, - // rolePermission2Form, TFormSchema } from "../ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils"; import { SecretRollbackPermission } from "../ProjectRoleListTab/components/ProjectRoleModifySection/SecretRollbackPermission"; @@ -124,14 +126,26 @@ type Props = { actorId: string; }; -export const AdditionalPrivilegeForm = ({ onGoBack, privilegeId, actorId, workspaceId }: Props) => { +export const AdditionalPrivilegeForm = ({ + onGoBack, + privilegeId, + actorId, + workspaceId, + isIdentity +}: Props) => { const { createNotification } = useNotificationContext(); const isNewRole = !privilegeId; const { data: projectUserPrivilegeDetails } = useGetProjectUserPrivilegeDetails( - privilegeId || "" + privilegeId && !isIdentity ? privilegeId : "" ); + const { data: identityProjectPrivilegeDetails } = useGetIdentityProjectPrivilegeDetails( + isIdentity && privilegeId ? privilegeId : "" + ); + + const privileges = isIdentity ? identityProjectPrivilegeDetails : projectUserPrivilegeDetails; + const { handleSubmit, register, @@ -141,24 +155,36 @@ export const AdditionalPrivilegeForm = ({ onGoBack, privilegeId, actorId, worksp control } = useForm({ resolver: zodResolver(formSchema), - values: projectUserPrivilegeDetails && { - ...projectUserPrivilegeDetails, - description: projectUserPrivilegeDetails.description || "", - permissions: rolePermission2Form(projectUserPrivilegeDetails.permissions) + values: privileges && { + ...privileges, + description: privileges.description || "", + permissions: rolePermission2Form(privileges.permissions) } }); const createProjectUserAdditionalPrivilege = useCreateProjectUserAdditionalPrivilege(); const updateProjectUserAdditionalPrivilege = useUpdateProjectUserAdditionalPrivilege(); + const createIdentityProjectAdditionalPrivilege = useCreateIdentityProjectAdditionalPrivilege(); + const updateIdentityProjectAdditionalPrivilege = useUpdateIdentityProjectAdditionalPrivilege(); + const handleRoleUpdate = async (el: TFormSchema) => { try { - await updateProjectUserAdditionalPrivilege.mutateAsync({ - ...el, - permissions: formRolePermission2API(el.permissions), - privilegeId: privilegeId as string, - workspaceId - }); + if (isIdentity) { + await updateIdentityProjectAdditionalPrivilege.mutateAsync({ + ...el, + permissions: formRolePermission2API(el.permissions), + privilegeId: privilegeId as string, + projectId: workspaceId + }); + } else { + await updateProjectUserAdditionalPrivilege.mutateAsync({ + ...el, + permissions: formRolePermission2API(el.permissions), + privilegeId: privilegeId as string, + workspaceId + }); + } createNotification({ type: "success", text: "Successfully update privilege" }); onGoBack(); } catch (err) { @@ -174,12 +200,21 @@ export const AdditionalPrivilegeForm = ({ onGoBack, privilegeId, actorId, worksp } try { - await createProjectUserAdditionalPrivilege.mutateAsync({ - ...el, - permissions: formRolePermission2API(el.permissions), - projectMembershipId: actorId, - workspaceId - }); + if (isIdentity) { + await createIdentityProjectAdditionalPrivilege.mutateAsync({ + ...el, + permissions: formRolePermission2API(el.permissions), + identityId: actorId, + projectId: workspaceId + }); + } else { + await createProjectUserAdditionalPrivilege.mutateAsync({ + ...el, + permissions: formRolePermission2API(el.permissions), + projectMembershipId: actorId, + workspaceId + }); + } createNotification({ type: "success", text: "Created new privilege" }); onGoBack(); } catch (err) { diff --git a/frontend/src/views/Project/MembersPage/components/AdditionalPrivilegeSection/AdditionalPrivilegeSection.tsx b/frontend/src/views/Project/MembersPage/components/AdditionalPrivilegeSection/AdditionalPrivilegeSection.tsx index c13af0b98..75869761f 100644 --- a/frontend/src/views/Project/MembersPage/components/AdditionalPrivilegeSection/AdditionalPrivilegeSection.tsx +++ b/frontend/src/views/Project/MembersPage/components/AdditionalPrivilegeSection/AdditionalPrivilegeSection.tsx @@ -19,7 +19,10 @@ import { } from "@app/components/v2"; import { useWorkspace } from "@app/context"; import { usePopUp } from "@app/hooks"; -import { useDeleteProjectUserAdditionalPrivilege } from "@app/hooks/api"; +import { + useDeleteIdentityProjectAdditionalPrivilege, + useDeleteProjectUserAdditionalPrivilege +} from "@app/hooks/api"; import { TWorkspaceUser } from "@app/hooks/api/types"; import { AdditionalPrivilegeForm } from "./AdditionalPrivilegeForm"; @@ -28,40 +31,51 @@ import { AdditionalPrivilegeTemporaryAccess } from "./AdditionalPrivilegeTempora type Props = { onGoBack: VoidFunction; name: string; - projectMembershipId: string; + isIdentity?: boolean; + // isIdentity id - identity id else projectMembershipId + actorId: string; privileges: TWorkspaceUser["additionalPrivileges"]; }; export const AdditionalPrivilegeSection = ({ onGoBack, privileges = [], - projectMembershipId, - name + actorId, + name, + isIdentity }: Props) => { const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([ "modifyPrivilege", "deletePrivilege" ] as const); const { createNotification } = useNotificationContext(); - const deleteProjectUserAdditionalPrivilege = useDeleteProjectUserAdditionalPrivilege(); const { currentWorkspace } = useWorkspace(); const workspaceId = currentWorkspace?.id || ""; + const deleteProjectUserAdditionalPrivilege = useDeleteProjectUserAdditionalPrivilege(); + const deleteProjectIdentityAdditionalPrivilege = useDeleteIdentityProjectAdditionalPrivilege(); const onPrivilegeDelete = async (privilegeId: string) => { try { - await deleteProjectUserAdditionalPrivilege.mutateAsync({ - privilegeId, - workspaceId - }); + if (isIdentity) { + await deleteProjectIdentityAdditionalPrivilege.mutateAsync({ + privilegeId, + projectId: workspaceId + }); + } else { + await deleteProjectUserAdditionalPrivilege.mutateAsync({ + privilegeId, + workspaceId + }); + } handlePopUpClose("deletePrivilege"); createNotification({ type: "success", - text: "Successfully removed user privilege" + text: "Successfully removed privilege" }); } catch (err) { createNotification({ type: "error", - text: "Failed to delete user privilege" + text: "Failed to delete privilege" }); } }; @@ -83,7 +97,8 @@ export const AdditionalPrivilegeSection = ({ onGoBack={() => handlePopUpClose("modifyPrivilege")} privilegeId={privilegeDetails?.id} workspaceId={workspaceId} - actorId={projectMembershipId} + isIdentity={isIdentity} + actorId={actorId} /> ); @@ -119,7 +134,11 @@ export const AdditionalPrivilegeSection = ({
{privileges.length === 0 && ( - + )} {privileges.map(({ id, name: privilegeName, description, slug, ...dto }) => (
; type TTemporaryRoleFormProps = { privilegeId: string; workspaceId: string; + isIdentity?: boolean; temporaryConfig?: { isTemporary?: boolean; temporaryAccessEndTime?: string | null; @@ -41,7 +46,8 @@ type TTemporaryRoleFormProps = { export const AdditionalPrivilegeTemporaryAccess = ({ temporaryConfig: defaultValues = {}, workspaceId, - privilegeId + privilegeId, + isIdentity }: TTemporaryRoleFormProps) => { const { popUp, handlePopUpToggle } = usePopUp(["setTempRole"] as const); const { createNotification } = useNotificationContext(); @@ -56,17 +62,29 @@ export const AdditionalPrivilegeTemporaryAccess = ({ isTemporaryFieldValue && new Date() > new Date(defaultValues.temporaryAccessEndTime || ""); const updateProjectUserAdditionalPrivilege = useUpdateProjectUserAdditionalPrivilege(); + const updateProjectIdentityAdditionalPrivilege = useUpdateIdentityProjectAdditionalPrivilege(); const handleGrantTemporaryAccess = async (el: TTemporaryRoleFormSchema) => { try { - await updateProjectUserAdditionalPrivilege.mutateAsync({ - privilegeId: privilegeId as string, - workspaceId, - isTemporary: true, - temporaryRange: el.temporaryRange, - temporaryAccessStartTime: new Date().toISOString(), - temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative - }); + if (isIdentity) { + await updateProjectIdentityAdditionalPrivilege.mutateAsync({ + privilegeId: privilegeId as string, + projectId: workspaceId, + isTemporary: true, + temporaryRange: el.temporaryRange, + temporaryAccessStartTime: new Date().toISOString(), + temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative + }); + } else { + await updateProjectUserAdditionalPrivilege.mutateAsync({ + privilegeId: privilegeId as string, + workspaceId, + isTemporary: true, + temporaryRange: el.temporaryRange, + temporaryAccessStartTime: new Date().toISOString(), + temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative + }); + } createNotification({ type: "success", text: "Successfully updated access" }); handlePopUpToggle("setTempRole"); } catch (err) { @@ -77,11 +95,19 @@ export const AdditionalPrivilegeTemporaryAccess = ({ const handleRevokeTemporaryAccess = async () => { try { - await updateProjectUserAdditionalPrivilege.mutateAsync({ - privilegeId: privilegeId as string, - workspaceId, - isTemporary: false - }); + if (isIdentity) { + await updateProjectIdentityAdditionalPrivilege.mutateAsync({ + privilegeId: privilegeId as string, + projectId: workspaceId, + isTemporary: false + }); + } else { + await updateProjectUserAdditionalPrivilege.mutateAsync({ + privilegeId: privilegeId as string, + workspaceId, + isTemporary: false + }); + } createNotification({ type: "success", text: "Successfully updated access" }); handlePopUpToggle("setTempRole"); } catch (err) { diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/IdentityTab.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/IdentityTab.tsx index 58397d96a..3aa301583 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/IdentityTab.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/IdentityTab.tsx @@ -1,17 +1,270 @@ +import Link from "next/link"; +import { + faArrowUpRightFromSquare, + faPlus, + faServer, + faUserShield, + faXmark +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { format } from "date-fns"; import { motion } from "framer-motion"; +import { twMerge } from "tailwind-merge"; -import { IdentitySection } from "./components"; +import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; +import { ProjectPermissionCan } from "@app/components/permissions"; +import { + Button, + DeleteActionModal, + EmptyState, + IconButton, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tr +} from "@app/components/v2"; +import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; +import { withProjectPermission } from "@app/hoc"; +import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api"; +import { usePopUp } from "@app/hooks/usePopUp"; -export const IdentityTab = () => { - return ( - - - - ); -}; +import { AdditionalPrivilegeSection } from "../AdditionalPrivilegeSection"; +import { IdentityModal } from "./components/IdentityModal"; +import { IdentityRoles } from "./components/IdentityRoles"; + +export const IdentityTab = withProjectPermission( + () => { + const { createNotification } = useNotificationContext(); + const { currentWorkspace } = useWorkspace(); + + const workspaceId = currentWorkspace?.id ?? ""; + + const { data, isLoading } = useGetWorkspaceIdentityMemberships(currentWorkspace?.id || ""); + const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace(); + + const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + "identity", + "deleteIdentity", + "upgradePlan", + "additionalPrivilege" + ] as const); + + const onRemoveIdentitySubmit = async (identityId: string) => { + try { + await deleteMutateAsync({ + identityId, + workspaceId + }); + + createNotification({ + text: "Successfully removed identity from project", + type: "success" + }); + + handlePopUpClose("deleteIdentity"); + } catch (err) { + console.error(err); + const error = err as any; + const text = error?.response?.data?.message ?? "Failed to remove identity from project"; + + createNotification({ + text, + type: "error" + }); + } + }; + + if (popUp.additionalPrivilege.isOpen) { + const privilegeDetails = popUp?.additionalPrivilege?.data as { + name: string; + index: number; + identityId: string; + }; + + return ( + + handlePopUpClose("additionalPrivilege")} + privileges={data?.[privilegeDetails.index]?.additionalPrivileges || []} + name={privilegeDetails.name} + actorId={privilegeDetails.identityId} + /> + + ); + } + + return ( + +
+
+

Identities

+
+ + + Documentation{" "} + + + +
+ + {(isAllowed) => ( + + )} + +
+ + + + + + + + + + + {isLoading && } + {!isLoading && + data && + data.length > 0 && + data.map( + ({ identity: { id, name }, roles, createdAt, additionalPrivileges }, index) => { + const hasAdditionalPrivilege = Boolean(additionalPrivileges.length); + return ( + + + + + + + ); + } + )} + {!isLoading && data && data?.length === 0 && ( + + + + )} + +
NameRoleAdded on +
{name} + + {(isAllowed) => ( + + )} + + {format(new Date(createdAt), "yyyy-MM-dd")} +
+ + {(isAllowed) => ( + + handlePopUpOpen("additionalPrivilege", { + name, + index, + identityId: id + }) + } + > + + + )} + + + {(isAllowed) => ( + { + handlePopUpOpen("deleteIdentity", { + identityId: id, + name + }); + }} + size="lg" + colorSchema="danger" + variant="plain" + ariaLabel="update" + className="ml-4" + isDisabled={!isAllowed} + > + + + )} + +
+
+ +
+
+ + + handlePopUpToggle("deleteIdentity", isOpen)} + deleteKey="confirm" + onDeleteApproved={() => + onRemoveIdentitySubmit( + (popUp?.deleteIdentity?.data as { identityId: string })?.identityId + ) + } + /> +
+
+ ); + }, + { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Identity } +); diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityModal.tsx similarity index 100% rename from frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx rename to frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityModal.tsx diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityRoles.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoles.tsx similarity index 100% rename from frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityRoles.tsx rename to frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoles.tsx diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentitySection.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentitySection.tsx deleted file mode 100644 index 4410bc639..000000000 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentitySection.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import Link from "next/link"; -import { faArrowUpRightFromSquare, faPlus } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -import { createNotification } from "@app/components/notifications"; -import { ProjectPermissionCan } from "@app/components/permissions"; -import { Button, DeleteActionModal } from "@app/components/v2"; -import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; -import { withProjectPermission } from "@app/hoc"; -import { useDeleteIdentityFromWorkspace } from "@app/hooks/api"; -import { usePopUp } from "@app/hooks/usePopUp"; - -import { IdentityModal } from "./IdentityModal"; -import { IdentityTable } from "./IdentityTable"; - -export const IdentitySection = withProjectPermission( - () => { - - const { currentWorkspace } = useWorkspace(); - - const workspaceId = currentWorkspace?.id ?? ""; - - const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace(); - - const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - "identity", - "deleteIdentity", - "upgradePlan" - ] as const); - - const onRemoveIdentitySubmit = async (identityId: string) => { - try { - await deleteMutateAsync({ - identityId, - workspaceId - }); - - createNotification({ - text: "Successfully removed identity from project", - type: "success" - }); - - handlePopUpClose("deleteIdentity"); - } catch (err) { - console.error(err); - const error = err as any; - const text = error?.response?.data?.message ?? "Failed to remove identity from project"; - - createNotification({ - text, - type: "error" - }); - } - }; - - return ( -
-
-

Identities

-
- - - Documentation{" "} - - - -
- - {(isAllowed) => ( - - )} - -
- - - handlePopUpToggle("deleteIdentity", isOpen)} - deleteKey="confirm" - onDeleteApproved={() => - onRemoveIdentitySubmit( - (popUp?.deleteIdentity?.data as { identityId: string })?.identityId - ) - } - /> -
- ); - }, - { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Identity } -); diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx deleted file mode 100644 index 24197e9fe..000000000 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { faServer, faXmark } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { format } from "date-fns"; - -import { ProjectPermissionCan } from "@app/components/permissions"; -import { - EmptyState, - IconButton, - Table, - TableContainer, - TableSkeleton, - TBody, - Td, - Th, - THead, - Tr -} from "@app/components/v2"; -import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; -import { useGetWorkspaceIdentityMemberships } from "@app/hooks/api"; -import { UsePopUpState } from "@app/hooks/usePopUp"; - -import { IdentityRoles } from "./IdentityRoles"; - -type Props = { - handlePopUpOpen: ( - popUpName: keyof UsePopUpState<["deleteIdentity", "identity"]>, - data?: { - identityId?: string; - name?: string; - } - ) => void; -}; - -export const IdentityTable = ({ handlePopUpOpen }: Props) => { - const { currentWorkspace } = useWorkspace(); - const { data, isLoading } = useGetWorkspaceIdentityMemberships(currentWorkspace?.id || ""); - - return ( - - - - - - - - - - - {isLoading && } - {!isLoading && - data && - data.length > 0 && - data.map(({ identity: { id, name }, roles, createdAt }) => { - return ( - - - - - - - ); - })} - {!isLoading && data && data?.length === 0 && ( - - - - )} - -
NameRoleAdded on -
{name} - - {(isAllowed) => ( - - )} - - {format(new Date(createdAt), "yyyy-MM-dd")} - - {(isAllowed) => ( - { - handlePopUpOpen("deleteIdentity", { - identityId: id, - name - }); - }} - size="lg" - colorSchema="danger" - variant="plain" - ariaLabel="update" - className="ml-4" - isDisabled={!isAllowed} - > - - - )} - -
- -
-
- ); -}; diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/index.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/index.tsx deleted file mode 100644 index f0663a6a5..000000000 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { IdentitySection } from "./IdentitySection"; diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/index.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/index.tsx deleted file mode 100644 index f0663a6a5..000000000 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { IdentitySection } from "./IdentitySection"; diff --git a/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx b/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx index fdf5a7309..b114aa17b 100644 --- a/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx +++ b/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx @@ -215,7 +215,7 @@ export const MemberListTab = () => { onGoBack={() => handlePopUpClose("additionalPrivilege")} privileges={members?.[privilegeDetails.index]?.additionalPrivileges || []} name={privilegeDetails.name} - projectMembershipId={privilegeDetails.projectMembershipId} + actorId={privilegeDetails.projectMembershipId} /> );