mirror of
https://github.com/fleetbase/fleetbase.git
synced 2025-12-19 14:18:57 +00:00
feat: Add lifecycle hooks support to onboarding orchestrator
- Add onFlowWillStart, onFlowDidStart, onStepWillChange, onStepDidChange, onFlowWillEnd, onFlowDidEnd hooks - Hooks are optional and backward compatible with existing flows - Add getCurrentPath() and isStepInPath() helper methods for multi-path flows - Support dynamic next() functions (already existed, now documented) - Maintain full backward compatibility with default@v1 flow
This commit is contained in:
@@ -12,14 +12,26 @@ export default class OnboardingOrchestratorService extends Service {
|
||||
@tracked history = [];
|
||||
@tracked sessionId = null;
|
||||
|
||||
start(flowId = null, opts = {}) {
|
||||
async start(flowId = null, opts = {}) {
|
||||
const flow = this.onboardingRegistry.getFlow(flowId ?? this.onboardingRegistry.defaultFlow);
|
||||
if (!flow) throw new Error(`Onboarding flow '${flowId}' not found`);
|
||||
|
||||
this.flow = flow;
|
||||
this.wrapper = flow.wrapper || null;
|
||||
this.sessionId = opts.sessionId || null;
|
||||
this.history = [];
|
||||
this.goto(flow.entry);
|
||||
|
||||
// Execute onFlowWillStart hook if defined
|
||||
if (typeof this.flow.onFlowWillStart === 'function') {
|
||||
await this.flow.onFlowWillStart(this.flow, this);
|
||||
}
|
||||
|
||||
await this.goto(flow.entry);
|
||||
|
||||
// Execute onFlowDidStart hook if defined
|
||||
if (typeof this.flow.onFlowDidStart === 'function') {
|
||||
await this.flow.onFlowDidStart(this.flow, this);
|
||||
}
|
||||
}
|
||||
|
||||
async goto(stepId) {
|
||||
@@ -27,27 +39,43 @@ export default class OnboardingOrchestratorService extends Service {
|
||||
const step = this.flow.steps.find((s) => s.id === stepId);
|
||||
if (!step) throw new Error(`Step '${stepId}' not found`);
|
||||
|
||||
// Execute onStepWillChange hook if defined
|
||||
const previousStep = this.current;
|
||||
if (typeof this.flow.onStepWillChange === 'function') {
|
||||
await this.flow.onStepWillChange(step, previousStep, this);
|
||||
}
|
||||
|
||||
// Guard function - skip step if guard returns false
|
||||
if (typeof step.guard === 'function' && !step.guard(this.onboardingContext)) {
|
||||
return this.next();
|
||||
}
|
||||
|
||||
// beforeEnter lifecycle hook
|
||||
if (typeof step.beforeEnter === 'function') {
|
||||
await step.beforeEnter(this.onboardingContext);
|
||||
}
|
||||
|
||||
this.current = step;
|
||||
|
||||
// Execute onStepDidChange hook if defined
|
||||
if (typeof this.flow.onStepDidChange === 'function') {
|
||||
await this.flow.onStepDidChange(this.current, previousStep, this);
|
||||
}
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (!this.flow || !this.current) return;
|
||||
|
||||
const leaving = this.current;
|
||||
|
||||
// afterLeave lifecycle hook
|
||||
if (typeof leaving.afterLeave === 'function') {
|
||||
await leaving.afterLeave(this.onboardingContext);
|
||||
}
|
||||
|
||||
if (!this.history.includes(leaving)) this.history.push(leaving);
|
||||
|
||||
// Support both string and function for next property
|
||||
let nextId;
|
||||
if (typeof leaving.next === 'function') {
|
||||
nextId = leaving.next(this.onboardingContext);
|
||||
@@ -55,8 +83,20 @@ export default class OnboardingOrchestratorService extends Service {
|
||||
nextId = leaving.next;
|
||||
}
|
||||
|
||||
// If no next step, flow is complete
|
||||
if (!nextId) {
|
||||
// Execute onFlowWillEnd hook if defined
|
||||
if (typeof this.flow.onFlowWillEnd === 'function') {
|
||||
await this.flow.onFlowWillEnd(leaving, this);
|
||||
}
|
||||
|
||||
this.current = null; // finished
|
||||
|
||||
// Execute onFlowDidEnd hook if defined
|
||||
if (typeof this.flow.onFlowDidEnd === 'function') {
|
||||
await this.flow.onFlowDidEnd(leaving, this);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -70,4 +110,31 @@ export default class OnboardingOrchestratorService extends Service {
|
||||
this.history = this.history.slice(0, -1);
|
||||
await this.goto(prev.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current path (for flows with multiple paths)
|
||||
* This is a helper method that can be used by flows to determine the current path
|
||||
*/
|
||||
getCurrentPath() {
|
||||
if (!this.flow || !this.flow.paths) return null;
|
||||
|
||||
// Determine path based on context or current step
|
||||
for (const [pathId, pathDef] of Object.entries(this.flow.paths)) {
|
||||
if (pathDef.steps && pathDef.steps.some(s => s.id === this.current?.id)) {
|
||||
return pathDef;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a step is in the current path
|
||||
*/
|
||||
isStepInPath(stepId) {
|
||||
const currentPath = this.getCurrentPath();
|
||||
if (!currentPath) return true; // If no paths defined, all steps are valid
|
||||
|
||||
return currentPath.steps?.some(s => s.id === stepId) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user