taskgenius_taskgenius-plugin/src/components/features/onboarding/OnboardingView.ts
Quorafind 77dd5f5da5 refactor(build): migrate to TypeScript path aliases and update esbuild to v0.25.9
Replace relative imports (../../../) with TypeScript path aliases (@/)
throughout the codebase for improved readability and maintainability.

- Configure path alias in tsconfig.json with @ mapping to src/
- Update esbuild from v0.13.12 to v0.25.9 for modern features
- Add alias configuration to esbuild.config.mjs for build support
- Refactor build configuration to use context API for watch mode
- Update all component and utility imports to use @/ prefix
- Fix incorrect CSS import paths in habit and settings components
- Maintain consistent import structure across 148 files

This change simplifies import statements, makes the codebase more
maintainable by avoiding deeply nested relative paths, and modernizes
the build toolchain.
2025-08-28 14:02:26 +08:00

538 lines
No EOL
14 KiB
TypeScript

import { ItemView, WorkspaceLeaf, setIcon, ButtonComponent } from "obsidian";
import type TaskProgressBarPlugin from "@/index";
import { t } from "@/translations/helper";
import {
OnboardingConfigManager,
OnboardingConfigMode,
OnboardingConfig,
} from "@/managers/onboarding-manager";
import { SettingsChangeDetector } from "@/services/settings-change-detector";
import { UserLevelSelector } from "./UserLevelSelector";
import { ConfigPreview } from "./ConfigPreview";
import { TaskCreationGuide } from "./TaskCreationGuide";
import { OnboardingComplete } from "./OnboardingComplete";
export const ONBOARDING_VIEW_TYPE = "task-genius-onboarding";
export enum OnboardingStep {
SETTINGS_CHECK = 0, // New: Check if user wants onboarding
WELCOME = 1,
USER_LEVEL_SELECT = 2,
CONFIG_PREVIEW = 3,
TASK_CREATION_GUIDE = 4,
COMPLETE = 5,
}
export interface OnboardingState {
currentStep: OnboardingStep;
selectedConfig?: OnboardingConfig;
skipTaskGuide: boolean;
isCompleting: boolean;
userHasChanges: boolean;
changesSummary: string[];
}
export class OnboardingView extends ItemView {
private plugin: TaskProgressBarPlugin;
private configManager: OnboardingConfigManager;
private settingsDetector: SettingsChangeDetector;
private onComplete: () => void;
private state: OnboardingState;
// Step components
private userLevelSelector: UserLevelSelector;
private configPreview: ConfigPreview;
private taskCreationGuide: TaskCreationGuide;
private onboardingComplete: OnboardingComplete;
// UI Elements
private onboardingHeaderEl: HTMLElement;
private onboardingContentEl: HTMLElement;
private footerEl: HTMLElement;
private nextButton: ButtonComponent;
private backButton: ButtonComponent;
private skipButton: ButtonComponent;
constructor(leaf: WorkspaceLeaf, plugin: TaskProgressBarPlugin, onComplete: () => void) {
super(leaf);
this.plugin = plugin;
this.configManager = new OnboardingConfigManager(plugin);
this.settingsDetector = new SettingsChangeDetector(plugin);
this.onComplete = onComplete;
// Initialize state
this.state = {
currentStep: OnboardingStep.SETTINGS_CHECK,
skipTaskGuide: false,
isCompleting: false,
userHasChanges: this.settingsDetector.hasUserMadeChanges(),
changesSummary: this.settingsDetector.getChangesSummary(),
};
// Initialize components
this.userLevelSelector = new UserLevelSelector(this.configManager);
this.configPreview = new ConfigPreview(this.configManager);
this.taskCreationGuide = new TaskCreationGuide(this.plugin);
this.onboardingComplete = new OnboardingComplete();
}
getViewType(): string {
return ONBOARDING_VIEW_TYPE;
}
getDisplayText(): string {
return t("Task Genius Setup");
}
getIcon(): string {
return "zap";
}
async onOpen() {
this.createViewStructure();
this.displayCurrentStep();
}
async onClose() {
// Cleanup when view is closed
this.contentEl.empty();
}
/**
* Create the basic view structure
*/
private createViewStructure() {
const container = this.contentEl;
container.empty();
container.addClass("onboarding-view");
// Header section
this.onboardingHeaderEl = container.createDiv("onboarding-header");
// Main content section
this.onboardingContentEl = container.createDiv("onboarding-content");
// Footer with navigation buttons
this.footerEl = container.createDiv("onboarding-footer");
this.createFooterButtons();
}
/**
* Create footer navigation buttons
*/
private createFooterButtons() {
const buttonContainer = this.footerEl.createDiv("onboarding-buttons");
// Skip button (shown on appropriate steps)
this.skipButton = new ButtonComponent(buttonContainer)
.setButtonText(t("Skip setup"))
.onClick(() => this.handleSkip());
// Back button
this.backButton = new ButtonComponent(buttonContainer)
.setButtonText(t("Back"))
.onClick(() => this.handleBack());
// Next button
this.nextButton = new ButtonComponent(buttonContainer)
.setButtonText(t("Next"))
.setCta()
.onClick(() => this.handleNext());
}
/**
* Display the current step content
*/
private displayCurrentStep() {
// Clear content
this.onboardingHeaderEl.empty();
this.onboardingContentEl.empty();
// Update button visibility
this.updateButtonStates();
switch (this.state.currentStep) {
case OnboardingStep.SETTINGS_CHECK:
this.displaySettingsCheckStep();
break;
case OnboardingStep.WELCOME:
this.displayWelcomeStep();
break;
case OnboardingStep.USER_LEVEL_SELECT:
this.displayUserLevelSelectStep();
break;
case OnboardingStep.CONFIG_PREVIEW:
this.displayConfigPreviewStep();
break;
case OnboardingStep.TASK_CREATION_GUIDE:
this.displayTaskCreationGuideStep();
break;
case OnboardingStep.COMPLETE:
this.displayCompleteStep();
break;
}
}
/**
* Display settings check step (new async approach)
*/
private displaySettingsCheckStep() {
// Header
this.onboardingHeaderEl.createEl("h1", { text: t("Task Genius Setup") });
// Content
const content = this.onboardingContentEl;
if (this.state.userHasChanges) {
// User has made changes - ask if they want onboarding
this.onboardingHeaderEl.createEl("p", {
text: t("We noticed you've already configured Task Genius"),
cls: "onboarding-subtitle",
});
const checkSection = content.createDiv("settings-check-section");
// Show detected changes
checkSection.createEl("h3", { text: t("Your current configuration includes:") });
const changesList = checkSection.createEl("ul", { cls: "changes-summary-list" });
this.state.changesSummary.forEach(change => {
const item = changesList.createEl("li");
const checkIcon = item.createSpan("change-check");
setIcon(checkIcon, "check");
item.createSpan("change-text").setText(change);
});
// Ask if they want onboarding
const questionSection = content.createDiv("onboarding-question");
questionSection.createEl("h3", { text: t("Would you like to run the setup wizard anyway?") });
const optionsContainer = questionSection.createDiv("question-options");
const yesButton = optionsContainer.createEl("button", {
text: t("Yes, show me the setup wizard"),
cls: "mod-cta question-button",
});
yesButton.addEventListener("click", () => {
this.state.currentStep = OnboardingStep.WELCOME;
this.displayCurrentStep();
});
const noButton = optionsContainer.createEl("button", {
text: t("No, I'm happy with my current setup"),
cls: "question-button",
});
noButton.addEventListener("click", () => this.handleSkip());
} else {
// User hasn't made changes - proceed with normal onboarding
this.state.currentStep = OnboardingStep.WELCOME;
this.displayCurrentStep();
}
}
/**
* Display welcome step
*/
private displayWelcomeStep() {
// Header
this.onboardingHeaderEl.createEl("h1", { text: t("Welcome to Task Genius") });
this.onboardingHeaderEl.createEl("p", {
text: t(
"Transform your task management with advanced progress tracking and workflow automation"
),
cls: "onboarding-subtitle",
});
// Content - reuse existing welcome step logic from modal
const content = this.onboardingContentEl;
const welcomeSection = content.createDiv("welcome-section");
// Plugin features overview
const featuresContainer = welcomeSection.createDiv("features-overview");
const features = [
{
icon: "bar-chart-3",
title: t("Progress Tracking"),
description: t(
"Visual progress bars and completion tracking for all your tasks"
),
},
{
icon: "building",
title: t("Project Management"),
description: t(
"Organize tasks by projects with advanced filtering and sorting"
),
},
{
icon: "zap",
title: t("Workflow Automation"),
description: t(
"Automate task status changes and improve your productivity"
),
},
{
icon: "calendar",
title: t("Multiple Views"),
description: t(
"Kanban boards, calendars, Gantt charts, and more visualization options"
),
},
];
features.forEach((feature) => {
const featureEl = featuresContainer.createDiv("feature-item");
const iconEl = featureEl.createDiv("feature-icon");
setIcon(iconEl, feature.icon);
const featureContent = featureEl.createDiv("feature-content");
featureContent.createEl("h3", { text: feature.title });
featureContent.createEl("p", { text: feature.description });
});
// Setup note
const setupNote = content.createDiv("setup-note");
setupNote.createEl("p", {
text: t(
"This quick setup will help you configure Task Genius based on your experience level and needs. You can always change these settings later."
),
cls: "setup-description",
});
}
/**
* Display user level selection step
*/
private displayUserLevelSelectStep() {
// Header
this.onboardingHeaderEl.createEl("h1", { text: t("Choose Your Usage Mode") });
this.onboardingHeaderEl.createEl("p", {
text: t(
"Select the configuration that best matches your task management experience"
),
cls: "onboarding-subtitle",
});
// Content
this.userLevelSelector.render(this.onboardingContentEl, (config) => {
this.state.selectedConfig = config;
this.updateButtonStates();
});
}
/**
* Display configuration preview step
*/
private displayConfigPreviewStep() {
if (!this.state.selectedConfig) {
this.state.currentStep = OnboardingStep.USER_LEVEL_SELECT;
this.displayCurrentStep();
return;
}
// Header
this.onboardingHeaderEl.createEl("h1", { text: t("Configuration Preview") });
this.onboardingHeaderEl.createEl("p", {
text: t(
"Review the settings that will be applied for your selected mode"
),
cls: "onboarding-subtitle",
});
// Content
this.configPreview.render(
this.onboardingContentEl,
this.state.selectedConfig
);
}
/**
* Display task creation guide step
*/
private displayTaskCreationGuideStep() {
// Header
this.onboardingHeaderEl.createEl("h1", { text: t("Create Your First Task") });
this.onboardingHeaderEl.createEl("p", {
text: t("Learn how to create and format tasks in Task Genius"),
cls: "onboarding-subtitle",
});
// Content
this.taskCreationGuide.render(this.onboardingContentEl);
}
/**
* Display completion step
*/
private displayCompleteStep() {
if (!this.state.selectedConfig) return;
// Header
this.onboardingHeaderEl.createEl("h1", { text: t("Setup Complete!") });
this.onboardingHeaderEl.createEl("p", {
text: t("Task Genius is now configured and ready to use"),
cls: "onboarding-subtitle",
});
// Content
this.onboardingComplete.render(
this.onboardingContentEl,
this.state.selectedConfig
);
}
/**
* Update button states based on current step
*/
private updateButtonStates() {
const step = this.state.currentStep;
// Skip button - show on settings check and welcome
this.skipButton.buttonEl.style.display =
step === OnboardingStep.SETTINGS_CHECK || step === OnboardingStep.WELCOME
? "inline-block" : "none";
// Back button - hide on first two steps
this.backButton.buttonEl.style.display =
step <= OnboardingStep.WELCOME ? "none" : "inline-block";
// Next button text and state
const isLastStep = step === OnboardingStep.COMPLETE;
const isSettingsCheck = step === OnboardingStep.SETTINGS_CHECK;
if (isSettingsCheck) {
this.nextButton.buttonEl.style.display = "none"; // Hide on settings check
} else {
this.nextButton.buttonEl.style.display = "inline-block";
this.nextButton.setButtonText(
isLastStep ? t("Start Using Task Genius") : t("Next")
);
}
// Enable/disable next based on selection
if (step === OnboardingStep.USER_LEVEL_SELECT) {
this.nextButton.setDisabled(!this.state.selectedConfig);
} else {
this.nextButton.setDisabled(this.state.isCompleting);
}
}
/**
* Handle skip onboarding
*/
private async handleSkip() {
await this.configManager.skipOnboarding();
this.onComplete();
this.close();
}
/**
* Handle back navigation
*/
private handleBack() {
if (this.state.currentStep > OnboardingStep.SETTINGS_CHECK) {
this.state.currentStep--;
// Skip task guide if it was skipped
if (
this.state.currentStep === OnboardingStep.TASK_CREATION_GUIDE &&
this.state.skipTaskGuide
) {
this.state.currentStep--;
}
this.displayCurrentStep();
}
}
/**
* Handle next navigation
*/
private async handleNext() {
const step = this.state.currentStep;
// Handle completion
if (step === OnboardingStep.COMPLETE) {
await this.completeOnboarding();
return;
}
// Validate current step
if (!this.validateCurrentStep()) {
return;
}
// Move to next step
this.state.currentStep++;
// Apply configuration when moving to preview
if (
this.state.currentStep === OnboardingStep.CONFIG_PREVIEW &&
this.state.selectedConfig
) {
try {
await this.configManager.applyConfiguration(
this.state.selectedConfig.mode
);
} catch (error) {
console.error("Failed to apply configuration:", error);
// Continue anyway, user can adjust in settings
}
}
// Skip task guide if requested
if (
this.state.currentStep === OnboardingStep.TASK_CREATION_GUIDE &&
this.state.skipTaskGuide
) {
this.state.currentStep++;
}
this.displayCurrentStep();
}
/**
* Validate current step before proceeding
*/
private validateCurrentStep(): boolean {
switch (this.state.currentStep) {
case OnboardingStep.USER_LEVEL_SELECT:
return !!this.state.selectedConfig;
default:
return true;
}
}
/**
* Complete onboarding process
*/
private async completeOnboarding() {
if (!this.state.selectedConfig || this.state.isCompleting) return;
this.state.isCompleting = true;
this.updateButtonStates();
try {
// Mark onboarding as completed
await this.configManager.completeOnboarding(
this.state.selectedConfig.mode
);
// Close view and trigger callback
this.onComplete();
this.close();
} catch (error) {
console.error("Failed to complete onboarding:", error);
this.state.isCompleting = false;
this.updateButtonStates();
}
}
/**
* Close the onboarding view
*/
private close() {
this.leaf.detach();
}
}