Created
September 30, 2021 19:12
-
-
Save waldothedeveloper/8a061a61733e939a627cac1c0db4fedd to your computer and use it in GitHub Desktop.
Step Online Form
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { createMachine, assign } from "xstate" | |
| import { | |
| zipCodeRegex, | |
| verifyZipcode, | |
| formatPhoneNumber, | |
| validatePhoneNumber, | |
| } from "../../utils/quiz_form_validation" | |
| // | |
| export const stepMachine = createMachine( | |
| { | |
| id: "stepMachine", | |
| initial: "one", | |
| context: { | |
| errorMessage: "", | |
| type_of_project: "", | |
| zipcode: "", | |
| verifiedAddress: undefined, | |
| address: undefined, | |
| emergency: "", | |
| nature_of_problem: "", | |
| ac_unit_age: "", | |
| project_status: "", | |
| moving: false, | |
| project_deadline: "", | |
| covered_by_insurance: false, | |
| owner_or_authorized_person: true, | |
| project_notes: "", | |
| fullname: "", | |
| phone: "", | |
| phoneRegex: undefined, | |
| completed: { | |
| one: false, | |
| two: false, | |
| three: false, | |
| four: false, | |
| five: false, | |
| six: false, | |
| seven: false, | |
| eight: false, | |
| nine: false, | |
| ten: false, | |
| eleven: false, | |
| twelve: false, | |
| thirteen: false, | |
| }, | |
| }, | |
| states: { | |
| //! # 1 type of project | |
| one: { | |
| on: { | |
| NEXT: { | |
| target: "two", | |
| cond: ctx => { | |
| const typeOfProject = ctx.type_of_project | |
| return ( | |
| typeOfProject === "Install a Central Air Conditioning System" || | |
| typeOfProject === | |
| "Repair or Service a Central Air Conditioning System" | |
| ) | |
| }, | |
| }, | |
| CONFIRM_TYPE_OF_PROJECT: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CONFIRM_TYPE_OF_PROJECT") return {} | |
| return { | |
| type_of_project: event.value, | |
| completed: { | |
| ...context.completed, | |
| one: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| }, | |
| }, | |
| //! # 2 zipcode | |
| two: { | |
| on: { | |
| NEXT: "three", | |
| PREV: "one", | |
| //! User can always change zip code in this state | |
| EDIT_ZIPCODE: { | |
| actions: ["assignZipCodeToContext"], | |
| }, | |
| }, | |
| // Different states for pending/valid/invalid/validating | |
| initial: "pending", | |
| states: { | |
| pending: { | |
| always: { | |
| cond: "regexZipCode", | |
| target: "validating", | |
| }, | |
| }, | |
| validating: { | |
| invoke: { | |
| src: (ctx, e) => verifyZipcode(ctx.zipcode), | |
| onDone: [ | |
| { | |
| cond: (ctx, event) => | |
| Object.keys(event.data).length > 0 ?? false, | |
| actions: "validateZipCodeAndSaveToContext", | |
| target: "valid", | |
| }, | |
| { target: "invalid" }, | |
| ], | |
| onError: [{ target: "retry" }], | |
| }, | |
| }, | |
| retry: { | |
| on: { | |
| EDIT_ZIPCODE: { | |
| actions: ["assignZipCodeToContext"], | |
| target: "pending", | |
| }, | |
| }, | |
| }, | |
| valid: { | |
| on: { | |
| EDIT_ZIPCODE: { | |
| actions: ["assignZipCodeToContext"], | |
| target: "pending", | |
| }, | |
| }, | |
| }, | |
| invalid: { | |
| entry: assign({ errorMessage: "Please enter a valid zipcode" }), | |
| on: { | |
| EDIT_ZIPCODE: { | |
| actions: ["assignZipCodeToContext"], | |
| target: "pending", | |
| }, | |
| }, | |
| exit: ["clearErrorMessage"], | |
| }, | |
| }, | |
| }, | |
| // ! # 3 emergency | |
| three: { | |
| on: { | |
| NEXT: { | |
| target: "four", | |
| cond: ctx => { | |
| const emergency = ctx.emergency | |
| return emergency === "YES" || emergency === "NO" | |
| }, | |
| }, | |
| PREV: "two", | |
| CONFIRM_EMERGENCY: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CONFIRM_EMERGENCY") return {} | |
| return { | |
| emergency: event.value, | |
| completed: { | |
| ...context.completed, | |
| three: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| }, | |
| }, | |
| //! # 4. problem nature | |
| four: { | |
| on: { | |
| CONFIRM_PROBLEM_NATURE: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CONFIRM_PROBLEM_NATURE") return {} | |
| return { | |
| nature_of_problem: event.value, | |
| completed: { | |
| ...context.completed, | |
| four: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| NEXT: { | |
| target: "five", | |
| cond: ctx => { | |
| return ctx.nature_of_problem !== "" | |
| }, | |
| }, | |
| PREV: "three", | |
| }, | |
| }, | |
| //! # 5 ac unit age | |
| five: { | |
| on: { | |
| CHANGE: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| ac_unit_age: event.value, | |
| completed: { | |
| ...context.completed, | |
| five: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| NEXT: { | |
| target: "six", | |
| cond: ctx => { | |
| return ctx.ac_unit_age !== "" | |
| }, | |
| }, | |
| PREV: "four", | |
| }, | |
| }, | |
| //! # 6. project status | |
| six: { | |
| on: { | |
| CHANGE: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| project_status: event.value, | |
| completed: { | |
| ...context.completed, | |
| six: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| NEXT: { | |
| target: "seven", | |
| cond: ctx => { | |
| return ctx.project_status !== "" | |
| }, | |
| }, | |
| PREV: "five", | |
| }, | |
| }, | |
| //! # 7. moving into or out | |
| seven: { | |
| on: { | |
| CHANGE: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| moving: event.value, | |
| completed: { | |
| ...context.completed, | |
| seven: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| NEXT: { | |
| target: "eight", | |
| cond: ctx => { | |
| return ctx.moving !== "" | |
| }, | |
| }, | |
| PREV: "six", | |
| }, | |
| }, | |
| //! # 8 project_deadline | |
| eight: { | |
| on: { | |
| CHANGE: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| project_deadline: event.value, | |
| completed: { | |
| ...context.completed, | |
| eight: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| NEXT: { | |
| target: "nine", | |
| cond: ctx => { | |
| return ctx.project_deadline !== "" | |
| }, | |
| }, | |
| PREV: "seven", | |
| }, | |
| }, | |
| //! # 9 covered by insurance | |
| nine: { | |
| on: { | |
| CHANGE: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| covered_by_insurance: event.value, | |
| completed: { | |
| ...context.completed, | |
| nine: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| NEXT: { | |
| target: "ten", | |
| cond: ctx => { | |
| return ( | |
| ctx.covered_by_insurance === "YES" || | |
| ctx.covered_by_insurance === "NO" | |
| ) | |
| }, | |
| }, | |
| PREV: "eight", | |
| }, | |
| }, | |
| //! # 10. owner_or_authorized_person | |
| ten: { | |
| on: { | |
| CHANGE: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| owner_or_authorized_person: event.value, | |
| completed: { | |
| ...context.completed, | |
| ten: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| NEXT: { | |
| target: "eleven", | |
| cond: ctx => { | |
| return ( | |
| ctx.owner_or_authorized_person === "YES" || | |
| ctx.owner_or_authorized_person === "NO" | |
| ) | |
| }, | |
| }, | |
| PREV: "nine", | |
| }, | |
| }, | |
| //! # 11. project_notes | |
| eleven: { | |
| on: { | |
| CHANGE: { | |
| actions: [ | |
| assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| project_notes: event.value, | |
| completed: { | |
| ...context.completed, | |
| eleven: true, | |
| }, | |
| } | |
| }), | |
| ], | |
| }, | |
| NEXT: { | |
| target: "twelve", | |
| cond: ctx => { | |
| return ctx.project_notes !== "" | |
| }, | |
| }, | |
| PREV: "ten", | |
| }, | |
| }, | |
| //! # 12. fullname | |
| twelve: { | |
| initial: "pending", | |
| states: { | |
| pending: { | |
| always: [ | |
| { | |
| target: "completeTask", | |
| cond: (ctx, event) => ctx.fullname.length > 0, | |
| }, | |
| { | |
| target: "incompleteTask", | |
| cond: (ctx, event) => ctx.fullname === "", | |
| }, | |
| ], | |
| }, | |
| completeTask: { | |
| entry: ["makeItTrue"], | |
| on: { | |
| CHANGE: { | |
| actions: ["editFullname"], | |
| target: "pending", | |
| }, | |
| }, | |
| }, | |
| incompleteTask: { | |
| entry: ["makeItFalse"], | |
| on: { | |
| CHANGE: { | |
| actions: ["editFullname"], | |
| target: "pending", | |
| }, | |
| }, | |
| }, | |
| }, | |
| on: { | |
| CHANGE: { | |
| actions: ["editFullname"], | |
| }, | |
| NEXT: { | |
| target: "thirteen", | |
| cond: ctx => { | |
| return ctx.fullname !== "" | |
| }, | |
| }, | |
| PREV: "eleven", | |
| }, | |
| }, | |
| //! # 13. phone number | |
| thirteen: { | |
| on: { | |
| CHANGE: { | |
| actions: ["editPhoneNumber", "showInvalidPhoneNumberErrorMessage"], | |
| }, | |
| NEXT: "fourteen", | |
| PREV: "twelve", | |
| }, | |
| initial: "idle", | |
| states: { | |
| idle: { | |
| always: { | |
| target: "validating", | |
| cond: (ctx, event) => ctx.phone.length > 2, | |
| }, | |
| }, | |
| validating: { | |
| entry: ["makePhoneFalse"], | |
| always: { | |
| target: "valid", | |
| cond: (ctx, event) => validatePhoneNumber(ctx.phone), | |
| }, | |
| }, | |
| valid: { | |
| entry: [ | |
| "makePhoneTrue", | |
| "clearErrorMessage", | |
| assign({ phoneRegex: true }), | |
| ], | |
| on: { | |
| CHANGE: { | |
| target: "idle", | |
| actions: [ | |
| "editPhoneNumber", | |
| "showInvalidPhoneNumberErrorMessage", | |
| ], | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| fourteen: { type: "final" }, | |
| }, | |
| }, | |
| { | |
| actions: { | |
| showInvalidPhoneNumberErrorMessage: assign((ctx, event) => { | |
| return ctx.phone.length > 0 | |
| ? { | |
| phoneRegex: undefined, | |
| errorMessage: "Please enter a valid phone number", | |
| } | |
| : { | |
| phoneRegex: undefined, | |
| errorMessage: "", | |
| } | |
| }), | |
| editPhoneNumber: assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| phone: formatPhoneNumber(event.value), | |
| } | |
| }), | |
| editFullname: assign((context, event) => { | |
| if (event.type !== "CHANGE") return {} | |
| return { | |
| fullname: event.value, | |
| } | |
| }), | |
| makePhoneTrue: assign((context, event) => { | |
| return { | |
| completed: { | |
| ...context.completed, | |
| thirteen: true, | |
| }, | |
| } | |
| }), | |
| makePhoneFalse: assign((context, event) => { | |
| return { | |
| completed: { | |
| ...context.completed, | |
| thirteen: false, | |
| }, | |
| } | |
| }), | |
| makeItTrue: assign((context, event) => { | |
| return { | |
| completed: { | |
| ...context.completed, | |
| twelve: true, | |
| }, | |
| } | |
| }), | |
| makeItFalse: assign((context, event) => { | |
| return { | |
| completed: { | |
| ...context.completed, | |
| twelve: false, | |
| }, | |
| } | |
| }), | |
| clearErrorMessage: assign({ | |
| errorMessage: undefined, | |
| }), | |
| assignErrorMessageToContext: assign((context, event) => { | |
| let _a | |
| _a = event.data | |
| console.log("error Message: ", _a) | |
| if (_a === null || _a === void 0) { | |
| return { | |
| errorMessage: void 0, | |
| } | |
| } | |
| if (_a.message) { | |
| return { | |
| errorMessage: _a.message, | |
| } | |
| } | |
| return { | |
| errorMessage: "An unknown error occurred", | |
| } | |
| }), | |
| assignZipCodeToContext: assign((ctx, event) => { | |
| return { | |
| zipcode: event.value, | |
| verifiedAddress: "pending", | |
| address: undefined, | |
| completed: { | |
| ...ctx.completed, | |
| two: false, | |
| }, | |
| } | |
| }), | |
| validateZipCodeAndSaveToContext: assign((ctx, event) => { | |
| if (Object.keys(event.data).length > 0) { | |
| const { city, state, postal_code } = event.data[ctx.zipcode][0] | |
| return { | |
| address: { | |
| city, | |
| state, | |
| postal_code, | |
| }, | |
| completed: { | |
| ...ctx.completed, | |
| two: true, | |
| }, | |
| } | |
| } | |
| }), | |
| }, | |
| guards: { | |
| regexZipCode: (ctx, event) => | |
| ctx.verifiedAddress !== void 0 && zipCodeRegex.test(ctx.zipcode), | |
| }, | |
| // activities, guards, delays, etc. | |
| } | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment