Replug invitations

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2025-12-27 00:37:15 +01:00
committed by Bryan Frimin
parent e0f6bbd204
commit 278411392d
21 changed files with 2084 additions and 237 deletions

View File

@@ -3,24 +3,39 @@ import { graphql } from "relay-runtime";
import { MemberList } from "./_components/MemberList"; import { MemberList } from "./_components/MemberList";
import type { MembersPageQuery } from "./__generated__/MembersPageQuery.graphql"; import type { MembersPageQuery } from "./__generated__/MembersPageQuery.graphql";
import { useTranslate } from "@probo/i18n"; import { useTranslate } from "@probo/i18n";
import { Card, TabBadge, TabItem, Tabs } from "@probo/ui"; import { Button, Card, TabBadge, TabItem, Tabs } from "@probo/ui";
import { useState } from "react"; import { useState } from "react";
import { InvitationList } from "./_components/InvitationList";
import { InviteUserDialog } from "./_components/InviteUserDialog";
export const membersPageQuery = graphql` export const membersPageQuery = graphql`
query MembersPageQuery($organizationId: ID!) { query MembersPageQuery($organizationId: ID!) {
viewer @required(action: THROW) { viewer @required(action: THROW) {
canInviteUser: permission(
action: "iam:membership:create"
id: $organizationId
)
...InvitationListItem_permissionsFragment
@arguments(organizationId: $organizationId)
...MemberListItem_permissionsFragment ...MemberListItem_permissionsFragment
@arguments(organizationId: $organizationId) @arguments(organizationId: $organizationId)
} }
organization: node(id: $organizationId) @required(action: THROW) { organization: node(id: $organizationId) @required(action: THROW) {
__typename __typename
... on Organization { ... on Organization {
...InviteUserDialog_currentRoleFragment
...MemberListFragment ...MemberListFragment
@arguments(first: 20, order: { direction: ASC, field: CREATED_AT }) @arguments(first: 20, order: { direction: ASC, field: CREATED_AT })
members(first: 20, orderBy: { direction: ASC, field: CREATED_AT }) members(first: 20, orderBy: { direction: ASC, field: CREATED_AT })
@required(action: THROW) { @required(action: THROW) {
totalCount totalCount
} }
...InvitationListFragment
@arguments(first: 20, order: { direction: ASC, field: CREATED_AT })
invitations(first: 20, orderBy: { direction: ASC, field: CREATED_AT })
@required(action: THROW) {
totalCount
}
} }
} }
} }
@@ -49,14 +64,11 @@ export function MembersPage(props: {
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h2 className="text-base font-medium">{__("Workspace members")}</h2> <h2 className="text-base font-medium">{__("Workspace members")}</h2>
{/* {isAuthorized("Organization", "inviteUser") && ( {viewer.canInviteUser && (
<InviteUserDialog <InviteUserDialog viewerMembershipFKey={organization}>
connectionId={invitationsPagination.data.invitations?.__id}
onRefetch={refetchInvitations}
>
<Button variant="secondary">{__("Invite member")}</Button> <Button variant="secondary">{__("Invite member")}</Button>
</InviteUserDialog> </InviteUserDialog>
)} */} )}
</div> </div>
<Tabs> <Tabs>
@@ -69,85 +81,26 @@ export function MembersPage(props: {
<TabBadge>{organization.members.totalCount}</TabBadge> <TabBadge>{organization.members.totalCount}</TabBadge>
)} )}
</TabItem> </TabItem>
{/* <TabItem <TabItem
active={activeTab === "invitations"} active={activeTab === "invitations"}
onClick={() => setActiveTab("invitations")} onClick={() => setActiveTab("invitations")}
> >
{__("Invitations")} {__("Invitations")}
{(invitationsPagination.data.invitations?.totalCount || 0) > 0 && ( {(organization.invitations.totalCount ?? 0) > 0 && (
<TabBadge> <TabBadge>{organization.invitations.totalCount}</TabBadge>
{invitationsPagination.data.invitations?.totalCount}
</TabBadge>
)} )}
</TabItem> */} </TabItem>
</Tabs> </Tabs>
<Card> <Card>
<div className="px-6 pb-6 pt-6"> <div className="px-6 pb-6 pt-6">
{activeTab === "memberships" && ( {activeTab === "memberships" && (
<MemberList fKey={organization} viewerFKey={viewer} /> <MemberList fKey={organization} permissionsFKey={viewer} />
)} )}
{/* {activeTab === "invitations" && ( {activeTab === "invitations" && (
<SortableTable <InvitationList fKey={organization} permissionsFKey={viewer} />
{...invitationsPagination}
refetch={({
order,
}: {
order: { direction: string; field: string };
}) => {
invitationsPagination.refetch({
order: {
direction: order.direction as "ASC" | "DESC",
field: order.field as
| "CREATED_AT"
| "EXPIRES_AT"
| "FULL_NAME"
| "EMAIL"
| "ROLE"
| "STATUS"
| "ACCEPTED_AT",
},
});
}}
pageSize={20}
>
<Thead>
<Tr>
<SortableTh field="FULL_NAME">{__("Name")}</SortableTh>
<SortableTh field="EMAIL">{__("Email")}</SortableTh>
<SortableTh field="ROLE">{__("Role")}</SortableTh>
<SortableTh field="CREATED_AT">{__("Invited")}</SortableTh>
<Th>{__("Status")}</Th>
<SortableTh field="ACCEPTED_AT">
{__("Accepted at")}
</SortableTh>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{invitations.length === 0 ? (
<Tr>
<Td colSpan={7} className="text-center text-txt-secondary">
{__("No invitations")}
</Td>
</Tr>
) : (
invitations.map((invitation) => (
<InvitationRow
key={invitation.id}
invitation={invitation}
connectionId={
invitationsPagination.data.invitations?.__id
}
organizationId={(organizationKey as { id: string }).id}
onRefetch={refetchInvitations}
/>
))
)} )}
</Tbody>
</SortableTable>
)} */}
</div> </div>
</Card> </Card>
</div> </div>

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<e16e82aa25ced17faf9e4c40f7e6130e>> * @generated SignedSource<<33faed6358cc7fba56a1f493c11f31d4>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -16,17 +16,21 @@ export type MembersPageQuery$variables = {
export type MembersPageQuery$data = { export type MembersPageQuery$data = {
readonly organization: { readonly organization: {
readonly __typename: "Organization"; readonly __typename: "Organization";
readonly invitations: {
readonly totalCount: number | null | undefined;
};
readonly members: { readonly members: {
readonly totalCount: number | null | undefined; readonly totalCount: number | null | undefined;
}; };
readonly " $fragmentSpreads": FragmentRefs<"MemberListFragment">; readonly " $fragmentSpreads": FragmentRefs<"InvitationListFragment" | "InviteUserDialog_currentRoleFragment" | "MemberListFragment">;
} | { } | {
// This will never be '%other', but we need some // This will never be '%other', but we need some
// value in case none of the concrete values match. // value in case none of the concrete values match.
readonly __typename: "%other"; readonly __typename: "%other";
}; };
readonly viewer: { readonly viewer: {
readonly " $fragmentSpreads": FragmentRefs<"MemberListItem_permissionsFragment">; readonly canInviteUser: boolean;
readonly " $fragmentSpreads": FragmentRefs<"InvitationListItem_permissionsFragment" | "MemberListItem_permissionsFragment">;
}; };
}; };
export type MembersPageQuery = { export type MembersPageQuery = {
@@ -47,301 +51,115 @@ v1 = {
"name": "id", "name": "id",
"variableName": "organizationId" "variableName": "organizationId"
}, },
v2 = [ v2 = {
"alias": "canInviteUser",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "iam:membership:create"
},
(v1/*: any*/)
],
"kind": "ScalarField",
"name": "permission",
"storageKey": null
},
v3 = [
{
"kind": "Variable",
"name": "organizationId",
"variableName": "organizationId"
}
],
v4 = [
(v1/*: any*/) (v1/*: any*/)
], ],
v3 = { v5 = {
"alias": null, "alias": null,
"args": null, "args": null,
"kind": "ScalarField", "kind": "ScalarField",
"name": "__typename", "name": "__typename",
"storageKey": null "storageKey": null
}, },
v4 = { v6 = {
"kind": "Literal", "kind": "Literal",
"name": "first", "name": "first",
"value": 20 "value": 20
}, },
v5 = { v7 = {
"direction": "ASC", "direction": "ASC",
"field": "CREATED_AT" "field": "CREATED_AT"
}, },
v6 = [ v8 = [
(v4/*: any*/), (v6/*: any*/),
{
"kind": "Literal",
"name": "order",
"value": (v7/*: any*/)
}
],
v9 = [
(v6/*: any*/),
{ {
"kind": "Literal", "kind": "Literal",
"name": "orderBy", "name": "orderBy",
"value": (v5/*: any*/) "value": (v7/*: any*/)
} }
], ],
v7 = { v10 = {
"alias": null, "alias": null,
"args": null, "args": null,
"kind": "ScalarField", "kind": "ScalarField",
"name": "totalCount", "name": "totalCount",
"storageKey": null "storageKey": null
}, },
v8 = { v11 = [
(v10/*: any*/)
],
v12 = {
"alias": null, "alias": null,
"args": null, "args": null,
"kind": "ScalarField", "kind": "ScalarField",
"name": "id", "name": "id",
"storageKey": null "storageKey": null
}, },
v9 = { v13 = {
"alias": null, "alias": null,
"args": null, "args": null,
"kind": "ScalarField", "kind": "ScalarField",
"name": "role", "name": "role",
"storageKey": null "storageKey": null
}; },
return { v14 = {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "MembersPageQuery",
"selections": [
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "viewer",
"plural": false,
"selections": [
{
"args": [
{
"kind": "Variable",
"name": "organizationId",
"variableName": "organizationId"
}
],
"kind": "FragmentSpread",
"name": "MemberListItem_permissionsFragment"
}
],
"storageKey": null
},
"action": "THROW"
},
{
"kind": "RequiredField",
"field": {
"alias": "organization",
"args": (v2/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"args": [
(v4/*: any*/),
{
"kind": "Literal",
"name": "order",
"value": (v5/*: any*/)
}
],
"kind": "FragmentSpread",
"name": "MemberListFragment"
},
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": (v6/*: any*/),
"concreteType": "MembershipConnection",
"kind": "LinkedField",
"name": "members",
"plural": false,
"selections": [
(v7/*: any*/)
],
"storageKey": "members(first:20,orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})"
},
"action": "THROW"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
},
"action": "THROW"
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "MembersPageQuery",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "viewer",
"plural": false,
"selections": [
{
"alias": "canUpdateMembership",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "iam:membership:update"
},
(v1/*: any*/)
],
"kind": "ScalarField",
"name": "permission",
"storageKey": null
},
{
"alias": "canDeleteMembership",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "iam:membership:delete"
},
(v1/*: any*/)
],
"kind": "ScalarField",
"name": "permission",
"storageKey": null
},
(v8/*: any*/)
],
"storageKey": null
},
{
"alias": "organization",
"args": (v2/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
(v8/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Membership",
"kind": "LinkedField",
"name": "viewerMembership",
"plural": false,
"selections": [
(v9/*: any*/),
(v8/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": (v6/*: any*/),
"concreteType": "MembershipConnection",
"kind": "LinkedField",
"name": "members",
"plural": false,
"selections": [
(v7/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "MembershipEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Membership",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v8/*: any*/),
(v9/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "MembershipProfile",
"kind": "LinkedField",
"name": "profile",
"plural": false,
"selections": [
{
"alias": null, "alias": null,
"args": null, "args": null,
"kind": "ScalarField", "kind": "ScalarField",
"name": "fullName", "name": "fullName",
"storageKey": null "storageKey": null
}, },
(v8/*: any*/) v15 = {
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "identity",
"plural": false,
"selections": [
{
"alias": null, "alias": null,
"args": null, "args": null,
"kind": "ScalarField", "kind": "ScalarField",
"name": "email", "name": "email",
"storageKey": null "storageKey": null
}, },
(v8/*: any*/) v16 = {
],
"storageKey": null
},
{
"alias": null, "alias": null,
"args": null, "args": null,
"kind": "ScalarField", "kind": "ScalarField",
"name": "createdAt", "name": "createdAt",
"storageKey": null "storageKey": null
}, },
(v3/*: any*/) v17 = {
],
"storageKey": null
},
{
"alias": null, "alias": null,
"args": null, "args": null,
"kind": "ScalarField", "kind": "ScalarField",
"name": "cursor", "name": "cursor",
"storageKey": null "storageKey": null
} },
], v18 = {
"storageKey": null
},
{
"alias": null, "alias": null,
"args": null, "args": null,
"concreteType": "PageInfo", "concreteType": "PageInfo",
@@ -379,8 +197,8 @@ return {
} }
], ],
"storageKey": null "storageKey": null
}, },
{ v19 = {
"kind": "ClientExtension", "kind": "ClientExtension",
"selections": [ "selections": [
{ {
@@ -391,20 +209,348 @@ return {
"storageKey": null "storageKey": null
} }
] ]
},
v20 = [
"orderBy"
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "MembersPageQuery",
"selections": [
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "viewer",
"plural": false,
"selections": [
(v2/*: any*/),
{
"args": (v3/*: any*/),
"kind": "FragmentSpread",
"name": "InvitationListItem_permissionsFragment"
},
{
"args": (v3/*: any*/),
"kind": "FragmentSpread",
"name": "MemberListItem_permissionsFragment"
} }
], ],
"storageKey": null
},
"action": "THROW"
},
{
"kind": "RequiredField",
"field": {
"alias": "organization",
"args": (v4/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v5/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "InviteUserDialog_currentRoleFragment"
},
{
"args": (v8/*: any*/),
"kind": "FragmentSpread",
"name": "MemberListFragment"
},
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": (v9/*: any*/),
"concreteType": "MembershipConnection",
"kind": "LinkedField",
"name": "members",
"plural": false,
"selections": (v11/*: any*/),
"storageKey": "members(first:20,orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})"
},
"action": "THROW"
},
{
"args": (v8/*: any*/),
"kind": "FragmentSpread",
"name": "InvitationListFragment"
},
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": (v9/*: any*/),
"concreteType": "InvitationConnection",
"kind": "LinkedField",
"name": "invitations",
"plural": false,
"selections": (v11/*: any*/),
"storageKey": "invitations(first:20,orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})"
},
"action": "THROW"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
},
"action": "THROW"
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "MembersPageQuery",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "viewer",
"plural": false,
"selections": [
(v2/*: any*/),
{
"alias": "canDeleteInvitation",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "iam:invitation:delete"
},
(v1/*: any*/)
],
"kind": "ScalarField",
"name": "permission",
"storageKey": null
},
{
"alias": "canUpdateMembership",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "iam:membership:update"
},
(v1/*: any*/)
],
"kind": "ScalarField",
"name": "permission",
"storageKey": null
},
{
"alias": "canDeleteMembership",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "iam:membership:delete"
},
(v1/*: any*/)
],
"kind": "ScalarField",
"name": "permission",
"storageKey": null
},
(v12/*: any*/)
],
"storageKey": null
},
{
"alias": "organization",
"args": (v4/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v5/*: any*/),
(v12/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Membership",
"kind": "LinkedField",
"name": "viewerMembership",
"plural": false,
"selections": [
(v13/*: any*/),
(v12/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": (v9/*: any*/),
"concreteType": "MembershipConnection",
"kind": "LinkedField",
"name": "members",
"plural": false,
"selections": [
(v10/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "MembershipEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Membership",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v12/*: any*/),
(v13/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "MembershipProfile",
"kind": "LinkedField",
"name": "profile",
"plural": false,
"selections": [
(v14/*: any*/),
(v12/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Identity",
"kind": "LinkedField",
"name": "identity",
"plural": false,
"selections": [
(v15/*: any*/),
(v12/*: any*/)
],
"storageKey": null
},
(v16/*: any*/),
(v5/*: any*/)
],
"storageKey": null
},
(v17/*: any*/)
],
"storageKey": null
},
(v18/*: any*/),
(v19/*: any*/)
],
"storageKey": "members(first:20,orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})" "storageKey": "members(first:20,orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})"
}, },
{ {
"alias": null, "alias": null,
"args": (v6/*: any*/), "args": (v9/*: any*/),
"filters": [ "filters": (v20/*: any*/),
"orderBy"
],
"handle": "connection", "handle": "connection",
"key": "MembersListFragment_members", "key": "MemberListFragment_members",
"kind": "LinkedHandle", "kind": "LinkedHandle",
"name": "members" "name": "members"
},
{
"alias": null,
"args": (v9/*: any*/),
"concreteType": "InvitationConnection",
"kind": "LinkedField",
"name": "invitations",
"plural": false,
"selections": [
(v10/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "InvitationEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Invitation",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v12/*: any*/),
(v14/*: any*/),
(v15/*: any*/),
(v13/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
(v16/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "expiresAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "acceptedAt",
"storageKey": null
},
(v5/*: any*/)
],
"storageKey": null
},
(v17/*: any*/)
],
"storageKey": null
},
(v18/*: any*/),
(v19/*: any*/)
],
"storageKey": "invitations(first:20,orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})"
},
{
"alias": null,
"args": (v9/*: any*/),
"filters": (v20/*: any*/),
"handle": "connection",
"key": "InvitationListFragment_invitations",
"kind": "LinkedHandle",
"name": "invitations"
} }
], ],
"type": "Organization", "type": "Organization",
@@ -416,16 +562,16 @@ return {
] ]
}, },
"params": { "params": {
"cacheID": "b125343184bf4d82a7d0e264faf7ac34", "cacheID": "1c006f49a510a658f66a78c66e9289db",
"id": null, "id": null,
"metadata": {}, "metadata": {},
"name": "MembersPageQuery", "name": "MembersPageQuery",
"operationKind": "query", "operationKind": "query",
"text": "query MembersPageQuery(\n $organizationId: ID!\n) {\n viewer {\n ...MemberListItem_permissionsFragment_4xMPKw\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n ...MemberListFragment_1jRT0c\n members(first: 20, orderBy: {direction: ASC, field: CREATED_AT}) {\n totalCount\n }\n }\n id\n }\n}\n\nfragment MemberListFragment_1jRT0c on Organization {\n ...MemberListItem_currentRoleFragment\n members(first: 20, orderBy: {direction: ASC, field: CREATED_AT}) {\n totalCount\n edges {\n node {\n id\n ...MemberListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment MemberListItemFragment on Membership {\n id\n role\n profile {\n fullName\n id\n }\n identity {\n email\n id\n }\n createdAt\n}\n\nfragment MemberListItem_currentRoleFragment on Organization {\n viewerMembership {\n role\n id\n }\n}\n\nfragment MemberListItem_permissionsFragment_4xMPKw on Identity {\n canUpdateMembership: permission(action: \"iam:membership:update\", id: $organizationId)\n canDeleteMembership: permission(action: \"iam:membership:delete\", id: $organizationId)\n}\n" "text": "query MembersPageQuery(\n $organizationId: ID!\n) {\n viewer {\n canInviteUser: permission(action: \"iam:membership:create\", id: $organizationId)\n ...InvitationListItem_permissionsFragment_4xMPKw\n ...MemberListItem_permissionsFragment_4xMPKw\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n ...InviteUserDialog_currentRoleFragment\n ...MemberListFragment_1jRT0c\n members(first: 20, orderBy: {direction: ASC, field: CREATED_AT}) {\n totalCount\n }\n ...InvitationListFragment_1jRT0c\n invitations(first: 20, orderBy: {direction: ASC, field: CREATED_AT}) {\n totalCount\n }\n }\n id\n }\n}\n\nfragment InvitationListFragment_1jRT0c on Organization {\n invitations(first: 20, orderBy: {direction: ASC, field: CREATED_AT}) {\n totalCount\n edges {\n node {\n id\n ...InvitationListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment InvitationListItemFragment on Invitation {\n id\n fullName\n email\n role\n status\n createdAt\n expiresAt\n acceptedAt\n}\n\nfragment InvitationListItem_permissionsFragment_4xMPKw on Identity {\n canDeleteInvitation: permission(action: \"iam:invitation:delete\", id: $organizationId)\n}\n\nfragment InviteUserDialog_currentRoleFragment on Organization {\n viewerMembership {\n role\n id\n }\n}\n\nfragment MemberListFragment_1jRT0c on Organization {\n ...MemberListItem_currentRoleFragment\n members(first: 20, orderBy: {direction: ASC, field: CREATED_AT}) {\n totalCount\n edges {\n node {\n id\n ...MemberListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment MemberListItemFragment on Membership {\n id\n role\n profile {\n fullName\n id\n }\n identity {\n email\n id\n }\n createdAt\n}\n\nfragment MemberListItem_currentRoleFragment on Organization {\n viewerMembership {\n role\n id\n }\n}\n\nfragment MemberListItem_permissionsFragment_4xMPKw on Identity {\n canUpdateMembership: permission(action: \"iam:membership:update\", id: $organizationId)\n canDeleteMembership: permission(action: \"iam:membership:delete\", id: $organizationId)\n}\n"
} }
}; };
})(); })();
(node as any).hash = "8c40640ce96580ebae677d117741094e"; (node as any).hash = "6e919357c168bbfbdccfa7375ce71660";
export default node; export default node;

View File

@@ -0,0 +1,117 @@
import { Tbody, Td, Th, Thead, Tr } from "@probo/ui";
import { SortableTable, SortableTh } from "/components/SortableTable";
import { useTranslate } from "@probo/i18n";
import { graphql, usePaginationFragment } from "react-relay";
import { InvitationListItem } from "./InvitationListItem";
import type { InvitationListFragment$key } from "./__generated__/InvitationListFragment.graphql";
import type { InvitationListItem_permissionsFragment$key } from "./__generated__/InvitationListItem_permissionsFragment.graphql";
import type { InvitationListFragment_RefetchQuery } from "./__generated__/InvitationListFragment_RefetchQuery.graphql";
const fragment = graphql`
fragment InvitationListFragment on Organization
@refetchable(queryName: "InvitationListFragment_RefetchQuery")
@argumentDefinitions(
first: { type: "Int", defaultValue: 20 }
order: {
type: "InvitationOrder"
defaultValue: { direction: ASC, field: CREATED_AT }
}
after: { type: "CursorKey", defaultValue: null }
before: { type: "CursorKey", defaultValue: null }
last: { type: "Int", defaultValue: null }
) {
invitations(
first: $first
after: $after
last: $last
before: $before
orderBy: $order
)
@connection(key: "InvitationListFragment_invitations")
@required(action: THROW) {
__id
totalCount
edges @required(action: THROW) {
node {
id
...InvitationListItemFragment
}
}
}
}
`;
export function InvitationList(props: {
fKey: InvitationListFragment$key;
permissionsFKey: InvitationListItem_permissionsFragment$key;
}) {
const { fKey, permissionsFKey } = props;
const { __ } = useTranslate();
const invitationsPagination = usePaginationFragment<
InvitationListFragment_RefetchQuery,
InvitationListFragment$key
>(fragment, fKey);
const refetchInvitations = () => {
invitationsPagination.refetch({}, { fetchPolicy: "network-only" });
};
return (
<SortableTable
{...invitationsPagination}
refetch={({ order }: { order: { direction: string; field: string } }) => {
invitationsPagination.refetch({
order: {
direction: order.direction as "ASC" | "DESC",
field: order.field as
| "CREATED_AT"
| "EXPIRES_AT"
// FIXME: put back
// | "FULL_NAME"
| "EMAIL"
| "ROLE"
// FIXME: put back
// | "STATUS"
| "ACCEPTED_AT",
},
});
}}
pageSize={20}
>
<Thead>
<Tr>
<SortableTh field="FULL_NAME">{__("Name")}</SortableTh>
<SortableTh field="EMAIL">{__("Email")}</SortableTh>
<SortableTh field="ROLE">{__("Role")}</SortableTh>
<SortableTh field="CREATED_AT">{__("Invited")}</SortableTh>
<Th>{__("Status")}</Th>
<SortableTh field="ACCEPTED_AT">{__("Accepted at")}</SortableTh>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{invitationsPagination.data.invitations.totalCount === 0 ? (
<Tr>
<Td colSpan={7} className="text-center text-txt-secondary">
{__("No invitations")}
</Td>
</Tr>
) : (
invitationsPagination.data.invitations.edges.map(
({ node: invitation }) => (
<InvitationListItem
connectionId={invitationsPagination.data.invitations.__id}
key={invitation.id}
fKey={invitation}
onRefetch={refetchInvitations}
permissionsFKey={permissionsFKey}
/>
),
)
)}
</Tbody>
</SortableTable>
);
}

View File

@@ -0,0 +1,151 @@
import { useTranslate } from "@probo/i18n";
import {
Badge,
Button,
IconTrashCan,
Spinner,
Td,
Tr,
useConfirm,
} from "@probo/ui";
import clsx from "clsx";
import { useFragment } from "react-relay";
import { graphql } from "relay-runtime";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
import { sprintf } from "@probo/helpers";
import { useOrganizationId } from "/hooks/useOrganizationId";
import type { InvitationListItemFragment$key } from "./__generated__/InvitationListItemFragment.graphql";
import type { InvitationListItem_permissionsFragment$key } from "./__generated__/InvitationListItem_permissionsFragment.graphql";
const fragment = graphql`
fragment InvitationListItemFragment on Invitation {
id
fullName
email
role
status
createdAt
expiresAt
acceptedAt
}
`;
const permissionsFragment = graphql`
fragment InvitationListItem_permissionsFragment on Identity
@argumentDefinitions(organizationId: { type: "ID!" }) {
canDeleteInvitation: permission(
action: "iam:invitation:delete"
id: $organizationId
)
}
`;
const deleteInvitationMutation = graphql`
mutation InvitationListItem_DeleteInvitationMutation(
$input: DeleteInvitationInput!
$connections: [ID!]!
) {
deleteInvitation(input: $input) {
deletedInvitationId @deleteEdge(connections: $connections)
}
}
`;
export function InvitationListItem(props: {
connectionId: string;
fKey: InvitationListItemFragment$key;
permissionsFKey: InvitationListItem_permissionsFragment$key;
onRefetch: () => void;
}) {
const { connectionId, fKey, permissionsFKey } = props;
const organizationId = useOrganizationId();
const { __ } = useTranslate();
const confirm = useConfirm();
const invitation = useFragment<InvitationListItemFragment$key>(
fragment,
fKey,
);
const permissions = useFragment<InvitationListItem_permissionsFragment$key>(
permissionsFragment,
permissionsFKey,
);
const [deleteInvitation, isDeleting] = useMutationWithToasts(
deleteInvitationMutation,
{
successMessage: __("Invitation deleted successfully"),
errorMessage: __("Failed to delete invitation"),
},
);
const handleDelete = () => {
confirm(
() => {
return deleteInvitation({
variables: {
input: {
organizationId,
invitationId: invitation.id,
},
connections: [connectionId],
},
});
},
{
message: sprintf(
__("Are you sure you want to delete the invitation for %s?"),
invitation.fullName,
),
},
);
};
return (
<Tr className={clsx(isDeleting && "opacity-60 pointer-events-none")}>
<Td>
<div className="font-semibold">{invitation.fullName}</div>
</Td>
<Td>{invitation.email}</Td>
<Td>
<Badge>{invitation.role}</Badge>
</Td>
<Td>{new Date(invitation.createdAt).toLocaleDateString()}</Td>
<Td>
{invitation.status === "ACCEPTED" ? (
<Badge variant="success">{__("Accepted")}</Badge>
) : invitation.status === "EXPIRED" ? (
<Badge variant="danger">{__("Expired")}</Badge>
) : (
<Badge variant="warning">{__("Pending")}</Badge>
)}
</Td>
<Td>
{invitation.acceptedAt
? new Date(invitation.acceptedAt).toLocaleDateString()
: "-"}
</Td>
<Td noLink width={80} className="text-end">
<div
className="flex gap-2 justify-end"
onClick={(e) => e.stopPropagation()}
>
{isDeleting ? (
<Spinner size={16} />
) : (
permissions.canDeleteInvitation && (
<Button
variant="danger"
onClick={handleDelete}
disabled={isDeleting}
icon={IconTrashCan}
aria-label={__("Delete invitation")}
/>
)
)}
</div>
</Td>
</Tr>
);
}

View File

@@ -0,0 +1,227 @@
import { getAssignableRoles } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import {
Dialog,
DialogContent,
Field,
Select,
Option,
DialogFooter,
Button,
Checkbox,
useDialogRef,
} from "@probo/ui";
import { Controller } from "react-hook-form";
import { useFragment } from "react-relay";
import { ConnectionHandler, graphql } from "relay-runtime";
import z from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
import { useOrganizationId } from "/hooks/useOrganizationId";
import type { PropsWithChildren } from "react";
import type { InviteUserDialog_currentRoleFragment$key } from "./__generated__/InviteUserDialog_currentRoleFragment.graphql";
const currentRoleFragment = graphql`
fragment InviteUserDialog_currentRoleFragment on Organization {
viewerMembership @required(action: THROW) {
role
}
}
`;
const inviteMutation = graphql`
mutation InviteUserDialogMutation(
$input: InviteMemberInput!
$connections: [ID!]!
) {
inviteMember(input: $input) {
invitationEdge @prependEdge(connections: $connections) {
node {
id
email
fullName
role
expiresAt
acceptedAt
createdAt
}
}
}
}
`;
const schema = z.object({
email: z.string().email(),
fullName: z.string(),
role: z
.enum(["OWNER", "ADMIN", "FULL", "VIEWER", "AUDITOR", "EMPLOYEE"])
.default("VIEWER"),
createPeople: z.boolean().default(false),
});
type InviteUserDialogProps = PropsWithChildren<{
viewerMembershipFKey: InviteUserDialog_currentRoleFragment$key;
}>;
export function InviteUserDialog(props: InviteUserDialogProps) {
const { children, viewerMembershipFKey } = props;
const organizationId = useOrganizationId();
const { __ } = useTranslate();
const dialogRef = useDialogRef();
const { viewerMembership } =
useFragment<InviteUserDialog_currentRoleFragment$key>(
currentRoleFragment,
viewerMembershipFKey,
);
const [inviteUser, isInviting] = useMutationWithToasts(inviteMutation, {
successMessage: __("Invitation sent successfully"),
errorMessage: __("Failed to send invitation"),
});
const assignableRoles = getAssignableRoles(viewerMembership.role);
const { register, handleSubmit, formState, reset, control } =
useFormWithSchema(schema, {
defaultValues: { role: "VIEWER", createPeople: false },
});
const onSubmit = handleSubmit((data) => {
const connectionId = ConnectionHandler.getConnectionID(
organizationId,
"InvitationListFragment_invitations",
);
inviteUser({
variables: {
input: {
organizationId,
email: data.email,
fullName: data.fullName,
role: data.role,
createPeople: data.createPeople,
},
connections: [connectionId],
},
onCompleted: () => {
reset();
dialogRef.current?.close();
},
});
});
return (
<Dialog
title={__("Invite member")}
trigger={children}
className="max-w-lg"
ref={dialogRef}
>
<form onSubmit={onSubmit}>
<DialogContent padded className="space-y-4">
<p className="text-txt-secondary text-sm">
Send an invitation to join your workspace.
</p>
<Field
type="email"
label={__("Email")}
placeholder={__("Email")}
{...register("email")}
error={formState.errors.email?.message}
/>
<Field
type="text"
label={__("Full name")}
placeholder={__("Full name")}
{...register("fullName")}
error={formState.errors.fullName?.message}
/>
<Field label={__("Role")} required>
<Controller
name="role"
control={control}
render={({ field }) => (
<>
<Select value={field.value} onValueChange={field.onChange}>
{assignableRoles.includes("OWNER") && (
<Option value="OWNER">{__("Owner")}</Option>
)}
{assignableRoles.includes("ADMIN") && (
<Option value="ADMIN">{__("Admin")}</Option>
)}
{assignableRoles.includes("VIEWER") && (
<Option value="VIEWER">{__("Viewer")}</Option>
)}
{assignableRoles.includes("AUDITOR") && (
<Option value="AUDITOR">{__("Auditor")}</Option>
)}
{assignableRoles.includes("EMPLOYEE") && (
<Option value="EMPLOYEE">{__("Employee")}</Option>
)}
</Select>
<div className="mt-2 text-sm text-txt-tertiary">
{field.value === "OWNER" && (
<p>{__("Full access to everything")}</p>
)}
{field.value === "ADMIN" && (
<p>
{__(
"Full access except organization setup and API keys",
)}
</p>
)}
{field.value === "VIEWER" && (
<p>{__("Read-only access")}</p>
)}
{field.value === "AUDITOR" && (
<p>
{__(
"Read-only access without settings, tasks and meetings",
)}
</p>
)}
{field.value === "EMPLOYEE" && (
<p>{__("Access to employee page")}</p>
)}
</div>
</>
)}
/>
</Field>
<div className="space-y-2">
<div className="flex items-center space-x-3">
<Controller
name="createPeople"
control={control}
render={({ field }) => (
<>
<Checkbox
checked={field.value ?? false}
onChange={field.onChange}
/>
<label
className="text-sm font-medium cursor-pointer"
onClick={() => field.onChange(!field.value)}
>
{__("Create people record")}
</label>
</>
)}
/>
</div>
<p className="text-xs text-txt-secondary ml-7">
{__(
"Creates a people record for this user in addition to the user account",
)}
</p>
</div>
</DialogContent>
<DialogFooter>
<Button type="submit" disabled={isInviting}>
{__("Invite user")}
</Button>
</DialogFooter>
</form>
</Dialog>
);
}

View File

@@ -27,7 +27,7 @@ const fragment = graphql`
last: $last last: $last
before: $before before: $before
orderBy: $order orderBy: $order
) @connection(key: "MembersListFragment_members") @required(action: THROW) { ) @connection(key: "MemberListFragment_members") @required(action: THROW) {
__id __id
totalCount totalCount
edges @required(action: THROW) { edges @required(action: THROW) {
@@ -42,9 +42,9 @@ const fragment = graphql`
export function MemberList(props: { export function MemberList(props: {
fKey: MemberListFragment$key; fKey: MemberListFragment$key;
viewerFKey: MemberListItem_permissionsFragment$key; permissionsFKey: MemberListItem_permissionsFragment$key;
}) { }) {
const { fKey, viewerFKey } = props; const { fKey, permissionsFKey } = props;
const { __ } = useTranslate(); const { __ } = useTranslate();
@@ -85,7 +85,7 @@ export function MemberList(props: {
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
{membersPagination.data.members.edges.length === 0 ? ( {membersPagination.data.members.totalCount === 0 ? (
<Tr> <Tr>
<Td colSpan={5} className="text-center text-txt-secondary"> <Td colSpan={5} className="text-center text-txt-secondary">
{__("No members")} {__("No members")}
@@ -94,11 +94,12 @@ export function MemberList(props: {
) : ( ) : (
membersPagination.data.members.edges.map(({ node: membership }) => ( membersPagination.data.members.edges.map(({ node: membership }) => (
<MemberListItem <MemberListItem
connectionId={membersPagination.data.members.__id}
key={membership.id} key={membership.id}
fKey={membership} fKey={membership}
permissionsFKey={membersPagination.data}
onRefetch={refetchMemberships} onRefetch={refetchMemberships}
viewerFKey={viewerFKey} permissionsFKey={permissionsFKey}
viewerFKey={membersPagination.data}
/> />
)) ))
)} )}

View File

@@ -12,7 +12,7 @@ import {
import clsx from "clsx"; import clsx from "clsx";
import { useState } from "react"; import { useState } from "react";
import { useFragment } from "react-relay"; import { useFragment } from "react-relay";
import { ConnectionHandler, graphql } from "relay-runtime"; import { graphql } from "relay-runtime";
import type { MemberListItemFragment$key } from "./__generated__/MemberListItemFragment.graphql"; import type { MemberListItemFragment$key } from "./__generated__/MemberListItemFragment.graphql";
import type { MemberListItem_permissionsFragment$key } from "./__generated__/MemberListItem_permissionsFragment.graphql"; import type { MemberListItem_permissionsFragment$key } from "./__generated__/MemberListItem_permissionsFragment.graphql";
import type { MemberListItem_currentRoleFragment$key } from "./__generated__/MemberListItem_currentRoleFragment.graphql"; import type { MemberListItem_currentRoleFragment$key } from "./__generated__/MemberListItem_currentRoleFragment.graphql";
@@ -69,12 +69,13 @@ const removeMemberMutation = graphql`
`; `;
export function MemberListItem(props: { export function MemberListItem(props: {
connectionId: string;
fKey: MemberListItemFragment$key; fKey: MemberListItemFragment$key;
permissionsFKey: MemberListItem_currentRoleFragment$key; permissionsFKey: MemberListItem_permissionsFragment$key;
viewerFKey: MemberListItem_permissionsFragment$key; viewerFKey: MemberListItem_currentRoleFragment$key;
onRefetch: () => void; onRefetch: () => void;
}) { }) {
const { fKey, onRefetch, permissionsFKey, viewerFKey } = props; const { fKey, connectionId, permissionsFKey, viewerFKey } = props;
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const { __ } = useTranslate(); const { __ } = useTranslate();
@@ -85,11 +86,11 @@ export function MemberListItem(props: {
const { viewerMembership } = const { viewerMembership } =
useFragment<MemberListItem_currentRoleFragment$key>( useFragment<MemberListItem_currentRoleFragment$key>(
currentRoleFragment, currentRoleFragment,
permissionsFKey, viewerFKey,
); );
const permissions = useFragment<MemberListItem_permissionsFragment$key>( const permissions = useFragment<MemberListItem_permissionsFragment$key>(
permissionsFragment, permissionsFragment,
viewerFKey, permissionsFKey,
); );
// Only OWNER can edit OWNER members // Only OWNER can edit OWNER members
@@ -105,10 +106,6 @@ export function MemberListItem(props: {
); );
const handleRemove = async () => { const handleRemove = async () => {
const connectionId = ConnectionHandler.getConnectionID(
organizationId,
"MembersListFragment_members",
);
confirm( confirm(
() => { () => {
return removeMembership({ return removeMembership({
@@ -119,7 +116,6 @@ export function MemberListItem(props: {
}, },
connections: [connectionId], connections: [connectionId],
}, },
onCompleted: onRefetch,
}); });
}, },
{ {

View File

@@ -0,0 +1,244 @@
/**
* @generated SignedSource<<6067ce11fa959cba895d2b10862592de>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type InvitationListFragment$data = {
readonly id: string;
readonly invitations: {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
readonly " $fragmentSpreads": FragmentRefs<"InvitationListItemFragment">;
};
}>;
readonly totalCount: number | null | undefined;
};
readonly " $fragmentType": "InvitationListFragment";
};
export type InvitationListFragment$key = {
readonly " $data"?: InvitationListFragment$data;
readonly " $fragmentSpreads": FragmentRefs<"InvitationListFragment">;
};
import InvitationListFragment_RefetchQuery_graphql from './InvitationListFragment_RefetchQuery.graphql';
const node: ReaderFragment = (function(){
var v0 = [
"invitations"
],
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
return {
"argumentDefinitions": [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "after"
},
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "before"
},
{
"defaultValue": 20,
"kind": "LocalArgument",
"name": "first"
},
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "last"
},
{
"defaultValue": {
"direction": "ASC",
"field": "CREATED_AT"
},
"kind": "LocalArgument",
"name": "order"
}
],
"kind": "Fragment",
"metadata": {
"connection": [
{
"count": null,
"cursor": null,
"direction": "bidirectional",
"path": (v0/*: any*/)
}
],
"refetch": {
"connection": {
"forward": {
"count": "first",
"cursor": "after"
},
"backward": {
"count": "last",
"cursor": "before"
},
"path": (v0/*: any*/)
},
"fragmentPathInResult": [
"node"
],
"operation": InvitationListFragment_RefetchQuery_graphql,
"identifierInfo": {
"identifierField": "id",
"identifierQueryVariableName": "id"
}
}
},
"name": "InvitationListFragment",
"selections": [
{
"kind": "RequiredField",
"field": {
"alias": "invitations",
"args": [
{
"kind": "Variable",
"name": "orderBy",
"variableName": "order"
}
],
"concreteType": "InvitationConnection",
"kind": "LinkedField",
"name": "__InvitationListFragment_invitations_connection",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "totalCount",
"storageKey": null
},
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": null,
"concreteType": "InvitationEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Invitation",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v1/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
"name": "InvitationListItemFragment"
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
],
"storageKey": null
},
"action": "THROW"
},
{
"alias": null,
"args": null,
"concreteType": "PageInfo",
"kind": "LinkedField",
"name": "pageInfo",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasPreviousPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "startCursor",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
},
"action": "THROW"
},
(v1/*: any*/)
],
"type": "Organization",
"abstractKey": null
};
})();
(node as any).hash = "837e567be489574a7f0350e26a8e5454";
export default node;

View File

@@ -0,0 +1,375 @@
/**
* @generated SignedSource<<f1bb84b37a7d8df3e9e563fe6204a307>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type InvitationOrderField = "ACCEPTED_AT" | "CREATED_AT" | "EMAIL" | "EXPIRES_AT" | "ROLE";
export type OrderDirection = "ASC" | "DESC";
export type InvitationOrder = {
direction: OrderDirection;
field: InvitationOrderField;
};
export type InvitationListFragment_RefetchQuery$variables = {
after?: any | null | undefined;
before?: any | null | undefined;
first?: number | null | undefined;
id: string;
last?: number | null | undefined;
order?: InvitationOrder | null | undefined;
};
export type InvitationListFragment_RefetchQuery$data = {
readonly node: {
readonly " $fragmentSpreads": FragmentRefs<"InvitationListFragment">;
} | null | undefined;
};
export type InvitationListFragment_RefetchQuery = {
response: InvitationListFragment_RefetchQuery$data;
variables: InvitationListFragment_RefetchQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "after"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "before"
},
v2 = {
"defaultValue": 20,
"kind": "LocalArgument",
"name": "first"
},
v3 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "id"
},
v4 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "last"
},
v5 = {
"defaultValue": {
"direction": "ASC",
"field": "CREATED_AT"
},
"kind": "LocalArgument",
"name": "order"
},
v6 = [
{
"kind": "Variable",
"name": "id",
"variableName": "id"
}
],
v7 = {
"kind": "Variable",
"name": "after",
"variableName": "after"
},
v8 = {
"kind": "Variable",
"name": "before",
"variableName": "before"
},
v9 = {
"kind": "Variable",
"name": "first",
"variableName": "first"
},
v10 = {
"kind": "Variable",
"name": "last",
"variableName": "last"
},
v11 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v12 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v13 = [
(v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
{
"kind": "Variable",
"name": "orderBy",
"variableName": "order"
}
];
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/),
(v2/*: any*/),
(v3/*: any*/),
(v4/*: any*/),
(v5/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "InvitationListFragment_RefetchQuery",
"selections": [
{
"alias": null,
"args": (v6/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"args": [
(v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
{
"kind": "Variable",
"name": "order",
"variableName": "order"
}
],
"kind": "FragmentSpread",
"name": "InvitationListFragment"
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/),
(v2/*: any*/),
(v4/*: any*/),
(v5/*: any*/),
(v3/*: any*/)
],
"kind": "Operation",
"name": "InvitationListFragment_RefetchQuery",
"selections": [
{
"alias": null,
"args": (v6/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v11/*: any*/),
(v12/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": (v13/*: any*/),
"concreteType": "InvitationConnection",
"kind": "LinkedField",
"name": "invitations",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "totalCount",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "InvitationEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Invitation",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v12/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "email",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "role",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "expiresAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "acceptedAt",
"storageKey": null
},
(v11/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "PageInfo",
"kind": "LinkedField",
"name": "pageInfo",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasPreviousPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "startCursor",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
},
{
"alias": null,
"args": (v13/*: any*/),
"filters": [
"orderBy"
],
"handle": "connection",
"key": "InvitationListFragment_invitations",
"kind": "LinkedHandle",
"name": "invitations"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "4cc36c8fb50b104ebe3fae66bf847ecc",
"id": null,
"metadata": {},
"name": "InvitationListFragment_RefetchQuery",
"operationKind": "query",
"text": "query InvitationListFragment_RefetchQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: InvitationOrder = {direction: ASC, field: CREATED_AT}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...InvitationListFragment_16fISc\n id\n }\n}\n\nfragment InvitationListFragment_16fISc on Organization {\n invitations(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n totalCount\n edges {\n node {\n id\n ...InvitationListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment InvitationListItemFragment on Invitation {\n id\n fullName\n email\n role\n status\n createdAt\n expiresAt\n acceptedAt\n}\n"
}
};
})();
(node as any).hash = "837e567be489574a7f0350e26a8e5454";
export default node;

View File

@@ -0,0 +1,100 @@
/**
* @generated SignedSource<<748bbadd485b1e131e583c119f980e91>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type InvitationStatus = "ACCEPTED" | "EXPIRED" | "PENDING";
export type MembershipRole = "ADMIN" | "AUDITOR" | "EMPLOYEE" | "OWNER" | "VIEWER";
import { FragmentRefs } from "relay-runtime";
export type InvitationListItemFragment$data = {
readonly acceptedAt: any | null | undefined;
readonly createdAt: any;
readonly email: any;
readonly expiresAt: any;
readonly fullName: string;
readonly id: string;
readonly role: MembershipRole;
readonly status: InvitationStatus;
readonly " $fragmentType": "InvitationListItemFragment";
};
export type InvitationListItemFragment$key = {
readonly " $data"?: InvitationListItemFragment$data;
readonly " $fragmentSpreads": FragmentRefs<"InvitationListItemFragment">;
};
const node: ReaderFragment = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "InvitationListItemFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "email",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "role",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "expiresAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "acceptedAt",
"storageKey": null
}
],
"type": "Invitation",
"abstractKey": null
};
(node as any).hash = "772414270b8ce2fed6c7b5fe97082c17";
export default node;

View File

@@ -0,0 +1,133 @@
/**
* @generated SignedSource<<a46996c5fe127b005124bc79ea88fea4>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteInvitationInput = {
invitationId: string;
organizationId: string;
};
export type InvitationListItem_DeleteInvitationMutation$variables = {
connections: ReadonlyArray<string>;
input: DeleteInvitationInput;
};
export type InvitationListItem_DeleteInvitationMutation$data = {
readonly deleteInvitation: {
readonly deletedInvitationId: string;
} | null | undefined;
};
export type InvitationListItem_DeleteInvitationMutation = {
response: InvitationListItem_DeleteInvitationMutation$data;
variables: InvitationListItem_DeleteInvitationMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "connections"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
},
v2 = [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deletedInvitationId",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "InvitationListItem_DeleteInvitationMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteInvitationPayload",
"kind": "LinkedField",
"name": "deleteInvitation",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "InvitationListItem_DeleteInvitationMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteInvitationPayload",
"kind": "LinkedField",
"name": "deleteInvitation",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "deleteEdge",
"key": "",
"kind": "ScalarHandle",
"name": "deletedInvitationId",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "35736b4156c15bb9eeb863b4d40fc4e6",
"id": null,
"metadata": {},
"name": "InvitationListItem_DeleteInvitationMutation",
"operationKind": "mutation",
"text": "mutation InvitationListItem_DeleteInvitationMutation(\n $input: DeleteInvitationInput!\n) {\n deleteInvitation(input: $input) {\n deletedInvitationId\n }\n}\n"
}
};
})();
(node as any).hash = "2191c08f6c6bf5bace0d70a6481124bb";
export default node;

View File

@@ -0,0 +1,59 @@
/**
* @generated SignedSource<<0fa501d76c400538c62a44425ebe4b11>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type InvitationListItem_permissionsFragment$data = {
readonly canDeleteInvitation: boolean;
readonly " $fragmentType": "InvitationListItem_permissionsFragment";
};
export type InvitationListItem_permissionsFragment$key = {
readonly " $data"?: InvitationListItem_permissionsFragment$data;
readonly " $fragmentSpreads": FragmentRefs<"InvitationListItem_permissionsFragment">;
};
const node: ReaderFragment = {
"argumentDefinitions": [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "organizationId"
}
],
"kind": "Fragment",
"metadata": null,
"name": "InvitationListItem_permissionsFragment",
"selections": [
{
"alias": "canDeleteInvitation",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "iam:invitation:delete"
},
{
"kind": "Variable",
"name": "id",
"variableName": "organizationId"
}
],
"kind": "ScalarField",
"name": "permission",
"storageKey": null
}
],
"type": "Identity",
"abstractKey": null
};
(node as any).hash = "e9e75ae0f5a3755a777acf16e5024ee0";
export default node;

View File

@@ -0,0 +1,211 @@
/**
* @generated SignedSource<<84f3f3da89ad2bdfecd79b479bf74814>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type MembershipRole = "ADMIN" | "AUDITOR" | "EMPLOYEE" | "OWNER" | "VIEWER";
export type InviteMemberInput = {
createPeople: boolean;
email: any;
fullName: string;
organizationId: string;
role: MembershipRole;
};
export type InviteUserDialogMutation$variables = {
connections: ReadonlyArray<string>;
input: InviteMemberInput;
};
export type InviteUserDialogMutation$data = {
readonly inviteMember: {
readonly invitationEdge: {
readonly node: {
readonly acceptedAt: any | null | undefined;
readonly createdAt: any;
readonly email: any;
readonly expiresAt: any;
readonly fullName: string;
readonly id: string;
readonly role: MembershipRole;
};
};
} | null | undefined;
};
export type InviteUserDialogMutation = {
response: InviteUserDialogMutation$data;
variables: InviteUserDialogMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "connections"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
},
v2 = [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
v3 = {
"alias": null,
"args": null,
"concreteType": "InvitationEdge",
"kind": "LinkedField",
"name": "invitationEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Invitation",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "email",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "role",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "expiresAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "acceptedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "InviteUserDialogMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "InviteMemberPayload",
"kind": "LinkedField",
"name": "inviteMember",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "InviteUserDialogMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "InviteMemberPayload",
"kind": "LinkedField",
"name": "inviteMember",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "prependEdge",
"key": "",
"kind": "LinkedHandle",
"name": "invitationEdge",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "43ea730d742b7d259b50b50d3684147c",
"id": null,
"metadata": {},
"name": "InviteUserDialogMutation",
"operationKind": "mutation",
"text": "mutation InviteUserDialogMutation(\n $input: InviteMemberInput!\n) {\n inviteMember(input: $input) {\n invitationEdge {\n node {\n id\n email\n fullName\n role\n expiresAt\n acceptedAt\n createdAt\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "b45737075b750b79b3a90748cb2565f3";
export default node;

View File

@@ -0,0 +1,60 @@
/**
* @generated SignedSource<<de896a2d4f3d1dce06f7b88630afb445>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type MembershipRole = "ADMIN" | "AUDITOR" | "EMPLOYEE" | "OWNER" | "VIEWER";
import { FragmentRefs } from "relay-runtime";
export type InviteUserDialog_currentRoleFragment$data = {
readonly viewerMembership: {
readonly role: MembershipRole;
};
readonly " $fragmentType": "InviteUserDialog_currentRoleFragment";
};
export type InviteUserDialog_currentRoleFragment$key = {
readonly " $data"?: InviteUserDialog_currentRoleFragment$data;
readonly " $fragmentSpreads": FragmentRefs<"InviteUserDialog_currentRoleFragment">;
};
const node: ReaderFragment = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "InviteUserDialog_currentRoleFragment",
"selections": [
{
"kind": "RequiredField",
"field": {
"alias": null,
"args": null,
"concreteType": "Membership",
"kind": "LinkedField",
"name": "viewerMembership",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "role",
"storageKey": null
}
],
"storageKey": null
},
"action": "THROW"
}
],
"type": "Organization",
"abstractKey": null
};
(node as any).hash = "167528734d4ad650128d5ed8f201c97e";
export default node;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<f1134e8dcf977b50bc30bb957ec4a007>> * @generated SignedSource<<30bea11238a554ed69a88c9f07d33d9e>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -126,7 +126,7 @@ return {
], ],
"concreteType": "MembershipConnection", "concreteType": "MembershipConnection",
"kind": "LinkedField", "kind": "LinkedField",
"name": "__MembersListFragment_members_connection", "name": "__MemberListFragment_members_connection",
"plural": false, "plural": false,
"selections": [ "selections": [
{ {
@@ -245,6 +245,6 @@ return {
}; };
})(); })();
(node as any).hash = "dcdc5a8acd5e8f7317fbd1bcd3570f61"; (node as any).hash = "909f6c9ed0fcaf7b0d170bded8c97562";
export default node; export default node;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<4966da1a2a597f71a90c8766ad760615>> * @generated SignedSource<<75b9df3cc430ed26d41b88a67a410aca>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -363,7 +363,7 @@ return {
"orderBy" "orderBy"
], ],
"handle": "connection", "handle": "connection",
"key": "MembersListFragment_members", "key": "MemberListFragment_members",
"kind": "LinkedHandle", "kind": "LinkedHandle",
"name": "members" "name": "members"
} }
@@ -387,6 +387,6 @@ return {
}; };
})(); })();
(node as any).hash = "dcdc5a8acd5e8f7317fbd1bcd3570f61"; (node as any).hash = "909f6c9ed0fcaf7b0d170bded8c97562";
export default node; export default node;

View File

@@ -206,6 +206,7 @@ type Organization implements Node {
last: Int last: Int
before: CursorKey before: CursorKey
status: InvitationStatus status: InvitationStatus
orderBy: InvitationOrder
): InvitationConnection @goField(forceResolver: true) ): InvitationConnection @goField(forceResolver: true)
samlConfigurations( samlConfigurations(
@@ -244,6 +245,7 @@ type Membership implements Node {
type Invitation implements Node { type Invitation implements Node {
id: ID! id: ID!
email: EmailAddr! email: EmailAddr!
fullName: String!
role: MembershipRole! role: MembershipRole!
expiresAt: Datetime! expiresAt: Datetime!
acceptedAt: Datetime acceptedAt: Datetime
@@ -600,6 +602,8 @@ input InviteMemberInput {
organizationId: ID! organizationId: ID!
email: EmailAddr! email: EmailAddr!
fullName: String! fullName: String!
role: MembershipRole!
createPeople: Boolean!
} }
input UpdateMembershipInput { input UpdateMembershipInput {

View File

@@ -147,6 +147,7 @@ type ComplexityRoot struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
Email func(childComplexity int) int Email func(childComplexity int) int
ExpiresAt func(childComplexity int) int ExpiresAt func(childComplexity int) int
FullName func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
Organization func(childComplexity int) int Organization func(childComplexity int) int
Role func(childComplexity int) int Role func(childComplexity int) int
@@ -234,7 +235,7 @@ type ComplexityRoot struct {
HeadquarterAddress func(childComplexity int) int HeadquarterAddress func(childComplexity int) int
HorizontalLogoURL func(childComplexity int) int HorizontalLogoURL func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
Invitations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, status *coredata.InvitationStatus) int Invitations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, status *coredata.InvitationStatus, orderBy *types.InvitationOrderBy) int
LogoURL func(childComplexity int) int LogoURL func(childComplexity int) int
Members func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) int Members func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) int
Name func(childComplexity int) int Name func(childComplexity int) int
@@ -479,7 +480,7 @@ type OrganizationResolver interface {
HorizontalLogoURL(ctx context.Context, obj *types.Organization) (*string, error) HorizontalLogoURL(ctx context.Context, obj *types.Organization) (*string, error)
Members(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) (*types.MembershipConnection, error) Members(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) (*types.MembershipConnection, error)
Invitations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, status *coredata.InvitationStatus) (*types.InvitationConnection, error) Invitations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, status *coredata.InvitationStatus, orderBy *types.InvitationOrderBy) (*types.InvitationConnection, error)
SamlConfigurations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.SAMLConfigurationConnection, error) SamlConfigurations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.SAMLConfigurationConnection, error)
ViewerMembership(ctx context.Context, obj *types.Organization) (*types.Membership, error) ViewerMembership(ctx context.Context, obj *types.Organization) (*types.Membership, error)
} }
@@ -767,6 +768,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
} }
return e.complexity.Invitation.ExpiresAt(childComplexity), true return e.complexity.Invitation.ExpiresAt(childComplexity), true
case "Invitation.fullName":
if e.complexity.Invitation.FullName == nil {
break
}
return e.complexity.Invitation.FullName(childComplexity), true
case "Invitation.id": case "Invitation.id":
if e.complexity.Invitation.ID == nil { if e.complexity.Invitation.ID == nil {
break break
@@ -1271,7 +1278,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false return 0, false
} }
return e.complexity.Organization.Invitations(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["status"].(*coredata.InvitationStatus)), true return e.complexity.Organization.Invitations(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["status"].(*coredata.InvitationStatus), args["orderBy"].(*types.InvitationOrderBy)), true
case "Organization.logoUrl": case "Organization.logoUrl":
if e.complexity.Organization.LogoURL == nil { if e.complexity.Organization.LogoURL == nil {
break break
@@ -2206,6 +2213,7 @@ type Organization implements Node {
last: Int last: Int
before: CursorKey before: CursorKey
status: InvitationStatus status: InvitationStatus
orderBy: InvitationOrder
): InvitationConnection @goField(forceResolver: true) ): InvitationConnection @goField(forceResolver: true)
samlConfigurations( samlConfigurations(
@@ -2244,6 +2252,7 @@ type Membership implements Node {
type Invitation implements Node { type Invitation implements Node {
id: ID! id: ID!
email: EmailAddr! email: EmailAddr!
fullName: String!
role: MembershipRole! role: MembershipRole!
expiresAt: Datetime! expiresAt: Datetime!
acceptedAt: Datetime acceptedAt: Datetime
@@ -2600,6 +2609,8 @@ input InviteMemberInput {
organizationId: ID! organizationId: ID!
email: EmailAddr! email: EmailAddr!
fullName: String! fullName: String!
role: MembershipRole!
createPeople: Boolean!
} }
input UpdateMembershipInput { input UpdateMembershipInput {
@@ -3245,6 +3256,11 @@ func (ec *executionContext) field_Organization_invitations_args(ctx context.Cont
return nil, err return nil, err
} }
args["status"] = arg4 args["status"] = arg4
arg5, err := graphql.ProcessArgField(ctx, rawArgs, "orderBy", ec.unmarshalOInvitationOrder2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐInvitationOrderBy)
if err != nil {
return nil, err
}
args["orderBy"] = arg5
return args, nil return args, nil
} }
@@ -4571,6 +4587,35 @@ func (ec *executionContext) fieldContext_Invitation_email(_ context.Context, fie
return fc, nil return fc, nil
} }
func (ec *executionContext) _Invitation_fullName(ctx context.Context, field graphql.CollectedField, obj *types.Invitation) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_Invitation_fullName,
func(ctx context.Context) (any, error) {
return obj.FullName, nil
},
nil,
ec.marshalNString2string,
true,
true,
)
}
func (ec *executionContext) fieldContext_Invitation_fullName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Invitation",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Invitation_role(ctx context.Context, field graphql.CollectedField, obj *types.Invitation) (ret graphql.Marshaler) { func (ec *executionContext) _Invitation_role(ctx context.Context, field graphql.CollectedField, obj *types.Invitation) (ret graphql.Marshaler) {
return graphql.ResolveField( return graphql.ResolveField(
ctx, ctx,
@@ -4906,6 +4951,8 @@ func (ec *executionContext) fieldContext_InvitationEdge_node(_ context.Context,
return ec.fieldContext_Invitation_id(ctx, field) return ec.fieldContext_Invitation_id(ctx, field)
case "email": case "email":
return ec.fieldContext_Invitation_email(ctx, field) return ec.fieldContext_Invitation_email(ctx, field)
case "fullName":
return ec.fieldContext_Invitation_fullName(ctx, field)
case "role": case "role":
return ec.fieldContext_Invitation_role(ctx, field) return ec.fieldContext_Invitation_role(ctx, field)
case "expiresAt": case "expiresAt":
@@ -7637,7 +7684,7 @@ func (ec *executionContext) _Organization_invitations(ctx context.Context, field
ec.fieldContext_Organization_invitations, ec.fieldContext_Organization_invitations,
func(ctx context.Context) (any, error) { func(ctx context.Context) (any, error) {
fc := graphql.GetFieldContext(ctx) fc := graphql.GetFieldContext(ctx)
return ec.resolvers.Organization().Invitations(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["status"].(*coredata.InvitationStatus)) return ec.resolvers.Organization().Invitations(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["status"].(*coredata.InvitationStatus), fc.Args["orderBy"].(*types.InvitationOrderBy))
}, },
nil, nil,
ec.marshalOInvitationConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐInvitationConnection, ec.marshalOInvitationConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐInvitationConnection,
@@ -12747,7 +12794,7 @@ func (ec *executionContext) unmarshalInputInviteMemberInput(ctx context.Context,
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"organizationId", "email", "fullName"} fieldsInOrder := [...]string{"organizationId", "email", "fullName", "role", "createPeople"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@@ -12775,6 +12822,20 @@ func (ec *executionContext) unmarshalInputInviteMemberInput(ctx context.Context,
return it, err return it, err
} }
it.FullName = data it.FullName = data
case "role":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("role"))
data, err := ec.unmarshalNMembershipRole2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐMembershipRole(ctx, v)
if err != nil {
return it, err
}
it.Role = data
case "createPeople":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("createPeople"))
data, err := ec.unmarshalNBoolean2bool(ctx, v)
if err != nil {
return it, err
}
it.CreatePeople = data
} }
} }
@@ -14320,6 +14381,11 @@ func (ec *executionContext) _Invitation(ctx context.Context, sel ast.SelectionSe
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1) atomic.AddUint32(&out.Invalids, 1)
} }
case "fullName":
out.Values[i] = ec._Invitation_fullName(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "role": case "role":
out.Values[i] = ec._Invitation_role(ctx, field, obj) out.Values[i] = ec._Invitation_role(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {

View File

@@ -67,6 +67,7 @@ func NewInvitation(invitation *coredata.Invitation) *Invitation {
ID: invitation.ID, ID: invitation.ID,
Email: invitation.Email, Email: invitation.Email,
Role: invitation.Role, Role: invitation.Role,
FullName: invitation.FullName,
ExpiresAt: invitation.ExpiresAt, ExpiresAt: invitation.ExpiresAt,
AcceptedAt: invitation.AcceptedAt, AcceptedAt: invitation.AcceptedAt,
CreatedAt: invitation.CreatedAt, CreatedAt: invitation.CreatedAt,

View File

@@ -168,6 +168,7 @@ func (this Identity) GetID() gid.GID { return this.ID }
type Invitation struct { type Invitation struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
Email mail.Addr `json:"email"` Email mail.Addr `json:"email"`
FullName string `json:"fullName"`
Role coredata.MembershipRole `json:"role"` Role coredata.MembershipRole `json:"role"`
ExpiresAt time.Time `json:"expiresAt"` ExpiresAt time.Time `json:"expiresAt"`
AcceptedAt *time.Time `json:"acceptedAt,omitempty"` AcceptedAt *time.Time `json:"acceptedAt,omitempty"`
@@ -188,6 +189,8 @@ type InviteMemberInput struct {
OrganizationID gid.GID `json:"organizationId"` OrganizationID gid.GID `json:"organizationId"`
Email mail.Addr `json:"email"` Email mail.Addr `json:"email"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
Role coredata.MembershipRole `json:"role"`
CreatePeople bool `json:"createPeople"`
} }
type InviteMemberPayload struct { type InviteMemberPayload struct {

View File

@@ -1048,7 +1048,7 @@ func (r *organizationResolver) Members(ctx context.Context, obj *types.Organizat
} }
// Invitations is the resolver for the invitations field. // Invitations is the resolver for the invitations field.
func (r *organizationResolver) Invitations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, status *coredata.InvitationStatus) (*types.InvitationConnection, error) { func (r *organizationResolver) Invitations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, status *coredata.InvitationStatus, orderBy *types.InvitationOrderBy) (*types.InvitationConnection, error) {
if gqlutils.OnlyTotalCountSelected(ctx) { if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.InvitationConnection{ return &types.InvitationConnection{
Resolver: r, Resolver: r,