首页布局优化前

This commit is contained in:
iven
2026-03-17 23:26:16 +08:00
parent 74dbf42644
commit e262200f1e
89 changed files with 2266 additions and 2120 deletions

View File

@@ -0,0 +1,128 @@
/**
* 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';
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) {
console.warn('[useOnboarding] 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);
console.log('[useOnboarding] 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);
console.log('[useOnboarding] 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) {
console.warn('[useOnboarding] 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;