// Available variables: // - Machine // - interpret // - assign // - send // - sendParent // - spawn // - raise // - actions // - XState (all XState exports) const mapInviteTypeToConnectionType = inviteType => { switch (inviteType) { case "COLLEAGUE": return "COLLEAGUE"; case "ORGANIZATION_CODE": // Legacy type from <=3.X case "PATIENT": // Legacy type from <=3.X case "SECURE_MESSAGE": return "SECURE_MESSAGE"; case "UNKNOWN": default: console.error(`Unexpected inviteType ${inviteType}`); return "SECURE_MESSAGE"; } }; const associateInvite = ( accountCreationIntent, accountInviteClientID, inviteCode ) => new Promise(resolve => setTimeout(() => { resolve({ data: { associateInvite: { confirmationScreen: { body: "", buttonText: "Continue", entityProfile: { url: "https://invite.spruce-dev.com/e/18M23V27KJ800" }, imageURL: "https://msg-media.spruce-dev.com/media/e867db5a-ba30-9dba-7cc0-38cb-f9887032?mimetype=image%2Fpng", photoStyle: "BORDERED_CIRCLE", title: "You're joining Sara Brown, PsyD" }, errorCode: null, errorMessage: null, // inviteType: "ORGANIZATION_CODE", // inviteType: "SECURE_MESSAGE", inviteType: "COLLEAGUE", phoneNumberVerificationText: null, values: [ { key: "client_data", value: '{"patient_invite":{"greeting":{"title":"You\'re Joining Sara Brown, PsyD","message":"Let\'s create your account so you can start securely messaging with Sara Brown, PsyD.","button_text":"Get Started"},"org_id":"entity_18M23V27KJ800","org_name":"Sara Brown, PsyD"},"practice_invite":{"greeting":{"title":"You\'re Joining Sara Brown, PsyD","message":"Let\'s create your account so you can start securely messaging with Sara Brown, PsyD.","button_text":"Get Started"},"org_id":"entity_18M23V27KJ800","org_name":"Sara Brown, PsyD","join_as_teammate":false}}' }, { key: "invite_type", value: "ORGANIZATION_CODE" }, { key: "$desktop_url", value: "https://app.spruce-dev.com/?invite=sara-brown" }, { key: "invite_token", value: "sara-brown" } ], verifyPhoneNumber: true } } }); }, 1000) ); const verifyPhoneNumber = phoneNumber => Promise.resolve({ data: { verifyPhoneNumberForAccountCreation: { errorCode: null, errorMessage: null, message: "A verification code has been sent to (630) 915-9986", token: "-XPIFGaWcwroulS8i_5zqQ" } } }); // const checkVerificationCode = verificationCode => // Promise.resolve({ // data: { // checkVerificationCode: { // account: null, // errorCode: "CODE_EXPIRED", // errorMessage: // "The entered code has expired. Please request a new code.", // inviteType: null, // success: false, // verifiedEntityInfo: null // } // } // }); const checkVerificationCode = verificationCode => Promise.resolve({ data: { checkVerificationCode: { account: null, confirmationScreen: null, errorCode: null, errorMessage: null, inviteType: null, success: true, verifiedEntityInfo: null } } }); const detectPresenceOfInviteCode = () => new Promise(resolve => setTimeout(() => { resolve("sara-brown"); }, 200) ); const fetchMachine = Machine({ id: "account-creation", initial: "initialize", context: { inviteCode: "", accountCreationIntent: "PROVIDER", connectionType: "", phoneVerificationToken: "", inviteConfirmationScreen: null, accountInviteClientID: "", firstName: "", lastName: "", shortTitle: "", organizationName: "", dob: {}, gender: "", genderDetail: "", email: "", password: "", errorMessage: "" }, states: { initialize: { initial: "detectPresenceOfInviteCode", states: { detectPresenceOfInviteCode: { invoke: { id: "detectPresenceOfInviteCode", src: (context, event) => detectPresenceOfInviteCode(), onDone: { target: "callingAssociateInvite", internal: true, actions: assign({ inviteCode: (context, event) => event.data }) }, onError: { target: "#choosePatientOrProvider", actions: assign({ error: (context, event) => { // In practice, there will never be an error, since we're simply checking the query params for the token and that can't fail console.error(event); } }) } } }, callingAssociateInvite: { invoke: { id: "associateInvite", src: (context, event) => associateInvite( context.accountCreationIntent, context.accountInviteClientID, context.inviteCode ), onDone: [ { target: "#choosePatientOrProvider", actions: assign({ connectionType: (context, event) => mapInviteTypeToConnectionType( event.data.data.associateInvite.inviteType ) }), cond: (context, event) => { if ( !event || !event.data || !event.data.data || !event.data.data.associateInvite || !event.data.data.associateInvite.inviteType ) { return false; } const connectionType = mapInviteTypeToConnectionType( event.data.data.associateInvite.inviteType ); switch (connectionType) { case "SECURE_MESSAGE": return true; case "COLLEAGUE": return false; } } }, { target: "#confirmPracticeBeingJoined", actions: assign({ connectionType: (context, event) => mapInviteTypeToConnectionType( event.data.data.associateInvite.inviteType ), confirmationScreen: (context, event) => mapInviteTypeToConnectionType( event.data.data.associateInvite.confirmationScreen ) }), cond: (context, event) => { if ( !event || !event.data || !event.data.data || !event.data.data.associateInvite || !event.data.data.associateInvite.inviteType ) { return false; } const connectionType = mapInviteTypeToConnectionType( event.data.data.associateInvite.inviteType ); switch (connectionType) { case "SECURE_MESSAGE": return false; case "COLLEAGUE": return true; } } } ], onError: { target: "#choosePatientOrProvider", actions: assign({ error: (context, event) => { console.error(event); } }) } } } } }, choosePatientOrProvider: { id: "choosePatientOrProvider", on: { PATIENT: { target: "enterPhoneNumber", actions: "markAsPatient" }, PROVIDER: [ { target: "enterPhoneNumber", actions: "markAsProvider", cond: context => context.inviteType === "COLLEAGUE" }, { target: "chooseJoinExistingClinicOrCreateNewClinic", actions: "markAsProvider" } ] } }, chooseJoinExistingClinicOrCreateNewClinic: { id: "chooseJoinExistingClinicOrCreateNewClinic", on: { JOIN_EXISTING: { target: "joinExistingClinic", actions: "markAsPatient" }, CREATE_NEW: { target: "enterPhoneNumber", actions: "markAsProvider" } } }, // The joinExistingClinic state is just going to explain how to join an existing practice and link to the help center joinExistingClinic: { on: { BACK: { target: "chooseJoinExistingClinicOrCreateNewClinic" } } }, enterPhoneNumber: { id: "enterPhoneNumber", initial: "awaitPhoneNumberEntry", states: { awaitPhoneNumberEntry: { on: { CHANGE: { actions: assign({ phoneNumber: (ctx, e) => e.data }) }, SUBMIT: { target: "sendingPhoneVerificationCode", internal: true }, BACK: "#choosePatientOrProvider" } }, sendingPhoneVerificationCode: { invoke: { id: "verifyPhoneNumber", src: (context, event) => verifyPhoneNumber(context.phoneNumber), onDone: { target: "#enterPhoneVerificationCode", internal: false }, onError: { target: "error", actions: assign({ error: (context, event) => { console.error(event); } }) } } }, error: { type: "final" } } }, enterPhoneVerificationCode: { id: "enterPhoneVerificationCode", initial: "awaitVerificationCodeEntry", states: { awaitVerificationCodeEntry: { on: { CHANGE: { actions: assign({ verificationCode: (ctx, e) => e.data }) }, SUBMIT: { target: "verifyingCode", internal: true } } }, verifyingCode: { invoke: { id: "checkVerificationCode", src: (context, event) => checkVerificationCode(context.verificationCode), onDone: [ { target: "awaitVerificationCodeEntry", internal: true, actions: assign({ errorMessage: (context, event) => event.data.data.checkVerificationCode.errorMessage }), cond: (context, event) => event && event.data && event.data.data && event.data.data.checkVerificationCode && event.data.data.checkVerificationCode.errorMessage }, { target: "#confirmPracticeBeingJoined", internal: false, actions: assign({ confirmationScreen: (context, event) => event.data.data.checkVerificationCode.confirmationScreen }), cond: (context, event) => event && event.data && event.data.data && event.data.data.checkVerificationCode && event.data.data.checkVerificationCode.confirmationScreen }, { target: "#enterPatientDemographics", cond: (context, event) => context.accountCreationIntent === "PATIENT" }, { target: "#enterOrgName", cond: (context, event) => context.accountCreationIntent === "PROVIDER" && context.connectionType === "SECURE_MESSAGE" }, { target: "#enterProviderDemographics", cond: (context, event) => context.accountCreationIntent === "PROVIDER" && context.connectionType === "COLLEAGUE" } ], onError: { target: "awaitVerificationCodeEntry", internal: true, actions: assign({ error: (context, event) => { console.error(event); } }) } } } } }, confirmPracticeBeingJoined: { id: "confirmPracticeBeingJoined", on: { CONFIRM: [ { target: "enterPhoneNumber", cond: ctx => ctx.connectionType === "COLLEAGUE" }, { target: "enterProviderDemographics", cond: ctx => ctx.accountCreationIntent === "PROVIDER" }, { target: "enterPatientDemographics", cond: ctx => ctx.accountCreationIntent === "PATIENT" } ] } }, enterOrgName: { id: "enterOrgName", on: { CHANGE: { actions: assign({ organizationName: (ctx, e) => e.data }) }, SUBMIT: "enterProviderDemographics", BACK: "enterPhoneVerificationCode" } }, enterPatientDemographics: { id: "enterPatientDemographics", on: { CHANGE_FIRST_NAME: { actions: assign({ firstName: (ctx, e) => e.data }) }, CHANGE_LAST_NAME: { actions: assign({ lastName: (ctx, e) => e.data }) }, CHANGE_DOB: { actions: assign({ dob: (ctx, e) => e.data }) }, CHANGE_GENDER: { actions: assign({ gender: (ctx, e) => e.data }) }, CHANGE_GENDER_DETAIL: { actions: assign({ genderDetail: (ctx, e) => e.data }) }, SUBMIT: "enterEmailAndPassword", BACK: "enterPhoneVerificationCode" } }, enterProviderDemographics: { id: "enterProviderDemographics", on: { CHANGE_FIRST_NAME: { actions: assign({ firstName: (ctx, e) => e.data }) }, CHANGE_LAST_NAME: { actions: assign({ lastName: (ctx, e) => e.data }) }, CHANGE_SHORT_TITLE: { actions: assign({ shortTitle: (ctx, e) => e.data }) }, SUBMIT: "enterEmailAndPassword", BACK: "enterPhoneVerificationCode" } }, enterEmailAndPassword: { on: { CHANGE_EMAIL: { actions: assign({ email: (ctx, e) => e.data }) }, CHANGE_PASSWORD: { actions: assign({ password: (ctx, e) => e.data }) }, SUBMIT: [ { target: "providerAccountCreated", cond: ctx => ctx.connectionType === "COLLEAGUE" || ctx.accountCreationIntent === "PROVIDER" }, { target: "patientAccountCreated", cond: ctx => ctx.accountCreationIntent === "PATIENT" } ] } }, patientAccountCreated: { type: "final" }, providerAccountCreated: { type: "final" } }, actions: { markAsProvider: assign({ accountCreationIntent: "PROVIDER" }), markAsPatient: assign({ accountCreationIntent: "PATIENT" }) } });