From 1dc0aa4ee82394a1c0a0e9ceeaf54f18fd7f526c Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Wed, 30 Jul 2025 00:02:22 +0200 Subject: [PATCH] Add interest to profile creation --- app/api/interests/route.ts | 45 ++++ app/api/user/update-profile/route.ts | 72 ++++-- app/complete-profile/page.tsx | 350 ++++++++++++++++++--------- prisma/seed.ts | 2 +- 4 files changed, 332 insertions(+), 137 deletions(-) create mode 100644 app/api/interests/route.ts diff --git a/app/api/interests/route.ts b/app/api/interests/route.ts new file mode 100644 index 00000000..a99a4d78 --- /dev/null +++ b/app/api/interests/route.ts @@ -0,0 +1,45 @@ +import { prisma } from "@/lib/server/prisma"; +import { NextResponse } from "next/server"; + +export async function GET() { + try { + // Get all interests from the database + const interests = await prisma.interest.findMany({ + select: { + id: true, + name: true, + }, + orderBy: { + name: 'asc' + } + }); + + const causeAreas = await prisma.causeArea.findMany({ + select: { + id: true, + name: true, + }, + orderBy: { + name: 'asc' + } + }); + + const connection = await prisma.connection.findMany({ + select: { + id: true, + name: true, + }, + orderBy: { + name: 'asc' + } + }); + + return NextResponse.json({ interests, causeAreas, connection }); + } catch (error) { + console.error('Error fetching interests:', error); + return NextResponse.json( + { error: "Failed to fetch interests" }, + { status: 500 } + ); + } +} diff --git a/app/api/user/update-profile/route.ts b/app/api/user/update-profile/route.ts index 34483e00..ff23320b 100644 --- a/app/api/user/update-profile/route.ts +++ b/app/api/user/update-profile/route.ts @@ -14,30 +14,64 @@ export async function POST(req: Request) { } const data = await req.json(); - console.log(`Req: ${data}`) + const { profile, image, interests = [] } = data; - // Update user with the new profile information - const updatedUser = await prisma.user.update({ - where: {email: session.user.email}, - data: { - ...(data.image && { image: data.image }), - profile: { - upsert: { - create: data.profile, - update: data.profile, + // Start a transaction to ensure data consistency + const result = await prisma.$transaction(async (prisma) => { + // First, update/create the profile + const updatedUser = await prisma.user.update({ + where: { email: session.user.email }, + data: { + ...(image && { image }), + profile: { + upsert: { + create: profile, + update: profile, + }, }, }, - }, - // , // Only update image if provided - // select: { - // id: true, - // email: true, - // name: true, - // image: true, - // }, + include: { + profile: true, + }, + }); + + // Process interests if any + if (interests.length > 0 && updatedUser.profile) { + // First, find or create all interests + const interestOperations = interests.map((interest: { id?: string; name: string }) => + prisma.interest.upsert({ + where: { id: interest.id || '' }, + update: { name: interest.name }, + create: { name: interest.name }, + }) + ); + + const createdInterests = await Promise.all(interestOperations); + + // Get the IDs of all created/updated interests + const interestIds = createdInterests.map(interest => interest.id); + + // First, remove all existing interests for this profile + await prisma.profileInterest.deleteMany({ + where: { profileId: updatedUser.profile.id }, + }); + + // Then, create new connections + if (interestIds.length > 0) { + await prisma.profileInterest.createMany({ + data: interestIds.map(interestId => ({ + profileId: updatedUser.profile!.id, + interestId, + })), + skipDuplicates: true, + }); + } + } + + return updatedUser; }); - return NextResponse.json(updatedUser); + return NextResponse.json(result); } catch (error) { console.error('Profile update error:', error); return NextResponse.json( diff --git a/app/complete-profile/page.tsx b/app/complete-profile/page.tsx index bfb2e021..75f60078 100644 --- a/app/complete-profile/page.tsx +++ b/app/complete-profile/page.tsx @@ -18,36 +18,52 @@ export default function CompleteProfile() { const [isSubmitting, setIsSubmitting] = useState(false); const [isUploading, setIsUploading] = useState(false); const [error, setError] = useState(''); + const [allInterests, setAllInterests] = useState<{id: string, name: string}[]>([]); + const [selectedInterests, setSelectedInterests] = useState>(new Set()); + const [newInterest, setNewInterest] = useState(''); + const [showDropdown, setShowDropdown] = useState(false); + const dropdownRef = useRef(null); const fileInputRef = useRef(null); const router = useRouter(); const {data: session, update} = useSession(); - // const [selected, setSelected] = useState(new Set(selectedInterests)); - // - // const toggleInterest = (interestId) => { - // setSelected((prev) => { - // const newSet = new Set(prev); - // newSet.has(interestId) ? newSet.delete(interestId) : newSet.add(interestId); - // return newSet; - // }); - // }; + // Load existing interests and set up click-outside handler + useEffect(() => { + async function fetchInterests() { + try { + const res = await fetch('/api/interests'); + if (res.ok) { + const data = await res.json(); + setAllInterests(data.interests || []); + } + } catch (error) { + console.error('Error loading interests:', error); + } + } + fetchInterests(); - // const handleInterestSubmit = (e) => { - // e.preventDefault(); - // onSave(Array.from(selected)); // send to API - // }; + // Close dropdown when clicking outside + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setShowDropdown(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); const handleImageUpload = async (e: ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; - // Validate file type if (!file.type.startsWith('image/')) { setError('Please upload an image file'); return; } - // Validate file size (5MB max) if (file.size > 5 * 1024 * 1024) { setError('Image size must be less than 5MB'); return; @@ -82,6 +98,51 @@ export default function CompleteProfile() { } }; + const toggleInterest = (interestId: string) => { + setSelectedInterests(prev => { + const newSet = new Set(prev); + if (newSet.has(interestId)) { + newSet.delete(interestId); + } else { + newSet.add(interestId); + } + return newSet; + }); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + addNewInterest(); + } else if (e.key === 'Escape') { + setShowDropdown(false); + } + }; + + const addNewInterest = (e?: React.FormEvent) => { + if (e) e.preventDefault(); + const interestToAdd = newInterest.trim(); + if (!interestToAdd) return; + + // Check if interest already exists (case-insensitive) + const existingInterest = allInterests.find( + i => i.name.toLowerCase() === interestToAdd.toLowerCase() + ); + + if (existingInterest) { + // Toggle selection if it exists + toggleInterest(existingInterest.id); + } else { + // Add new interest + const newInterestObj = { id: `new-${Date.now()}`, name: interestToAdd }; + setAllInterests(prev => [...prev, newInterestObj]); + setSelectedInterests(prev => new Set(prev).add(newInterestObj.id)); + } + + setNewInterest(''); + setShowDropdown(false); + }; + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -103,10 +164,14 @@ export default function CompleteProfile() { personalityType, conflictStyle, }, + interests: Array.from(selectedInterests).map(id => ({ + id: id.startsWith('new-') ? undefined : id, + name: allInterests.find(i => i.id === id)?.name || id.replace('new-', '') + })), ...(key && {image: key}), }); console.log(`Body: ${body}`) - // alert(body) + const response = await fetch('/api/user/update-profile', { method: 'POST', headers: { @@ -118,13 +183,10 @@ export default function CompleteProfile() { if (!response.ok) { const errorData = await response.json(); setError(errorData.error || 'Failed to update profile'); - return + return; } - // Update the session to reflect the changes await update(); - - // Redirect to the home page or dashboard router.push('/'); } catch (error) { console.error('Profile update error:', error); @@ -145,17 +207,6 @@ export default function CompleteProfile() { const personalityOptions = Object.values(PersonalityType); const conflictOptions = Object.values(ConflictStyle); - // const answers = [ - // { - // prompt: 'What is your favorite color?', - // answer: 'Blue', - // }, - // { - // prompt: 'What is your favorite animal?', - // answer: 'Dog', - // } - // ] - return (
@@ -224,8 +275,7 @@ export default function CompleteProfile() {
-
- +
-
+
-