import { subject } from "@casl/ability"; import { faArrowDown, faArrowUp, faArrowUpRightFromSquare, faClock, faEllipsisV, faMagnifyingGlass, faPlus, faServer, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useNavigate } from "@tanstack/react-router"; import { format } from "date-fns"; import { motion } from "framer-motion"; import { twMerge } from "tailwind-merge"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; import { Button, DeleteActionModal, EmptyState, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, Input, Pagination, Spinner, Table, TableContainer, TableSkeleton, Tag, TBody, Td, Th, THead, Tooltip, Tr } from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { formatProjectRoleName } from "@app/helpers/roles"; import { getUserTablePreference, PreferenceKey, setUserTablePreference } from "@app/helpers/userTablePreferences"; import { withProjectPermission } from "@app/hoc"; import { usePagination, useResetPageHelper } from "@app/hooks"; import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api"; import { OrderByDirection } from "@app/hooks/api/generic/types"; import { ProjectIdentityOrderBy } from "@app/hooks/api/workspace/types"; import { usePopUp } from "@app/hooks/usePopUp"; import { IdentityModal } from "./components/IdentityModal"; const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2; export const IdentityTab = withProjectPermission( () => { const { currentWorkspace } = useWorkspace(); const navigate = useNavigate(); const { offset, limit, orderBy, setOrderBy, orderDirection, setOrderDirection, search, debouncedSearch, setPage, setSearch, perPage, page, setPerPage } = usePagination(ProjectIdentityOrderBy.Name, { initPerPage: getUserTablePreference("projectIdentityTable", PreferenceKey.PerPage, 20) }); const handlePerPageChange = (newPerPage: number) => { setPerPage(newPerPage); setUserTablePreference("projectIdentityTable", PreferenceKey.PerPage, newPerPage); }; const workspaceId = currentWorkspace?.id ?? ""; const { data, isPending, isFetching } = useGetWorkspaceIdentityMemberships( { workspaceId: currentWorkspace?.id || "", offset, limit, orderDirection, orderBy, search: debouncedSearch }, { placeholderData: (prevData) => prevData } ); const { totalCount = 0 } = data ?? {}; useResetPageHelper({ totalCount, offset, setPage }); 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" }); } }; const handleSort = (column: ProjectIdentityOrderBy) => { if (column === orderBy) { setOrderDirection((prev) => prev === OrderByDirection.ASC ? OrderByDirection.DESC : OrderByDirection.ASC ); return; } setOrderBy(column); setOrderDirection(OrderByDirection.ASC); }; return (

Identities

Documentation{" "}
{(isAllowed) => ( )}
setSearch(e.target.value)} leftIcon={} placeholder="Search identities by name..." /> {isPending && } {!isPending && data && data.identityMemberships.length > 0 && data.identityMemberships.map((identityMember) => { const { identity: { id, name }, roles, createdAt } = identityMember; return ( { if (evt.key === "Enter") { navigate({ to: `/${currentWorkspace?.type}/$projectId/identities/$identityId` as const, params: { projectId: currentWorkspace.id, identityId: id } }); } }} onClick={() => navigate({ to: `/${currentWorkspace?.type}/$projectId/identities/$identityId` as const, params: { projectId: currentWorkspace.id, identityId: id } }) } > ); })}
Name handleSort(ProjectIdentityOrderBy.Name)} >
Role Added on {isFetching ? : null}
{name}
{roles .slice(0, MAX_ROLES_TO_BE_SHOWN_IN_TABLE) .map( ({ role, customRoleName, id: roleId, isTemporary, temporaryAccessEndTime }) => { const isExpired = new Date() > new Date(temporaryAccessEndTime || ("" as string)); return (
{formatProjectRoleName(role, customRoleName)}
{isTemporary && (
)}
); } )} {roles.length > MAX_ROLES_TO_BE_SHOWN_IN_TABLE && ( +{roles.length - MAX_ROLES_TO_BE_SHOWN_IN_TABLE} {roles .slice(MAX_ROLES_TO_BE_SHOWN_IN_TABLE) .map( ({ role, customRoleName, id: roleId, isTemporary, temporaryAccessEndTime }) => { const isExpired = new Date() > new Date(temporaryAccessEndTime || ("" as string)); return (
{formatProjectRoleName(role, customRoleName)}
{isTemporary && (
new Date( temporaryAccessEndTime as string ) && "text-red-600" )} />
)}
); } )}
)}
{format(new Date(createdAt), "yyyy-MM-dd")} {(isAllowed) => ( { evt.stopPropagation(); evt.preventDefault(); handlePopUpOpen("deleteIdentity", { identityId: id, name }); }} size="lg" colorSchema="danger" variant="plain" ariaLabel="update" className="ml-4" isDisabled={!isAllowed} > )}
{!isPending && data && totalCount > 0 && ( setPage(newPage)} onChangePerPage={handlePerPageChange} /> )} {!isPending && data && data?.identityMemberships.length === 0 && ( 0 ? "No identities match search filter" : "No identities have been added to this project" } icon={faServer} /> )}
handlePopUpToggle("deleteIdentity", isOpen)} deleteKey="confirm" onDeleteApproved={() => onRemoveIdentitySubmit( (popUp?.deleteIdentity?.data as { identityId: string })?.identityId ) } />
); }, { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Identity } );