@@ -47,6 +47,34 @@ const uploadProfileImage = async (
4747 }
4848} ;
4949
50+ const validateUsername = ( username : string ) : { valid : boolean ; error ?: string } => {
51+ // Must be 8-30 characters
52+ if ( username . length < 8 ) {
53+ return { valid : false , error : "Username must be at least 8 characters" } ;
54+ }
55+ if ( username . length > 30 ) {
56+ return { valid : false , error : "Username must be 30 characters or less" } ;
57+ }
58+
59+ // Only letters, numbers, periods, and underscores
60+ const validPattern = / ^ [ a - z A - Z 0 - 9 . _ ] + $ / ;
61+ if ( ! validPattern . test ( username ) ) {
62+ return { valid : false , error : "Username can only contain letters, numbers, periods, and underscores" } ;
63+ }
64+
65+ // Cannot start or end with a period
66+ if ( username . startsWith ( '.' ) || username . endsWith ( '.' ) ) {
67+ return { valid : false , error : "Username cannot start or end with a period" } ;
68+ }
69+
70+ // Cannot have consecutive periods
71+ if ( username . includes ( '..' ) ) {
72+ return { valid : false , error : "Username cannot have consecutive periods" } ;
73+ }
74+
75+ return { valid : true } ;
76+ } ;
77+
5078export const signupUser = async ( formData : FormData ) => {
5179 try {
5280 const supabase = await createClientServerComponent ( ) ;
@@ -56,6 +84,7 @@ export const signupUser = async (formData: FormData) => {
5684 const rawEmail = formData . get ( "email_address" ) as string ;
5785 const email_address = rawEmail . toLowerCase ( ) ;
5886 const password = formData . get ( "password" ) as string ;
87+ const username = formData . get ( "username" ) as string ;
5988 const tech_stacks = JSON . parse ( formData . get ( "tech_stacks" ) as string ) || [ ] ;
6089 const positions : { id : number ; name : string } [ ] =
6190 ( JSON . parse ( formData . get ( "positions" ) as string ) as {
@@ -85,6 +114,24 @@ export const signupUser = async (formData: FormData) => {
85114 return { success : false , error : "Email already exists" } ;
86115 }
87116
117+ if ( username ) {
118+ // Validate username format
119+ const usernameValidation = validateUsername ( username ) ;
120+ if ( ! usernameValidation . valid ) {
121+ return { success : false , error : usernameValidation . error } ;
122+ }
123+
124+ // Check if username is already taken (case-insensitive)
125+ const { data : existingUsername } = await supabase
126+ . from ( "codev" )
127+ . select ( "username" )
128+ . ilike ( "username" , username )
129+ . single ( ) ;
130+
131+ if ( existingUsername ) {
132+ return { success : false , error : "Username is already taken" } ;
133+ }
134+ }
88135 // Handle profile image upload if provided
89136 const image_url = profileImage && profileImage . size > 0
90137 ? await uploadProfileImage ( profileImage , "profileImage" , "codebility" )
@@ -178,6 +225,8 @@ export const signupUser = async (formData: FormData) => {
178225 first_name : firstName ,
179226 last_name : lastName ,
180227 email_address,
228+ username : username || null , // ADDED: Store username if provided, otherwise null
229+ username_updated_at : username ? new Date ( ) . toISOString ( ) : null , // ADDED: Track when username was set
181230 phone_number : formData . get ( "phone_number" ) as string ,
182231 address : null ,
183232 about : ( formData . get ( "about" ) as string ) || null ,
0 commit comments