# All files / src/hooks useUsernameValidation.ts

0% Statements 0/43
0% Branches 0/37
0% Functions 0/6
0% Lines 0/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110

     import { useState, useEffect, useCallback } from 'react';
    import { useQuery } from '@tanstack/react-query';
    import { useTranslation } from 'react-i18next';
    import { apiService } from '@/services/api';
    import { useAuth } from '@/contexts/AuthContext';

    interface UsernameValidationResult {
      available: boolean;
      message: string;
    }

    interface UseUsernameValidationOptions {
      debounceMs?: number;
      skipIfEmpty?: boolean;
    }

    export const useUsernameValidation = (
      username: string,
      options: UseUsernameValidationOptions = {}
    ) => {
      const { t } = useTranslation();
      const { debounceMs = 500, skipIfEmpty = true } = options;
      const { user } = useAuth();
      const [debouncedUsername, setDebouncedUsername] = useState(username);

      // Debounce the username input
      useEffect(() => {
        if (skipIfEmpty && !username.trim()) {
          setDebouncedUsername('');
          return;
        }

        const timer = setTimeout(() => {
          setDebouncedUsername(username);
        }, debounceMs);

        return () => clearTimeout(timer);
      }, [username, debounceMs, skipIfEmpty]);

      // Query to check username availability
      const { data, isLoading, error } = useQuery({
        queryKey: ['username-check', debouncedUsername, user?.role],
        queryFn: async (): Promise<UsernameValidationResult> => {
          if (!debouncedUsername.trim() || debouncedUsername.length < 3) {
            return {
              available: false,
              message: debouncedUsername.length < 3 && debouncedUsername.length > 0
                ? t('reseller.endUsers.validation.usernameMin')
                : t('login.usernamePlaceholder')
            };
          }

          // Choose endpoint: admin/reseller when logged, public when anonymous
          let endpoint: string;
          if (user?.role === 'admin') {
            endpoint = `/api/admin/users/check-username/${encodeURIComponent(debouncedUsername)}`;
          } else if (user?.role === 'reseller') {
            endpoint = `/api/reseller/users/check-username/${encodeURIComponent(debouncedUsername)}`;
          } else {
            endpoint = `/api/redeem/check-username/${encodeURIComponent(debouncedUsername)}`;
          }

          const result = await apiService.get<UsernameValidationResult>(endpoint);
          if (!result.success) {
            throw new Error(result.error.details || t('common.serverError'));
          }
          return result.data;
        },
        enabled: !!debouncedUsername.trim() && debouncedUsername.length >= 3,
        staleTime: 30000, // Cache for 30 seconds
        retry: false, // Don't retry on validation errors
      });

      // Determine the validation state
      const getValidationState = useCallback(() => {
        if (!debouncedUsername.trim()) {
          return { isValid: null, color: 'gray', message: '' };
        }

        if (isLoading) {
          return { isValid: null, color: 'blue', message: t('common.loading') };
        }

        if (error) {
          return { isValid: false, color: 'red', message: t('common.serverError') };
        }

        if (data) {
          return {
            isValid: data.available,
            color: data.available ? 'green' : 'red',
            message: data.message
          };
        }

        return { isValid: null, color: 'gray', message: '' };
      }, [debouncedUsername, isLoading, error, data, t]);

      const validationState = getValidationState();

      return {
        isAvailable: data?.available ?? null,
        isChecking: isLoading,
        message: validationState.message,
        color: validationState.color,
        isValid: validationState.isValid,
        error: error?.message
      };
    };