将 desktop/src 中 23 处 console.log 替换为 createLogger() 结构化日志: - 生产构建自动静默 debug/info 级别 - 保留 console.error 用于关键错误可见性 - 新增 dompurify 依赖修复 XSS 防护引入缺失 涉及文件: App.tsx, offlineStore.ts, autonomy-manager.ts, gateway-auth.ts, llm-service.ts, request-helper.ts, security-index.ts, skill-discovery.ts, use-onboarding.ts 等 16 个文件
132 lines
3.6 KiB
TypeScript
132 lines
3.6 KiB
TypeScript
/**
|
|
* useOnboarding - Hook for detecting and managing first-time user onboarding
|
|
*
|
|
* Determines if user needs to go through the onboarding wizard.
|
|
* Stores completion status in localStorage.
|
|
*/
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { createLogger } from './logger';
|
|
|
|
const log = createLogger('useOnboarding');
|
|
|
|
const ONBOARDING_COMPLETED_KEY = 'zclaw-onboarding-completed';
|
|
const USER_PROFILE_KEY = 'zclaw-user-profile';
|
|
|
|
export interface UserProfile {
|
|
userName: string;
|
|
userRole?: string;
|
|
completedAt: string;
|
|
}
|
|
|
|
export interface OnboardingState {
|
|
isNeeded: boolean;
|
|
isLoading: boolean;
|
|
userProfile: UserProfile | null;
|
|
markCompleted: (profile: Omit<UserProfile, 'completedAt'>) => void;
|
|
resetOnboarding: () => void;
|
|
}
|
|
|
|
/**
|
|
* Hook to manage first-time user onboarding
|
|
*
|
|
* Usage:
|
|
* ```tsx
|
|
* const { isNeeded, isLoading, markCompleted } = useOnboarding();
|
|
*
|
|
* if (isNeeded) {
|
|
* return <OnboardingWizard onComplete={markCompleted} />;
|
|
* }
|
|
* ```
|
|
*/
|
|
export function useOnboarding(): OnboardingState {
|
|
const [isNeeded, setIsNeeded] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
|
|
|
|
// Check onboarding status on mount
|
|
useEffect(() => {
|
|
try {
|
|
const completed = localStorage.getItem(ONBOARDING_COMPLETED_KEY);
|
|
const profileStr = localStorage.getItem(USER_PROFILE_KEY);
|
|
|
|
if (completed === 'true' && profileStr) {
|
|
const profile = JSON.parse(profileStr) as UserProfile;
|
|
setUserProfile(profile);
|
|
setIsNeeded(false);
|
|
} else {
|
|
// No onboarding record - first time user
|
|
setIsNeeded(true);
|
|
}
|
|
} catch (err) {
|
|
log.warn('Failed to check onboarding status:', err);
|
|
setIsNeeded(true);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
// Mark onboarding as completed
|
|
const markCompleted = useCallback((profile: Omit<UserProfile, 'completedAt'>) => {
|
|
const fullProfile: UserProfile = {
|
|
...profile,
|
|
completedAt: new Date().toISOString(),
|
|
};
|
|
|
|
try {
|
|
localStorage.setItem(ONBOARDING_COMPLETED_KEY, 'true');
|
|
localStorage.setItem(USER_PROFILE_KEY, JSON.stringify(fullProfile));
|
|
setUserProfile(fullProfile);
|
|
setIsNeeded(false);
|
|
log.debug('Onboarding completed for user:', profile.userName);
|
|
} catch (err) {
|
|
console.error('[useOnboarding] Failed to save onboarding status:', err);
|
|
}
|
|
}, []);
|
|
|
|
// Reset onboarding (for testing or user request)
|
|
const resetOnboarding = useCallback(() => {
|
|
try {
|
|
localStorage.removeItem(ONBOARDING_COMPLETED_KEY);
|
|
localStorage.removeItem(USER_PROFILE_KEY);
|
|
setUserProfile(null);
|
|
setIsNeeded(true);
|
|
log.debug('Onboarding reset');
|
|
} catch (err) {
|
|
console.error('[useOnboarding] Failed to reset onboarding:', err);
|
|
}
|
|
}, []);
|
|
|
|
return {
|
|
isNeeded,
|
|
isLoading,
|
|
userProfile,
|
|
markCompleted,
|
|
resetOnboarding,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get stored user profile without hook (for use outside React components)
|
|
*/
|
|
export function getStoredUserProfile(): UserProfile | null {
|
|
try {
|
|
const profileStr = localStorage.getItem(USER_PROFILE_KEY);
|
|
if (profileStr) {
|
|
return JSON.parse(profileStr) as UserProfile;
|
|
}
|
|
} catch (err) {
|
|
log.warn('Failed to get user profile:', err);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Check if onboarding is completed (for use outside React components)
|
|
*/
|
|
export function isOnboardingCompleted(): boolean {
|
|
return localStorage.getItem(ONBOARDING_COMPLETED_KEY) === 'true';
|
|
}
|
|
|
|
export default useOnboarding;
|