Skip to content

Instantly share code, notes, and snippets.

@sr-shifu
Last active March 19, 2024 18:55
State machine config example
// Define the types for our state and step names
type WizardState = {
selectedProduct: "car" | "home" | "bundle";
customerDetails: {
firstName: string;
lastName: string;
};
// do not strictly type all the details here
carInsuranceCoverage: object;
homeInsuranceCoverage: object;
carDetails: {
licenseNumber: string;
};
propertyDetails: {
address: string;
};
insurancePeriod: {
startDate: Date;
endDate: Date;
};
paymentMethod: string;
};
type StepNames =
| "enterCustomerDetails"
| "selectInsuranceProduct"
| "specifyCarInsuranceCoverage"
| "specifyCarInsuranceDetails"
| "specifyHomeInsuranceCoverage"
| "specifyHomeInsuranceDetails"
| "setInsurancePeriod"
| "authenticate"
| "payment"
| "confirmation";
// Define the state machine configuration
const stateMachineConfig = {
initialStep: "enterCustomerDetails",
steps: {
enterCustomerDetails: {
canAdvance: (currentState) =>
currentState.customerDetails?.firstName != null &&
currentState.customerDetails?.lastName != null,
choices: {
selectInsuranceProduct: {}, // Move to the next step after entering details
},
},
selectInsuranceProduct: {
canAdvance: (currentState) => currentState.selectedProduct != null,
choices: {
// Move to specifying coverage after selecting insurance product type
specifyCarInsuranceCoverage: {
when: (currentState) =>
currentState.selectedProduct === "car" ||
currentState.selectedProduct === "bundle",
},
specifyHomeInsuranceCoverage: {
when: (currentState) => currentState.selectedProduct === "home",
},
},
},
specifyCarInsuranceCoverage: {
// For demo purposes, always allow advancing assuming there is always some default coverage selected
choices: {
specifyCarInsuranceDetails: {}, // Move to specifying details after selecting coverage
},
},
specifyCarInsuranceDetails: {
canAdvance: (currentState) =>
currentState.carDetails?.licenseNumber != null,
choices: {
setInsurancePeriod: {},
},
},
specifyHomeInsuranceCoverage: {
// For demo purposes, always allow advancing assuming there is always some default coverage selected
choices: {
specifyCarInsuranceDetails: {}, // Move to specifying details after selecting coverage
},
},
specifyHomeInsuranceDetails: {
canAdvance: (currentState) =>
currentState.propertyDetails?.address != null,
choices: {
setInsurancePeriod: {},
},
},
setInsurancePeriod: {
canAdvance: (currentState) =>
currentState.insurancePeriod?.startDate <
currentState.insurancePeriod?.endDate,
choices: {
// go to auth screen if user is not logged in
authenticate: {
when: () => !localStorage.getItem("userAuth"),
},
// otherwise, always fallback to paymet step (no need to specify when conditiom)
payment: {},
},
},
authenticate: {
canAdvance: () => !!localStorage.getItem("userAuth"),
choices: {
payment: {}, // Move to payment after authentication
},
},
payment: {
canAdvance: (currentState) => currentState.paymentMethod != null,
choices: {
confirmation: {}, // Move to confirmation step after payment
},
},
confirmation: {
isTerminal: true, // This is the final step
},
},
views: {
enterCustomerDetails: EnterCustomerDetailsComponent,
selectInsuranceProduct: SelectInsuranceProductComponent,
specifyCarInsuranceCoverage: SpecifyCarCoverageComponent,
specifyCarInsuranceDetails: SpecifyCarDetailsComponent,
specifyHomeInsuranceCoverage: SpecifyHomeCoverageComponent,
specifyHomeInsuranceDetails: SpecifyHomeDetailsComponent,
setInsurancePeriod: SetInsurancePeriodComponent,
authenticate: AuthComponent,
payment: PaymentComponent,
confirmation: ConfirmationComponent,
},
} satisfies StateMachineConfig<WizardState, StepNames>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment