Files
metabuilder/frontends/workflowui/docs/HOOKS_REFERENCE.md
2026-03-09 22:30:41 +00:00

15 KiB

Custom Hooks Reference Guide

Complete documentation for all 8 custom hooks created in the refactoring.


Table of Contents

  1. useAuthForm
  2. usePasswordValidation
  3. useLoginLogic
  4. useRegisterLogic
  5. useHeaderLogic
  6. useResponsiveSidebar
  7. useProjectSidebarLogic
  8. useDashboardLogic

useAuthForm

Location: src/hooks/useAuthForm.ts Lines: 55 Category: State Management Used By: LoginPage, RegisterPage

Purpose

Centralized authentication form state and error management. Synchronizes local form state with Redux auth state.

Return Type

interface UseAuthFormReturn {
  email: string;
  password: string;
  localError: string;
  isLoading: boolean;
  errorMessage: string | null;
  setEmail: (email: string) => void;
  setPassword: (password: string) => void;
  setLocalError: (error: string) => void;
  clearErrors: () => void;
}

Usage Example

const {
  email,
  password,
  localError,
  isLoading,
  errorMessage,
  setEmail,
  setPassword,
  clearErrors
} = useAuthForm();

return (
  <form onSubmit={(e) => {
    e.preventDefault();
    clearErrors();
    handleLogin({ email, password });
  }}>
    <input value={email} onChange={(e) => setEmail(e.target.value)} />
    <input value={password} onChange={(e) => setPassword(e.target.value)} />
    {(localError || errorMessage) && <div>{localError || errorMessage}</div>}
  </form>
);

Key Features

  • Syncs with Redux auth state (isLoading, errorMessage)
  • Local error management
  • Unified error clearing
  • Simple API for form field updates

usePasswordValidation

Location: src/hooks/usePasswordValidation.ts Lines: 52 Category: Business Logic Used By: RegisterPage

Purpose

Calculate password strength and provide validation feedback.

Return Type

interface UsePasswordValidationReturn {
  passwordStrength: number; // 0-4 scale
  validatePassword: (pwd: string) => PasswordValidationResult;
  handlePasswordChange: (value: string) => void;
}

interface PasswordValidationResult {
  score: number;     // 0-4
  message: string;   // "Weak", "Fair", "Good", "Strong"
}

Usage Example

const { passwordStrength, validatePassword, handlePasswordChange } = usePasswordValidation();

return (
  <>
    <input
      type="password"
      onChange={(e) => handlePasswordChange(e.target.value)}
    />
    <div className="strength-bar">
      <div style={{ width: `${(passwordStrength / 4) * 100}%` }} />
    </div>
    <span>{validatePassword(value).message}</span>
  </>
);

Validation Rules

Criterion Points
Length ≥ 8 characters 1
Contains lowercase letters 1
Contains uppercase letters 1
Contains numbers 1
Total 4

Strength Levels

Score Message
0 "Enter a password"
1 "Weak"
2 "Fair"
3 "Good"
4 "Strong"

useLoginLogic

Location: src/hooks/useLoginLogic.ts Lines: 68 Category: API Integration Used By: LoginPage

Purpose

Complete login business logic including validation, API calls, state management, and navigation.

Return Type

interface UseLoginLogicReturn {
  handleLogin: (data: LoginData) => Promise<void>;
}

interface LoginData {
  email: string;
  password: string;
}

Usage Example

const { handleLogin } = useLoginLogic();
const { email, password, clearErrors } = useAuthForm();

const onSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  clearErrors();
  try {
    await handleLogin({ email, password });
  } catch (error) {
    // Error already set in Redux state
  }
};

return <form onSubmit={onSubmit}>...</form>;

Validation Rules

  • Email must not be empty
  • Password must not be empty

What It Does

  1. Validates form data
  2. Sets loading state (Redux)
  3. Calls authService.login()
  4. Saves token and user to localStorage
  5. Updates Redux auth state
  6. Redirects to dashboard (/)
  7. Handles errors and dispatches to Redux

Error Handling

try {
  await handleLogin({ email, password });
} catch (error) {
  // Error message is already in Redux state
  // Error was also dispatched to Redux
  // Display with: errorMessage from useAuthForm
}

useRegisterLogic

Location: src/hooks/useRegisterLogic.ts Lines: 89 Category: API Integration Used By: RegisterPage

Purpose

Registration business logic with comprehensive validation and API integration.

Return Type

interface UseRegisterLogicReturn {
  handleRegister: (data: RegistrationData) => Promise<void>;
}

interface RegistrationData {
  name: string;
  email: string;
  password: string;
  confirmPassword: string;
}

Usage Example

const { handleRegister } = useRegisterLogic();
const { email, password, clearErrors } = useAuthForm();
const [name, setName] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');

const onSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  clearErrors();
  try {
    await handleRegister({
      name,
      email,
      password,
      confirmPassword
    });
  } catch (error) {
    // Error is in Redux state
  }
};

Validation Rules

Field Rules
Name Required, min 2 characters
Email Required, non-empty
Password Required, min 8 chars, uppercase, lowercase, numbers
Confirm Must match password

What It Does

  1. Validates all form fields
  2. Sets loading state (Redux)
  3. Calls authService.register()
  4. Saves token and user to localStorage
  5. Updates Redux auth state
  6. Redirects to dashboard (/)
  7. Handles errors and dispatches to Redux

useHeaderLogic

Location: src/hooks/useHeaderLogic.ts Lines: 48 Category: UI Logic Used By: MainLayout Header component

Purpose

Header component logic for user menu management and logout functionality.

Return Type

interface UseHeaderLogicReturn {
  user: any;                          // Current user object
  isAuthenticated: boolean;           // Auth status
  showUserMenu: boolean;              // Menu visibility
  setShowUserMenu: (show: boolean) => void;
  handleLogout: () => void;           // Execute logout
  toggleUserMenu: () => void;         // Toggle menu
}

Usage Example

const {
  user,
  isAuthenticated,
  showUserMenu,
  handleLogout,
  toggleUserMenu
} = useHeaderLogic();

return (
  <header>
    {isAuthenticated && user && (
      <div>
        <button onClick={toggleUserMenu}>
          {user.name}
        </button>
        {showUserMenu && (
          <menu>
            <span>{user.email}</span>
            <button onClick={handleLogout}>Logout</button>
          </menu>
        )}
      </div>
    )}
  </header>
);

What It Does

  • Manages user menu visibility state
  • Clears localStorage on logout
  • Dispatches logout action to Redux
  • Closes menu after logout
  • Redirects to login page

useResponsiveSidebar

Location: src/hooks/useResponsiveSidebar.ts Lines: 50 Category: Responsive Logic Used By: MainLayout

Purpose

Responsive sidebar behavior with mobile detection and auto-close functionality.

Parameters

useResponsiveSidebar(
  sidebarOpen: boolean,
  onSidebarChange: (open: boolean) => void
)

Return Type

interface UseResponsiveSidebarReturn {
  isMobile: boolean;                      // Is mobile screen
  isCollapsed: boolean;                   // Collapse state
  setIsCollapsed: (collapsed: boolean) => void;
  toggleCollapsed: () => void;
}

Usage Example

const { isMobile, isCollapsed, toggleCollapsed } = useResponsiveSidebar(
  sidebarOpen,
  setSidebar
);

return (
  <div>
    <header>
      <button onClick={toggleCollapsed}>
        {isCollapsed ? 'Expand' : 'Collapse'}
      </button>
    </header>
    {!isCollapsed && (
      <aside>{/* Sidebar content */}</aside>
    )}
  </div>
);

Mobile Breakpoint

  • Mobile: window.innerWidth < 768px
  • Desktop: window.innerWidth >= 768px

Features

  • Window resize listener with cleanup
  • Auto-closes sidebar on mobile
  • Collapse/expand toggle
  • Performance optimized with useCallback

useProjectSidebarLogic

Location: src/hooks/useProjectSidebarLogic.ts Lines: 91 Category: Data Management Used By: ProjectSidebar

Purpose

Project sidebar logic with filtering, form management, and optimized performance.

Parameters

useProjectSidebarLogic(projects: Project[])

Return Type

interface UseProjectSidebarLogicReturn {
  isCollapsed: boolean;
  showNewProjectForm: boolean;
  newProjectName: string;
  starredProjects: Project[];        // Memoized
  regularProjects: Project[];        // Memoized
  setIsCollapsed: (collapsed: boolean) => void;
  toggleCollapsed: () => void;
  setShowNewProjectForm: (show: boolean) => void;
  setNewProjectName: (name: string) => void;
  handleCreateProject: (e: React.FormEvent, onSuccess: () => void) => Promise<void>;
  handleProjectClick: (projectId: string, onSelect: (id: string) => void) => void;
  resetProjectForm: () => void;
}

Usage Example

const {
  starredProjects,
  regularProjects,
  showNewProjectForm,
  newProjectName,
  handleCreateProject,
  handleProjectClick,
  resetProjectForm
} = useProjectSidebarLogic(projects);

return (
  <aside>
    {starredProjects.map(p => (
      <div
        key={p.id}
        onClick={() => handleProjectClick(p.id, onSelectProject)}
      >
        {p.name}
      </div>
    ))}
    {showNewProjectForm && (
      <form onSubmit={(e) => handleCreateProject(e, () => Promise.resolve())}>
        <input value={newProjectName} onChange={...} />
        <button onClick={resetProjectForm}>Cancel</button>
      </form>
    )}
  </aside>
);

Performance Optimizations

  • Memoized filtering with useMemo
  • Callback optimization with useCallback
  • Prevents unnecessary re-renders

useDashboardLogic

Location: src/hooks/useDashboardLogic.ts Lines: 84 Category: Data Management Used By: Dashboard (app/page.tsx)

Purpose

Dashboard workspace management including creation, switching, and loading states.

Return Type

interface UseDashboardLogicReturn {
  isLoading: boolean;
  showCreateForm: boolean;
  newWorkspaceName: string;
  workspaces: any[];
  currentWorkspace: any;
  setShowCreateForm: (show: boolean) => void;
  setNewWorkspaceName: (name: string) => void;
  handleCreateWorkspace: (e: React.FormEvent) => Promise<void>;
  handleWorkspaceClick: (workspaceId: string) => void;
  resetWorkspaceForm: () => void;
}

Usage Example

const {
  isLoading,
  workspaces,
  showCreateForm,
  newWorkspaceName,
  handleCreateWorkspace,
  handleWorkspaceClick,
  resetWorkspaceForm,
  setShowCreateForm,
  setNewWorkspaceName
} = useDashboardLogic();

return (
  <div>
    {isLoading ? (
      <div>Loading...</div>
    ) : (
      <>
        {showCreateForm && (
          <form onSubmit={handleCreateWorkspace}>
            <input
              value={newWorkspaceName}
              onChange={(e) => setNewWorkspaceName(e.target.value)}
            />
            <button onClick={resetWorkspaceForm}>Cancel</button>
          </form>
        )}
        <div>
          {workspaces.map(w => (
            <div
              key={w.id}
              onClick={() => handleWorkspaceClick(w.id)}
            >
              {w.name}
            </div>
          ))}
        </div>
      </>
    )}
  </div>
);

What It Does

  1. Loads workspaces on mount (useEffect)
  2. Sets loading state during operations
  3. Creates new workspace with default color (#1976d2)
  4. Switches to new workspace
  5. Navigates to workspace page
  6. Handles form state and reset

Default Workspace Config

{
  name: newWorkspaceName,
  description: '',
  color: '#1976d2'
}

Best Practices

Composition Pattern

Combine multiple hooks in a component:

export default function RegisterPage() {
  const { email, password, ...authForm } = useAuthForm();
  const { passwordStrength, ...validation } = usePasswordValidation();
  const { handleRegister } = useRegisterLogic();
  // ... component logic
}

Error Handling

All hooks that make API calls handle errors:

try {
  await hookFunction();
} catch (error) {
  // Error is already in Redux state
  // Display with selector from errorMessage
}

Callback Dependencies

All callbacks are properly memoized:

const handleClick = useCallback(() => {
  // handler logic
}, [dependencies]); // Explicit dependencies

Form Reset Pattern

Standard form reset across all hooks:

const resetForm = useCallback(() => {
  setFormField('');
  setShowForm(false);
}, []);

Testing Examples

Testing usePasswordValidation

it('should validate strong password', () => {
  const validation = usePasswordValidation()
    .validatePassword('Abc123!@');
  expect(validation.score).toBe(4);
  expect(validation.message).toBe('Strong');
});

Testing useLoginLogic

it('should reject empty email', async () => {
  const { handleLogin } = useLoginLogic();
  await expect(
    handleLogin({ email: '', password: 'pass' })
  ).rejects.toThrow('Email is required');
});

Testing useProjectSidebarLogic

it('should filter starred projects', () => {
  const projects = [
    { id: 1, starred: true },
    { id: 2, starred: false }
  ];
  const { starredProjects } = useProjectSidebarLogic(projects);
  expect(starredProjects).toHaveLength(1);
});

Migration Guide

Before (Mixed Logic & UI)

export default function LoginPage() {
  const [email, setEmail] = useState('');
  const dispatch = useDispatch();
  
  const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault();
    dispatch(setLoading(true));
    try {
      const response = await authService.login(email, password);
      localStorage.setItem('auth_token', response.token);
      dispatch(setAuthenticated(response));
      router.push('/');
    } catch (error) {
      dispatch(setError(error.message));
    }
  };
  
  return <form onSubmit={handleLogin}>...</form>;
}

After (Separated Logic & UI)

export default function LoginPage() {
  const { email, setEmail, clearErrors } = useAuthForm();
  const { handleLogin } = useLoginLogic();
  
  const onSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    clearErrors();
    try {
      await handleLogin({ email, password });
    } catch {
      // Error in Redux state
    }
  };
  
  return <form onSubmit={onSubmit}>...</form>;
}

Summary

Hook LOC Category Components
useAuthForm 55 State Login, Register
usePasswordValidation 52 Logic Register
useLoginLogic 68 API Login
useRegisterLogic 89 API Register
useHeaderLogic 48 UI MainLayout
useResponsiveSidebar 50 Responsive MainLayout
useProjectSidebarLogic 91 Data ProjectSidebar
useDashboardLogic 84 Data Dashboard
Total 534

All hooks are production-ready and type-safe with full TypeScript support.