Fix invite user dialog create people + connection prepend edge

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2026-01-02 15:50:19 +01:00
committed by Bryan Frimin
parent b09a49b7f9
commit 26371857e9
11 changed files with 33 additions and 76 deletions

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<6067ce11fa959cba895d2b10862592de>> * @generated SignedSource<<37b59716fa1aba5ed805ce03278ce01d>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -111,13 +111,7 @@ return {
"kind": "RequiredField", "kind": "RequiredField",
"field": { "field": {
"alias": "invitations", "alias": "invitations",
"args": [ "args": null,
{
"kind": "Variable",
"name": "orderBy",
"variableName": "order"
}
],
"concreteType": "InvitationConnection", "concreteType": "InvitationConnection",
"kind": "LinkedField", "kind": "LinkedField",
"name": "__InvitationListFragment_invitations_connection", "name": "__InvitationListFragment_invitations_connection",
@@ -239,6 +233,6 @@ return {
}; };
})(); })();
(node as any).hash = "837e567be489574a7f0350e26a8e5454"; (node as any).hash = "6a1b77bc8b0e5f345de9865ec78ef7e9";
export default node; export default node;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<6d84f6b0d10bbae09a14ecc5d84c2814>> * @generated SignedSource<<26a05edf635e281469d5498e24879e00>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -355,9 +355,7 @@ return {
{ {
"alias": null, "alias": null,
"args": (v13/*: any*/), "args": (v13/*: any*/),
"filters": [ "filters": [],
"orderBy"
],
"handle": "connection", "handle": "connection",
"key": "InvitationListFragment_invitations", "key": "InvitationListFragment_invitations",
"kind": "LinkedHandle", "kind": "LinkedHandle",
@@ -383,6 +381,6 @@ return {
}; };
})(); })();
(node as any).hash = "837e567be489574a7f0350e26a8e5454"; (node as any).hash = "6a1b77bc8b0e5f345de9865ec78ef7e9";
export default node; export default node;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<438f2d019c7a08242f35c048016a97e1>> * @generated SignedSource<<14b3e1990be5f8333046dffcada56516>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,7 +11,6 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type MembershipRole = "ADMIN" | "AUDITOR" | "EMPLOYEE" | "OWNER" | "VIEWER"; export type MembershipRole = "ADMIN" | "AUDITOR" | "EMPLOYEE" | "OWNER" | "VIEWER";
export type InviteMemberInput = { export type InviteMemberInput = {
createPeople: boolean;
email: string; email: string;
fullName: string; fullName: string;
organizationId: string; organizationId: string;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<5854e660d90c68e5ec01199e55c42a41>> * @generated SignedSource<<70828342a675d5b198c87a4a00455155>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -197,10 +197,7 @@ v17 = {
"storageKey": null "storageKey": null
} }
] ]
}, };
v18 = [
"orderBy"
];
return { return {
"fragment": { "fragment": {
"argumentDefinitions": (v0/*: any*/), "argumentDefinitions": (v0/*: any*/),
@@ -407,7 +404,9 @@ return {
{ {
"alias": null, "alias": null,
"args": (v6/*: any*/), "args": (v6/*: any*/),
"filters": (v18/*: any*/), "filters": [
"orderBy"
],
"handle": "connection", "handle": "connection",
"key": "MemberListFragment_members", "key": "MemberListFragment_members",
"kind": "LinkedHandle", "kind": "LinkedHandle",
@@ -493,7 +492,7 @@ return {
{ {
"alias": null, "alias": null,
"args": (v6/*: any*/), "args": (v6/*: any*/),
"filters": (v18/*: any*/), "filters": [],
"handle": "connection", "handle": "connection",
"key": "InvitationListFragment_invitations", "key": "InvitationListFragment_invitations",
"kind": "LinkedHandle", "kind": "LinkedHandle",

View File

@@ -26,7 +26,7 @@ const fragment = graphql`
before: $before before: $before
orderBy: $order orderBy: $order
) )
@connection(key: "InvitationListFragment_invitations") @connection(key: "InvitationListFragment_invitations", filters: [])
@required(action: THROW) { @required(action: THROW) {
__id __id
totalCount totalCount

View File

@@ -8,7 +8,6 @@ import {
Option, Option,
DialogFooter, DialogFooter,
Button, Button,
Checkbox,
useDialogRef, useDialogRef,
} from "@probo/ui"; } from "@probo/ui";
import { Controller } from "react-hook-form"; import { Controller } from "react-hook-form";
@@ -57,7 +56,6 @@ const schema = z.object({
role: z role: z
.enum(["OWNER", "ADMIN", "FULL", "VIEWER", "AUDITOR", "EMPLOYEE"]) .enum(["OWNER", "ADMIN", "FULL", "VIEWER", "AUDITOR", "EMPLOYEE"])
.default("VIEWER"), .default("VIEWER"),
createPeople: z.boolean().default(false),
}); });
type InviteUserDialogProps = PropsWithChildren<{ type InviteUserDialogProps = PropsWithChildren<{
@@ -85,13 +83,14 @@ export function InviteUserDialog(props: InviteUserDialogProps) {
const { register, handleSubmit, formState, reset, control } = const { register, handleSubmit, formState, reset, control } =
useFormWithSchema(schema, { useFormWithSchema(schema, {
defaultValues: { role: "VIEWER", createPeople: false }, defaultValues: { role: "VIEWER" },
}); });
const onSubmit = handleSubmit((data) => { const onSubmit = handleSubmit((data) => {
const connectionId = ConnectionHandler.getConnectionID( const connectionId = ConnectionHandler.getConnectionID(
organizationId, organizationId,
"InvitationListFragment_invitations", "InvitationListFragment_invitations",
{},
); );
inviteUser({ inviteUser({
variables: { variables: {
@@ -100,7 +99,6 @@ export function InviteUserDialog(props: InviteUserDialogProps) {
email: data.email, email: data.email,
fullName: data.fullName, fullName: data.fullName,
role: data.role, role: data.role,
createPeople: data.createPeople,
}, },
connections: [connectionId], connections: [connectionId],
}, },
@@ -189,33 +187,6 @@ export function InviteUserDialog(props: InviteUserDialogProps) {
)} )}
/> />
</Field> </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> </DialogContent>
<DialogFooter> <DialogFooter>
<Button type="submit" disabled={isInviting}> <Button type="submit" disabled={isInviting}>

View File

@@ -90,6 +90,12 @@ type (
AttributeRole *string AttributeRole *string
AutoSignupEnabled *bool AutoSignupEnabled *bool
} }
CreateInvitationRequest struct {
Email mail.Addr
FullName string
Role coredata.MembershipRole
}
) )
const ( const (
@@ -350,9 +356,7 @@ func (s *OrganizationService) CountInvitations(
func (s *OrganizationService) InviteMember( func (s *OrganizationService) InviteMember(
ctx context.Context, ctx context.Context,
organizationID gid.GID, organizationID gid.GID,
emailAddress mail.Addr, req *CreateInvitationRequest,
fullName string,
role coredata.MembershipRole,
) (*coredata.Invitation, error) { ) (*coredata.Invitation, error) {
var ( var (
scope = coredata.NewScopeFromObjectID(organizationID) scope = coredata.NewScopeFromObjectID(organizationID)
@@ -360,9 +364,9 @@ func (s *OrganizationService) InviteMember(
invitation = &coredata.Invitation{ invitation = &coredata.Invitation{
ID: gid.New(organizationID.TenantID(), coredata.InvitationEntityType), ID: gid.New(organizationID.TenantID(), coredata.InvitationEntityType),
OrganizationID: organizationID, OrganizationID: organizationID,
Email: emailAddress, Email: req.Email,
FullName: fullName, FullName: req.FullName,
Role: role, Role: req.Role,
Status: coredata.InvitationStatusPending, Status: coredata.InvitationStatusPending,
ExpiresAt: now.Add(s.invitationTokenValidity), ExpiresAt: now.Add(s.invitationTokenValidity),
CreatedAt: now, CreatedAt: now,
@@ -383,7 +387,7 @@ func (s *OrganizationService) InviteMember(
} }
identity := &coredata.Identity{} identity := &coredata.Identity{}
err = identity.LoadByEmail(ctx, tx, emailAddress) err = identity.LoadByEmail(ctx, tx, invitation.Email)
if err != nil && err != coredata.ErrResourceNotFound { if err != nil && err != coredata.ErrResourceNotFound {
return fmt.Errorf("cannot load identity: %w", err) return fmt.Errorf("cannot load identity: %w", err)
} }

View File

@@ -608,7 +608,6 @@ input InviteMemberInput {
email: EmailAddr! email: EmailAddr!
fullName: String! fullName: String!
role: MembershipRole! role: MembershipRole!
createPeople: Boolean!
} }
input UpdateMembershipInput { input UpdateMembershipInput {

View File

@@ -2615,7 +2615,6 @@ input InviteMemberInput {
email: EmailAddr! email: EmailAddr!
fullName: String! fullName: String!
role: MembershipRole! role: MembershipRole!
createPeople: Boolean!
} }
input UpdateMembershipInput { input UpdateMembershipInput {
@@ -12795,7 +12794,7 @@ func (ec *executionContext) unmarshalInputInviteMemberInput(ctx context.Context,
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"organizationId", "email", "fullName", "role", "createPeople"} fieldsInOrder := [...]string{"organizationId", "email", "fullName", "role"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@@ -12830,13 +12829,6 @@ func (ec *executionContext) unmarshalInputInviteMemberInput(ctx context.Context,
return it, err return it, err
} }
it.Role = data 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
} }
} }

View File

@@ -190,7 +190,6 @@ type InviteMemberInput struct {
Email mail.Addr `json:"email"` Email mail.Addr `json:"email"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
Role coredata.MembershipRole `json:"role"` Role coredata.MembershipRole `json:"role"`
CreatePeople bool `json:"createPeople"`
} }
type InviteMemberPayload struct { type InviteMemberPayload struct {

View File

@@ -884,9 +884,11 @@ func (r *mutationResolver) InviteMember(ctx context.Context, input types.InviteM
invitation, err := r.iam.OrganizationService.InviteMember( invitation, err := r.iam.OrganizationService.InviteMember(
ctx, ctx,
input.OrganizationID, input.OrganizationID,
input.Email, &iam.CreateInvitationRequest{
input.FullName, Email: input.Email,
coredata.MembershipRoleViewer, FullName: input.FullName,
Role: input.Role,
},
) )
if err != nil { if err != nil {
var errOrganizationNotFound *iam.ErrOrganizationNotFound var errOrganizationNotFound *iam.ErrOrganizationNotFound