Files
workflow-builder/src/utils/tutorialManager.ts
T
Nikhil-Doye 229035e79f Add interactive tutorial and connection management features
- Introduced a new InteractiveTutorial component to guide users through creating workflows step-by-step.
- Added ConnectionSuggestions and ConnectionValidation components to assist users in establishing valid connections between nodes.
- Enhanced the WorkflowEditor to integrate connection management features, improving user experience and workflow design.
- Updated onboarding system to include tutorial prompts, encouraging users to engage with new features.
- Implemented utility functions for managing connection suggestions and validations, ensuring robust workflow integrity.
2025-10-22 18:39:06 -04:00

272 lines
6.0 KiB
TypeScript

/**
* Tutorial Manager - Handles interactive tutorial state and progress
*/
export interface TutorialState {
isActive: boolean;
currentStep: number;
completedSteps: string[];
skippedSteps: string[];
startTime: number;
endTime?: number;
userActions: TutorialAction[];
}
export interface TutorialAction {
stepId: string;
action: string;
timestamp: number;
success: boolean;
data?: any;
}
export interface TutorialProgress {
stepsCompleted: number;
totalSteps: number;
completionRate: number;
timeSpent: number;
userEngagement: number;
}
class TutorialManager {
private static instance: TutorialManager;
private state: TutorialState;
private listeners: ((state: TutorialState) => void)[] = [];
private constructor() {
this.state = {
isActive: false,
currentStep: 0,
completedSteps: [],
skippedSteps: [],
startTime: 0,
userActions: [],
};
}
static getInstance(): TutorialManager {
if (!TutorialManager.instance) {
TutorialManager.instance = new TutorialManager();
}
return TutorialManager.instance;
}
/**
* Start a new tutorial session
*/
startTutorial(): void {
this.state = {
isActive: true,
currentStep: 0,
completedSteps: [],
skippedSteps: [],
startTime: Date.now(),
userActions: [],
};
this.notifyListeners();
}
/**
* Complete the current tutorial
*/
completeTutorial(): void {
this.state.isActive = false;
this.state.endTime = Date.now();
this.saveTutorialData();
this.notifyListeners();
}
/**
* Skip the current step
*/
skipStep(stepId: string): void {
if (!this.state.skippedSteps.includes(stepId)) {
this.state.skippedSteps.push(stepId);
}
this.nextStep();
}
/**
* Mark a step as completed
*/
completeStep(stepId: string, action?: string, data?: any): void {
if (!this.state.completedSteps.includes(stepId)) {
this.state.completedSteps.push(stepId);
}
this.recordAction(stepId, action || "complete", true, data);
this.nextStep();
}
/**
* Move to the next step
*/
nextStep(): void {
this.state.currentStep += 1;
this.notifyListeners();
}
/**
* Move to the previous step
*/
previousStep(): void {
if (this.state.currentStep > 0) {
this.state.currentStep -= 1;
this.notifyListeners();
}
}
/**
* Record a user action
*/
recordAction(
stepId: string,
action: string,
success: boolean,
data?: any
): void {
this.state.userActions.push({
stepId,
action,
timestamp: Date.now(),
success,
data,
});
}
/**
* Check if a step is completed
*/
isStepCompleted(stepId: string): boolean {
return this.state.completedSteps.includes(stepId);
}
/**
* Check if a step was skipped
*/
isStepSkipped(stepId: string): boolean {
return this.state.skippedSteps.includes(stepId);
}
/**
* Get current tutorial state
*/
getState(): TutorialState {
return { ...this.state };
}
/**
* Get tutorial progress
*/
getProgress(): TutorialProgress {
const totalSteps = 8; // Total number of tutorial steps
const stepsCompleted = this.state.completedSteps.length;
const timeSpent = this.state.endTime
? this.state.endTime - this.state.startTime
: Date.now() - this.state.startTime;
// Calculate user engagement based on actions taken
const totalActions = this.state.userActions.length;
const successfulActions = this.state.userActions.filter(
(a) => a.success
).length;
const userEngagement =
totalActions > 0 ? (successfulActions / totalActions) * 100 : 0;
return {
stepsCompleted,
totalSteps,
completionRate: (stepsCompleted / totalSteps) * 100,
timeSpent,
userEngagement,
};
}
/**
* Subscribe to tutorial state changes
*/
subscribe(listener: (state: TutorialState) => void): () => void {
this.listeners.push(listener);
return () => {
const index = this.listeners.indexOf(listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
};
}
/**
* Notify all listeners of state changes
*/
private notifyListeners(): void {
this.listeners.forEach((listener) => listener(this.state));
}
/**
* Save tutorial data to localStorage
*/
private saveTutorialData(): void {
const tutorialData = {
...this.state,
completedAt: new Date().toISOString(),
progress: this.getProgress(),
};
localStorage.setItem("tutorialData", JSON.stringify(tutorialData));
}
/**
* Load tutorial data from localStorage
*/
loadTutorialData(): any {
const data = localStorage.getItem("tutorialData");
return data ? JSON.parse(data) : null;
}
/**
* Reset tutorial data
*/
resetTutorial(): void {
localStorage.removeItem("tutorialData");
this.state = {
isActive: false,
currentStep: 0,
completedSteps: [],
skippedSteps: [],
startTime: 0,
userActions: [],
};
this.notifyListeners();
}
/**
* Check if user has completed tutorial before
*/
hasCompletedTutorial(): boolean {
const data = this.loadTutorialData();
return data && data.progress && data.progress.completionRate >= 80;
}
/**
* Get tutorial recommendations based on user behavior
*/
getRecommendations(): string[] {
const progress = this.getProgress();
const recommendations: string[] = [];
if (progress.completionRate < 50) {
recommendations.push("Try the interactive tutorial to learn the basics");
} else if (progress.completionRate < 80) {
recommendations.push("Complete the tutorial to unlock advanced features");
} else if (progress.userEngagement < 70) {
recommendations.push("Explore different node types and configurations");
} else {
recommendations.push("You're ready to create complex workflows!");
}
return recommendations;
}
}
export const tutorialManager = TutorialManager.getInstance();