Disable activation on scim profiles

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-02-18 17:14:43 +04:00
parent 13aef29ddf
commit 9941a6a66a
12 changed files with 163 additions and 287 deletions

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<c5c251783bc35aea15dec0c06f6ef1fb>>
* @generated SignedSource<<a32e2b4330ac1d54762817127289b51d>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -301,6 +301,13 @@ return {
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "emailAddress",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -333,25 +340,6 @@ return {
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "identity",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "email",
"storageKey": null
},
(v12/*: any*/)
],
"storageKey": null
},
{
"alias": "lastInvitation",
"args": (v14/*: any*/),
@@ -377,20 +365,6 @@ return {
"plural": false,
"selections": [
(v12/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "expiresAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "acceptedAt",
"storageKey": null
},
(v15/*: any*/),
(v11/*: any*/)
],
@@ -524,12 +498,12 @@ return {
]
},
"params": {
"cacheID": "f25ca8641478e79fb2ccf6fa825fe042",
"cacheID": "15a5dd79df1b16aa1a66ccb8024f9627",
"id": null,
"metadata": {},
"name": "PeopleListFragment_RefetchQuery",
"operationKind": "query",
"text": "query PeopleListFragment_RefetchQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: ProfileOrder = {direction: ASC, field: FULL_NAME}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PeopleListFragment_16fISc\n id\n }\n}\n\nfragment PeopleListFragment_16fISc on Organization {\n profiles(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n totalCount\n edges {\n node {\n id\n ...PeopleListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n source\n state\n fullName\n membership {\n id\n role\n canUpdate: permission(action: \"iam:membership:update\")\n }\n identity {\n email\n id\n }\n lastInvitation: pendingInvitations(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) {\n edges {\n node {\n id\n expiresAt\n acceptedAt\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n canInvite: permission(action: \"iam:invitation:create\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n}\n"
"text": "query PeopleListFragment_RefetchQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: ProfileOrder = {direction: ASC, field: FULL_NAME}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PeopleListFragment_16fISc\n id\n }\n}\n\nfragment PeopleListFragment_16fISc on Organization {\n profiles(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n totalCount\n edges {\n node {\n id\n ...PeopleListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n source\n state\n fullName\n emailAddress\n membership {\n id\n role\n canUpdate: permission(action: \"iam:membership:update\")\n }\n lastInvitation: pendingInvitations(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) {\n edges {\n node {\n id\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n canInvite: permission(action: \"iam:invitation:create\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<91bec60a40a9170868272814635ea3fa>>
* @generated SignedSource<<f8383da9cb37af6caca48c69a809346c>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -17,18 +17,14 @@ export type PeopleListItemFragment$data = {
readonly canInvite: boolean;
readonly canUpdate: boolean;
readonly createdAt: string;
readonly emailAddress: string;
readonly fullName: string;
readonly id: string;
readonly identity: {
readonly email: string;
};
readonly lastInvitation: {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly acceptedAt: string | null | undefined;
readonly createdAt: string;
readonly expiresAt: string;
readonly id: string;
};
}>;
@@ -101,6 +97,13 @@ return {
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "emailAddress",
"storageKey": null
},
{
"kind": "RequiredField",
"field": {
@@ -137,28 +140,6 @@ return {
},
"action": "THROW"
},
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "identity",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "email",
"storageKey": null
}
],
"storageKey": null
},
"action": "THROW"
},
{
"kind": "RequiredField",
"field": {
@@ -195,20 +176,6 @@ return {
"plural": false,
"selections": [
(v0/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "expiresAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "acceptedAt",
"storageKey": null
},
(v1/*: any*/),
{
"alias": null,
@@ -318,6 +285,6 @@ return {
};
})();
(node as any).hash = "b276a06948260a9d89947b386c6392d1";
(node as any).hash = "f10aa3067eeb7fc95eb7c7a893e7dd6e";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<e32759f25b173edfe9c63fb659d7e75e>>
* @generated SignedSource<<86b86e1209fbe58b62c2be028fb0482c>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -266,6 +266,13 @@ return {
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "emailAddress",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -298,25 +305,6 @@ return {
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "identity",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "email",
"storageKey": null
},
(v6/*: any*/)
],
"storageKey": null
},
{
"alias": "lastInvitation",
"args": (v8/*: any*/),
@@ -342,20 +330,6 @@ return {
"plural": false,
"selections": [
(v6/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "expiresAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "acceptedAt",
"storageKey": null
},
(v9/*: any*/),
(v2/*: any*/)
],
@@ -489,12 +463,12 @@ return {
]
},
"params": {
"cacheID": "a5d551184b2abe39feea3dfb05bfe21d",
"cacheID": "af366ea24d2c96763b104acc69d9b3ff",
"id": null,
"metadata": {},
"name": "PeoplePageQuery",
"operationKind": "query",
"text": "query PeoplePageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n canCreateUser: permission(action: \"iam:membership-profile:create\")\n ...PeopleListFragment_8lnpd\n }\n id\n }\n}\n\nfragment PeopleListFragment_8lnpd on Organization {\n profiles(first: 20, orderBy: {direction: ASC, field: FULL_NAME}) {\n totalCount\n edges {\n node {\n id\n ...PeopleListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n source\n state\n fullName\n membership {\n id\n role\n canUpdate: permission(action: \"iam:membership:update\")\n }\n identity {\n email\n id\n }\n lastInvitation: pendingInvitations(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) {\n edges {\n node {\n id\n expiresAt\n acceptedAt\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n canInvite: permission(action: \"iam:invitation:create\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n}\n"
"text": "query PeoplePageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n canCreateUser: permission(action: \"iam:membership-profile:create\")\n ...PeopleListFragment_8lnpd\n }\n id\n }\n}\n\nfragment PeopleListFragment_8lnpd on Organization {\n profiles(first: 20, orderBy: {direction: ASC, field: FULL_NAME}) {\n totalCount\n edges {\n node {\n id\n ...PeopleListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n source\n state\n fullName\n emailAddress\n membership {\n id\n role\n canUpdate: permission(action: \"iam:membership:update\")\n }\n lastInvitation: pendingInvitations(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) {\n edges {\n node {\n id\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n canInvite: permission(action: \"iam:invitation:create\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<a8e224db1200fa5aaa6edc17c09e7a35>>
* @generated SignedSource<<1eea90ca68f7381b881c68f0489837cb>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -25,6 +25,7 @@ export type PersonFormFragment$data = {
readonly role: MembershipRole;
};
readonly position: string | null | undefined;
readonly source: string;
readonly " $fragmentType": "PersonFormFragment";
};
export type PersonFormFragment$key = {
@@ -59,6 +60,13 @@ const node: ReaderFragment = {
"name": "emailAddress",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "source",
"storageKey": null
},
{
"kind": "RequiredField",
"field": {
@@ -134,6 +142,6 @@ const node: ReaderFragment = {
"abstractKey": null
};
(node as any).hash = "4f99d9b9ed708290c5a7d7ec258a0b1d";
(node as any).hash = "37e8a56333b217e5b6d3b94b455d7d40";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<85bf3907699ac605e0603c8d1ae6ef8b>>
* @generated SignedSource<<7889e38e185230a2c5b41c3b0fb73cca>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -195,6 +195,13 @@ return {
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "emailAddress",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -227,25 +234,6 @@ return {
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "identity",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "email",
"storageKey": null
},
(v3/*: any*/)
],
"storageKey": null
},
{
"alias": "lastInvitation",
"args": (v4/*: any*/),
@@ -271,20 +259,6 @@ return {
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "expiresAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "acceptedAt",
"storageKey": null
},
(v5/*: any*/),
{
"alias": null,
@@ -425,12 +399,12 @@ return {
]
},
"params": {
"cacheID": "afbd8ffc5b5ae7156a108a2d1b25440b",
"cacheID": "738254d707f8ed725c30ee87baa585fb",
"id": null,
"metadata": {},
"name": "PersonForm_createMutation",
"operationKind": "mutation",
"text": "mutation PersonForm_createMutation(\n $input: CreateUserInput!\n) {\n createUser(input: $input) {\n profileEdge {\n node {\n ...PeopleListItemFragment\n id\n }\n }\n }\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n source\n state\n fullName\n membership {\n id\n role\n canUpdate: permission(action: \"iam:membership:update\")\n }\n identity {\n email\n id\n }\n lastInvitation: pendingInvitations(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) {\n edges {\n node {\n id\n expiresAt\n acceptedAt\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n canInvite: permission(action: \"iam:invitation:create\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n}\n"
"text": "mutation PersonForm_createMutation(\n $input: CreateUserInput!\n) {\n createUser(input: $input) {\n profileEdge {\n node {\n ...PeopleListItemFragment\n id\n }\n }\n }\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n source\n state\n fullName\n emailAddress\n membership {\n id\n role\n canUpdate: permission(action: \"iam:membership:update\")\n }\n lastInvitation: pendingInvitations(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) {\n edges {\n node {\n id\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n canInvite: permission(action: \"iam:invitation:create\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<679cd9698980b9fdf8b330df8df3aac5>>
* @generated SignedSource<<aa1188431214a8844e8ee1879bbada7e>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -17,11 +17,10 @@ export type PersonPageQuery$data = {
readonly person: {
readonly __typename: "Profile";
readonly canDelete: boolean;
readonly emailAddress: string;
readonly fullName: string;
readonly id: string;
readonly identity: {
readonly email: string;
};
readonly source: string;
readonly " $fragmentSpreads": FragmentRefs<"PersonFormFragment">;
} | {
// This will never be '%other', but we need some
@@ -74,10 +73,17 @@ v5 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "email",
"name": "emailAddress",
"storageKey": null
},
v6 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "source",
"storageKey": null
},
v7 = {
"alias": "canDelete",
"args": [
{
@@ -113,23 +119,9 @@ return {
"selections": [
(v3/*: any*/),
(v4/*: any*/),
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "identity",
"plural": false,
"selections": [
(v5/*: any*/)
],
"storageKey": null
},
"action": "THROW"
},
(v5/*: any*/),
(v6/*: any*/),
(v7/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
@@ -168,27 +160,9 @@ return {
"kind": "InlineFragment",
"selections": [
(v4/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "identity",
"plural": false,
"selections": [
(v5/*: any*/),
(v3/*: any*/)
],
"storageKey": null
},
(v5/*: any*/),
(v6/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "emailAddress",
"storageKey": null
},
(v7/*: any*/),
{
"alias": null,
"args": null,
@@ -266,16 +240,16 @@ return {
]
},
"params": {
"cacheID": "cb1cec2fd7c66b42f61d40e6a22b0d67",
"cacheID": "f78728c7deb19de68b5b996c58ebd05e",
"id": null,
"metadata": {},
"name": "PersonPageQuery",
"operationKind": "query",
"text": "query PersonPageQuery(\n $personId: ID!\n) {\n person: node(id: $personId) {\n __typename\n ... on Profile {\n id\n fullName\n identity {\n email\n id\n }\n canDelete: permission(action: \"iam:membership-profile:delete\")\n ...PersonFormFragment\n }\n id\n }\n}\n\nfragment PersonFormFragment on Profile {\n id\n fullName\n emailAddress\n membership {\n role\n id\n }\n kind\n position\n additionalEmailAddresses\n contractStartDate\n contractEndDate\n canUpdate: permission(action: \"iam:membership-profile:update\")\n}\n"
"text": "query PersonPageQuery(\n $personId: ID!\n) {\n person: node(id: $personId) {\n __typename\n ... on Profile {\n id\n fullName\n emailAddress\n source\n canDelete: permission(action: \"iam:membership-profile:delete\")\n ...PersonFormFragment\n }\n id\n }\n}\n\nfragment PersonFormFragment on Profile {\n id\n fullName\n emailAddress\n source\n membership {\n role\n id\n }\n kind\n position\n additionalEmailAddresses\n contractStartDate\n contractEndDate\n canUpdate: permission(action: \"iam:membership-profile:update\")\n}\n"
}
};
})();
(node as any).hash = "c2954bc62ed4a3e3a9b1ca9596b68e1c";
(node as any).hash = "4aecfb7a9ff05a585ccc6b533fb75bec";
export default node;

View File

@@ -1,6 +1,6 @@
import { sprintf } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { ActionDropdown, Avatar, Breadcrumb, Card, DropdownItem, IconTrashCan, useConfirm } from "@probo/ui";
import { ActionDropdown, Avatar, Badge, Breadcrumb, Card, DropdownItem, IconTrashCan, useConfirm } from "@probo/ui";
import { type PreloadedQuery, usePreloadedQuery } from "react-relay";
import { useNavigate } from "react-router";
import { graphql } from "relay-runtime";
@@ -18,9 +18,8 @@ export const personPageQuery = graphql`
... on Profile {
id
fullName
identity @required(action: THROW) {
email
}
emailAddress
source
canDelete: permission(action: "iam:membership-profile:delete")
...PersonFormFragment
}
@@ -100,9 +99,11 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
<div className="flex items-center gap-6">
<Avatar name={person.fullName} size="xl" />
<div>
<div className="text-2xl">{person.fullName}</div>
<div className="text-lg text-txt-secondary">{person.identity.email}</div>
<div className="flex items-center gap-2">
<span className="text-2xl">{person.fullName}</span>
<Badge variant="info">{person.source}</Badge>
</div>
<div className="text-lg text-txt-secondary">{person.emailAddress}</div>
</div>
</div>
{person.canDelete && (

View File

@@ -29,14 +29,12 @@ const fragment = graphql`
source
state
fullName
emailAddress
membership @required(action: THROW) {
id
role
canUpdate: permission(action: "iam:membership:update")
}
identity @required(action: THROW) {
email
}
lastInvitation: pendingInvitations(first: 1, orderBy: { field: CREATED_AT, direction: DESC })
@required(action: THROW)
@connection(key: "PeopleListItem_lastInvitation") {
@@ -44,8 +42,6 @@ const fragment = graphql`
edges {
node {
id
expiresAt
acceptedAt
createdAt
}
}
@@ -193,87 +189,85 @@ export function PeopleListItem(props: {
};
return (
<>
<Tr to={`/organizations/${organizationId}/people/${profile.id}`}>
<Td className={clsx(
<Tr to={`/organizations/${organizationId}/people/${profile.id}`}>
<Td className={clsx(
isRemoving && "opacity-60 pointer-events-none",
isInactive && "opacity-50",
)}
>
<span className="font-semibold">{profile.fullName}</span>
</Td>
<Td>
<Badge variant={profile.state === "INACTIVE" ? "neutral" : "success"}>{profile.state}</Badge>
</Td>
<Td className={clsx(
isRemoving && "opacity-60 pointer-events-none",
isInactive && "opacity-50",
)}
>
<div className="flex items-center gap-2">
{profile.emailAddress}
<Badge variant="info">{profile.source}</Badge>
</div>
</Td>
<Td
noLink
className={clsx(
"pr-4",
isRemoving && "opacity-60 pointer-events-none",
isInactive && "opacity-50",
)}
>
<Select
disabled={!profile.membership.canUpdate || isUpdatingRole}
value={profile.membership.role}
onValueChange={role => void handleUpdateRole(role)}
>
<span className="font-semibold">{profile.fullName}</span>
</Td>
<Td>
<Badge variant={profile.state === "INACTIVE" ? "neutral" : "success"}>{profile.state}</Badge>
</Td>
<Td className={clsx(
isRemoving && "opacity-60 pointer-events-none",
isInactive && "opacity-50",
)}
>
<div className="flex items-center gap-2">
{profile.identity.email}
<Badge variant="info">{profile.source}</Badge>
</div>
</Td>
<Td
noLink
className={clsx(
"pr-4",
isRemoving && "opacity-60 pointer-events-none",
isInactive && "opacity-50",
{availableRoles.includes("OWNER") && (
<Option value="OWNER">{__("Owner")}</Option>
)}
>
<Select
disabled={!profile.membership.canUpdate || isUpdatingRole}
value={profile.membership.role}
onValueChange={role => void handleUpdateRole(role)}
>
{availableRoles.includes("OWNER") && (
<Option value="OWNER">{__("Owner")}</Option>
)}
{availableRoles.includes("ADMIN") && (
<Option value="ADMIN">{__("Admin")}</Option>
)}
{availableRoles.includes("VIEWER") && (
<Option value="VIEWER">{__("Viewer")}</Option>
)}
{availableRoles.includes("AUDITOR") && (
<Option value="AUDITOR">{__("Auditor")}</Option>
)}
{availableRoles.includes("EMPLOYEE") && (
<Option value="EMPLOYEE">{__("Employee")}</Option>
)}
</Select>
</Td>
<Td className={clsx(
isRemoving && "opacity-60 pointer-events-none",
isInactive && "opacity-50",
)}
>
{new Date(profile.createdAt).toLocaleDateString()}
</Td>
<Td noLink width={160} className="text-end">
<ActionDropdown>
{isInactive && (
<DropdownItem
onClick={handleInvite}
icon={IconMail}
>
{lastInvitation ? __("Resend activation mail") : __("Send activation mail")}
</DropdownItem>
)}
{profile.canDelete && (
<DropdownItem
onClick={handleRemove}
variant="danger"
icon={IconTrashCan}
>
{__("Remove person")}
</DropdownItem>
)}
</ActionDropdown>
</Td>
</Tr>
</>
{availableRoles.includes("ADMIN") && (
<Option value="ADMIN">{__("Admin")}</Option>
)}
{availableRoles.includes("VIEWER") && (
<Option value="VIEWER">{__("Viewer")}</Option>
)}
{availableRoles.includes("AUDITOR") && (
<Option value="AUDITOR">{__("Auditor")}</Option>
)}
{availableRoles.includes("EMPLOYEE") && (
<Option value="EMPLOYEE">{__("Employee")}</Option>
)}
</Select>
</Td>
<Td className={clsx(
isRemoving && "opacity-60 pointer-events-none",
isInactive && "opacity-50",
)}
>
{new Date(profile.createdAt).toLocaleDateString()}
</Td>
<Td noLink width={160} className="text-end">
<ActionDropdown>
{isInactive && profile.source !== "SCIM" && profile.canInvite && (
<DropdownItem
onClick={handleInvite}
icon={IconMail}
>
{lastInvitation ? __("Resend activation mail") : __("Send activation mail")}
</DropdownItem>
)}
{profile.canDelete && profile.source !== "SCIM" && (
<DropdownItem
onClick={handleRemove}
variant="danger"
icon={IconTrashCan}
>
{__("Remove person")}
</DropdownItem>
)}
</ActionDropdown>
</Td>
</Tr>
);
}

View File

@@ -23,6 +23,7 @@ const fragment = graphql`
id
fullName
emailAddress
source
membership @required(action: THROW) {
role
}
@@ -268,7 +269,7 @@ export function PersonFormLoader(props: { fragmentRef: PersonFormFragment$key })
return (
<PersonForm
id={person.id}
disabled={!person.canUpdate}
disabled={!person.canUpdate || person.source === "SCIM"}
defaultValues={
{
kind: person.kind,