feat: Implement username change functionality

This commit is contained in:
gpt-engineer-app[bot]
2025-09-28 17:53:37 +00:00
parent 3e1ea57c11
commit 6b329f6887
6 changed files with 282 additions and 22 deletions

17
src/hooks/useDebounce.ts Normal file
View File

@@ -0,0 +1,17 @@
import { useState, useEffect } from 'react';
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}

View File

@@ -0,0 +1,80 @@
import { useState, useEffect } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { usernameSchema } from '@/lib/validation';
import { useDebounce } from './useDebounce';
export type UsernameValidationState = {
isValid: boolean;
isAvailable: boolean | null;
isChecking: boolean;
error: string | null;
};
export function useUsernameValidation(username: string, currentUsername?: string) {
const [state, setState] = useState<UsernameValidationState>({
isValid: false,
isAvailable: null,
isChecking: false,
error: null,
});
const debouncedUsername = useDebounce(username, 500);
useEffect(() => {
if (!debouncedUsername || debouncedUsername === currentUsername) {
setState({
isValid: debouncedUsername === currentUsername,
isAvailable: null,
isChecking: false,
error: null,
});
return;
}
// Validate format first
const validation = usernameSchema.safeParse(debouncedUsername);
if (!validation.success) {
setState({
isValid: false,
isAvailable: null,
isChecking: false,
error: validation.error.issues[0].message,
});
return;
}
// Check availability
setState(prev => ({ ...prev, isChecking: true, error: null }));
checkUsernameAvailability(validation.data);
}, [debouncedUsername, currentUsername]);
const checkUsernameAvailability = async (normalizedUsername: string) => {
try {
const { data, error } = await supabase
.from('profiles')
.select('username')
.eq('username', normalizedUsername)
.maybeSingle();
if (error) throw error;
const isAvailable = !data;
setState({
isValid: isAvailable,
isAvailable,
isChecking: false,
error: isAvailable ? null : 'Username is already taken',
});
} catch (error) {
setState({
isValid: false,
isAvailable: null,
isChecking: false,
error: 'Error checking username availability',
});
}
};
return state;
}