From ff26769ce1f7df1edfba41db036ea97ce6a5de9e Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 4 Feb 2026 15:27:28 +0100 Subject: [PATCH] Add bio field and enhance smart assignment with bio matching - Add bio field to User model for judge/mentor profile descriptions - Add bio step to onboarding wizard (optional step with 500 char limit) - Enhance smart assignment to match judge bio against project description - Uses keyword extraction and Jaccard-like similarity scoring - Only applies if judge has a bio (no penalty for empty bio) - Max 15 points for bio match on top of existing scoring - Fix geographic distribution query to use round relation for programId - Update score breakdown: tags (40), bio (15), workload (25), country (15) Co-Authored-By: Claude Opus 4.5 --- prisma/schema.prisma | 3 +- src/app/(auth)/onboarding/page.tsx | 68 ++++++++++- src/server/routers/analytics.ts | 2 +- src/server/routers/user.ts | 3 + src/server/services/smart-assignment.ts | 154 +++++++++++++++++++++--- 5 files changed, 205 insertions(+), 25 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index edf8229..7a22fe8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -199,7 +199,8 @@ model User { country String? // User's home country (for mentor matching) metadataJson Json? @db.JsonB - // Profile image + // Profile + bio String? // User bio for matching with project descriptions profileImageKey String? // Storage key (e.g., "avatars/user123/1234567890.jpg") profileImageProvider String? // Storage provider used: 's3' or 'local' diff --git a/src/app/(auth)/onboarding/page.tsx b/src/app/(auth)/onboarding/page.tsx index d25ba25..4678a86 100644 --- a/src/app/(auth)/onboarding/page.tsx +++ b/src/app/(auth)/onboarding/page.tsx @@ -25,6 +25,7 @@ import { import { toast } from 'sonner' import { ExpertiseSelect } from '@/components/shared/expertise-select' import { AvatarUpload } from '@/components/shared/avatar-upload' +import { Textarea } from '@/components/ui/textarea' import { User, Phone, @@ -36,9 +37,10 @@ import { ArrowLeft, Camera, Globe, + FileText, } from 'lucide-react' -type Step = 'name' | 'photo' | 'country' | 'phone' | 'tags' | 'preferences' | 'complete' +type Step = 'name' | 'photo' | 'country' | 'bio' | 'phone' | 'tags' | 'preferences' | 'complete' export default function OnboardingPage() { const router = useRouter() @@ -48,6 +50,7 @@ export default function OnboardingPage() { // Form state const [name, setName] = useState('') const [country, setCountry] = useState('') + const [bio, setBio] = useState('') const [phoneNumber, setPhoneNumber] = useState('') const [expertiseTags, setExpertiseTags] = useState([]) const [lockedTags, setLockedTags] = useState([]) @@ -70,6 +73,10 @@ export default function OnboardingPage() { if (userData.country) { setCountry(userData.country) } + // Pre-fill bio if available + if (userData.bio) { + setBio(userData.bio) + } // Pre-fill phone if available if (userData.phoneNumber) { setPhoneNumber(userData.phoneNumber) @@ -96,10 +103,10 @@ export default function OnboardingPage() { // Dynamic steps based on WhatsApp availability const steps: Step[] = useMemo(() => { if (whatsappEnabled) { - return ['name', 'photo', 'country', 'phone', 'tags', 'preferences', 'complete'] + return ['name', 'photo', 'country', 'bio', 'phone', 'tags', 'preferences', 'complete'] } // Skip phone step if WhatsApp is disabled - return ['name', 'photo', 'country', 'tags', 'preferences', 'complete'] + return ['name', 'photo', 'country', 'bio', 'tags', 'preferences', 'complete'] }, [whatsappEnabled]) const currentIndex = steps.indexOf(step) @@ -128,6 +135,7 @@ export default function OnboardingPage() { await completeOnboarding.mutateAsync({ name, country: country || undefined, + bio: bio || undefined, phoneNumber: phoneNumber || undefined, expertiseTags, notificationPreference, @@ -302,7 +310,49 @@ export default function OnboardingPage() { )} - {/* Step 4: Phone (only if WhatsApp enabled) */} + {/* Step 4: Bio */} + {step === 'bio' && ( + <> + + + + About You + + + Tell us a bit about yourself and your expertise. This helps us match you with relevant projects. (Optional) + + + +
+ +