← back to Dear Bubbe Nextjs
components/UserOnboarding.tsx
1186 lines
'use client'
import { useState } from 'react'
import { useSession } from 'next-auth/react'
interface OnboardingProps {
onComplete: (data: any) => void
}
export default function UserOnboarding({ onComplete }: OnboardingProps) {
const { data: session } = useSession()
const [step, setStep] = useState(1)
const [formData, setFormData] = useState({
preferredName: '',
gender: '',
relationshipStatus: '',
sameGenderPartner: false,
spouseName: '', // Added spouse name
birthYear: '',
religiousLevel: '',
hasKids: '',
numberOfKids: 0,
kidsInfo: [],
incomeLevel: '',
education: '',
school: '',
degree: '',
graduationYear: '',
jobTitle: '',
company: '',
industry: '',
yearsAtJob: '',
country: '',
state: '',
city: '',
// New Bubbe personality settings
bubbePersonality: 'rude', // 'rude' or 'sweet'
yiddishEnabled: true,
translationEnabled: true,
bubbeVoice: 'yU6e7uJLZCE8eA26VAqb' // Original voice as default
})
const currentYear = new Date().getFullYear()
const years = Array.from({ length: 100 }, (_, i) => currentYear - i - 13) // 13+ years old
const handleNext = () => {
if (step < 12) { // Increased for Bubbe settings step
setStep(step + 1)
} else {
submitProfile()
}
}
const handleBack = () => {
if (step > 1) setStep(step - 1)
}
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault()
handleNext()
}
}
const submitProfile = async () => {
try {
// Save Bubbe settings to localStorage
localStorage.setItem('bubbePersonality', formData.bubbePersonality)
localStorage.setItem('yiddishEnabled', formData.yiddishEnabled.toString())
localStorage.setItem('translationEnabled', formData.translationEnabled.toString())
localStorage.setItem('bubbeVoice', formData.bubbeVoice)
const response = await fetch('/api/user-profile', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...formData,
email: session?.user?.email,
name: session?.user?.name,
age: formData.birthYear ? currentYear - parseInt(formData.birthYear) : null
})
})
if (response.ok) {
// Pass formData with flag to trigger voice welcome
onComplete({ ...formData, triggerVoiceWelcome: true })
}
} catch (error) {
console.error('Failed to save profile:', error)
}
}
const renderStep = () => {
switch (step) {
case 1:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
Oy vey, {session?.user?.name}! Let Bubbe get to know you better!
</h2>
<p className="text-gray-600">What should I call you, bubbeleh?</p>
<input
type="text"
placeholder="Your preferred name"
value={formData.preferredName}
onChange={(e) => setFormData({ ...formData, preferredName: e.target.value })}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
onKeyPress={handleKeyPress}
autoFocus
/>
<p className="text-sm text-gray-500">
Leave blank to use "{session?.user?.name}"
</p>
</div>
)
case 2:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
So tell me, what are you?
</h2>
<p className="text-gray-600">Don't be shy, Bubbe needs to know!</p>
<div className="grid grid-cols-2 gap-3">
{['male', 'female', 'other', 'prefer_not_to_say'].map(option => (
<button
key={option}
onClick={() => setFormData({ ...formData, gender: option })}
className={`px-4 py-3 rounded-lg border-2 transition-all ${
formData.gender === option
? 'border-amber-500 bg-amber-50 text-amber-800'
: 'border-gray-300 hover:border-amber-300'
}`}
>
{option === 'male' ? '👨 Male' :
option === 'female' ? '👩 Female' :
option === 'other' ? '🌈 Other' :
'🤐 Rather not say'}
</button>
))}
</div>
</div>
)
case 3:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
Nu? What's your relationship status?
</h2>
<p className="text-gray-600">Don't lie to your Bubbe!</p>
<div className="space-y-2 max-h-[40vh] overflow-y-auto pr-2">
{[
{ value: 'single', label: '😢 Single (Oy, we need to fix this!)' },
{ value: 'dating', label: '😊 Dating someone' },
{ value: 'engaged', label: '💍 Engaged (Finally!)' },
{ value: 'married', label: '💑 Married (Good, good!)' },
{ value: 'divorced', label: '💔 Divorced' },
{ value: 'widowed', label: '🖤 Widowed' },
{ value: 'complicated', label: '🤷 It\'s complicated' }
].map(option => (
<button
key={option.value}
onClick={() => setFormData({ ...formData, relationshipStatus: option.value })}
className={`w-full px-4 py-3 rounded-lg border-2 transition-all text-left ${
formData.relationshipStatus === option.value
? 'border-amber-500 bg-amber-50 text-amber-800'
: 'border-gray-300 hover:border-amber-300'
}`}
>
{option.label}
</button>
))}
</div>
{(formData.relationshipStatus === 'married' || formData.relationshipStatus === 'dating' || formData.relationshipStatus === 'engaged') && (
<>
<div className="mt-4 p-3 bg-amber-50 rounded-lg">
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="checkbox"
checked={formData.sameGenderPartner}
onChange={(e) => setFormData({ ...formData, sameGenderPartner: e.target.checked })}
className="w-5 h-5 text-amber-600 rounded focus:ring-amber-500"
/>
<span className="text-gray-700">Same-gender partner? 🏳️🌈 (Love is love, bubbeleh!)</span>
</label>
</div>
<div className="mt-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
{formData.relationshipStatus === 'married' ? "Spouse's name" : "Partner's name"} (optional)
</label>
<input
type="text"
placeholder={formData.relationshipStatus === 'married' ? "What's your spouse's name?" : "What's their name?"}
value={formData.spouseName}
onChange={(e) => setFormData({ ...formData, spouseName: e.target.value })}
onKeyPress={handleKeyPress}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
<p className="text-sm text-gray-500 mt-1">So I can properly nag you about them!</p>
</div>
</>
)}
</div>
)
case 4:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
How old are you? (Don't make me guess!)
</h2>
<p className="text-gray-600">What year were you born?</p>
<select
value={formData.birthYear}
onChange={(e) => setFormData({ ...formData, birthYear: e.target.value })}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value="">Select year...</option>
{years.map(year => (
<option key={year} value={year}>{year}</option>
))}
</select>
{formData.birthYear && (
<p className="text-amber-600">
So you're {currentYear - parseInt(formData.birthYear)} years old!
{currentYear - parseInt(formData.birthYear) > 30 && formData.relationshipStatus === 'single' &&
' Oy vey, still single at this age?!'
}
</p>
)}
</div>
)
case 5:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
How religious are you?
</h2>
<p className="text-gray-600">Be honest with your Bubbe!</p>
<div className="space-y-2">
{[
{ value: 'very', label: '🕍 Very religious (Good, good!)' },
{ value: 'somewhat', label: '✡️ Somewhat religious' },
{ value: 'not_very', label: '🤏 Not very religious' },
{ value: 'secular', label: '🌍 Secular Jew' },
{ value: 'atheist', label: '😱 Not religious at all (Oy gevalt!)' }
].map(option => (
<button
key={option.value}
onClick={() => setFormData({ ...formData, religiousLevel: option.value })}
className={`w-full px-4 py-3 rounded-lg border-2 transition-all text-left ${
formData.religiousLevel === option.value
? 'border-amber-500 bg-amber-50 text-amber-800'
: 'border-gray-300 hover:border-amber-300'
}`}
>
{option.label}
</button>
))}
</div>
</div>
)
case 6:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
The important question: Do you have kids?
</h2>
<p className="text-gray-600">Where are my grandchildren?!</p>
<div className="grid grid-cols-2 gap-3">
<button
onClick={() => setFormData({ ...formData, hasKids: 'yes' })}
className={`px-4 py-3 rounded-lg border-2 transition-all ${
formData.hasKids === 'yes'
? 'border-amber-500 bg-amber-50 text-amber-800'
: 'border-gray-300 hover:border-amber-300'
}`}
>
👶 Yes!
</button>
<button
onClick={() => setFormData({ ...formData, hasKids: 'no' })}
className={`px-4 py-3 rounded-lg border-2 transition-all ${
formData.hasKids === 'no'
? 'border-amber-500 bg-amber-50 text-amber-800'
: 'border-gray-300 hover:border-amber-300'
}`}
>
😔 Not yet
</button>
</div>
{formData.hasKids === 'yes' && (
<div className="space-y-3 mt-4">
<label className="text-gray-700">How many?</label>
<input
type="number"
min="0"
max="20"
value={formData.numberOfKids}
onChange={(e) => setFormData({ ...formData, numberOfKids: parseInt(e.target.value) || 0 })}
onKeyPress={handleKeyPress}
className="w-full px-4 py-3 border border-amber-300 rounded-lg"
/>
<p className="text-sm text-gray-500">
(You can tell me their names later if you want)
</p>
</div>
)}
{formData.hasKids === 'no' && formData.relationshipStatus === 'married' && (
<p className="text-amber-600 font-bold">
Married and no kids?! What are you waiting for, the Messiah?!
</p>
)}
</div>
)
case 7:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
Nu, let's talk tachlis - what's your income?
</h2>
<p className="text-gray-600">Don't be shy, Bubbe won't judge... much.</p>
<select
value={formData.incomeLevel}
onChange={(e) => setFormData({ ...formData, incomeLevel: e.target.value })}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value="">Select Income Range (optional)</option>
<option value="under_30k">Under $30,000 (Oy vey, let me feed you!)</option>
<option value="30k_50k">$30,000 - $50,000 (Getting by)</option>
<option value="50k_75k">$50,000 - $75,000 (Respectable)</option>
<option value="75k_100k">$75,000 - $100,000 (Not bad!)</option>
<option value="100k_150k">$100,000 - $150,000 (Very nice!)</option>
<option value="150k_200k">$150,000 - $200,000 (My child the success!)</option>
<option value="200k_300k">$200,000 - $300,000 (Now we're talking!)</option>
<option value="300k_500k">$300,000 - $500,000 (Mazel tov!)</option>
<option value="over_500k">Over $500,000 (Finally, a doctor's salary!)</option>
<option value="prefer_not_say">Prefer not to say (Keeping secrets from Bubbe?)</option>
</select>
{formData.incomeLevel && formData.incomeLevel.startsWith('under') && (
<p className="text-amber-600">
Oy vey! You need a better job, bubbeleh!
</p>
)}
{formData.incomeLevel && formData.incomeLevel.includes('over_500k') && (
<p className="text-amber-600">
Finally! But money isn't everything - are you happy?
</p>
)}
</div>
)
case 8:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
Where did you go to school? (I need to brag!)
</h2>
<p className="text-gray-600">Tell Bubbe about your education</p>
<select
value={formData.school}
onChange={(e) => setFormData({ ...formData, school: e.target.value })}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value="">Select School/University (optional)</option>
<optgroup label="Ivy League (Oy, impressive!)">
<option value="Brown University">Brown University</option>
<option value="Columbia University">Columbia University</option>
<option value="Cornell University">Cornell University</option>
<option value="Dartmouth College">Dartmouth College</option>
<option value="Harvard University">Harvard University</option>
<option value="Princeton University">Princeton University</option>
<option value="University of Pennsylvania">University of Pennsylvania</option>
<option value="Yale University">Yale University</option>
</optgroup>
<optgroup label="Top Universities">
<option value="Boston College">Boston College</option>
<option value="Boston University">Boston University</option>
<option value="Brandeis University">Brandeis University</option>
<option value="California Institute of Technology">California Institute of Technology</option>
<option value="Carnegie Mellon University">Carnegie Mellon University</option>
<option value="Duke University">Duke University</option>
<option value="Emory University">Emory University</option>
<option value="Georgetown University">Georgetown University</option>
<option value="Johns Hopkins University">Johns Hopkins University</option>
<option value="Massachusetts Institute of Technology">Massachusetts Institute of Technology (MIT)</option>
<option value="New York University">New York University</option>
<option value="Northwestern University">Northwestern University</option>
<option value="Rice University">Rice University</option>
<option value="Stanford University">Stanford University</option>
<option value="Tufts University">Tufts University</option>
<option value="University of Chicago">University of Chicago</option>
<option value="University of Notre Dame">University of Notre Dame</option>
<option value="University of Southern California">University of Southern California</option>
<option value="Vanderbilt University">Vanderbilt University</option>
<option value="Washington University in St. Louis">Washington University in St. Louis</option>
</optgroup>
<optgroup label="Public Universities">
<option value="Arizona State University">Arizona State University</option>
<option value="Michigan State University">Michigan State University</option>
<option value="Ohio State University">Ohio State University</option>
<option value="Pennsylvania State University">Pennsylvania State University</option>
<option value="Purdue University">Purdue University</option>
<option value="Rutgers University">Rutgers University</option>
<option value="Texas A&M University">Texas A&M University</option>
<option value="University of Arizona">University of Arizona</option>
<option value="University of California, Berkeley">UC Berkeley</option>
<option value="University of California, Los Angeles">UCLA</option>
<option value="University of California, San Diego">UC San Diego</option>
<option value="University of Colorado Boulder">University of Colorado Boulder</option>
<option value="University of Connecticut">University of Connecticut</option>
<option value="University of Florida">University of Florida</option>
<option value="University of Georgia">University of Georgia</option>
<option value="University of Illinois">University of Illinois</option>
<option value="University of Iowa">University of Iowa</option>
<option value="University of Maryland">University of Maryland</option>
<option value="University of Massachusetts">University of Massachusetts</option>
<option value="University of Michigan">University of Michigan</option>
<option value="University of Minnesota">University of Minnesota</option>
<option value="University of North Carolina">University of North Carolina</option>
<option value="University of Texas at Austin">University of Texas at Austin</option>
<option value="University of Virginia">University of Virginia</option>
<option value="University of Washington">University of Washington</option>
<option value="University of Wisconsin">University of Wisconsin</option>
</optgroup>
<optgroup label="Jewish Colleges (Mazel tov!)">
<option value="Yeshiva University">Yeshiva University</option>
<option value="Touro College">Touro College</option>
<option value="Hebrew Union College">Hebrew Union College</option>
<option value="Jewish Theological Seminary">Jewish Theological Seminary</option>
</optgroup>
<optgroup label="Liberal Arts Colleges">
<option value="Amherst College">Amherst College</option>
<option value="Barnard College">Barnard College</option>
<option value="Bates College">Bates College</option>
<option value="Bowdoin College">Bowdoin College</option>
<option value="Bryn Mawr College">Bryn Mawr College</option>
<option value="Carleton College">Carleton College</option>
<option value="Colby College">Colby College</option>
<option value="Grinnell College">Grinnell College</option>
<option value="Hamilton College">Hamilton College</option>
<option value="Haverford College">Haverford College</option>
<option value="Middlebury College">Middlebury College</option>
<option value="Oberlin College">Oberlin College</option>
<option value="Pomona College">Pomona College</option>
<option value="Smith College">Smith College</option>
<option value="Swarthmore College">Swarthmore College</option>
<option value="Vassar College">Vassar College</option>
<option value="Wellesley College">Wellesley College</option>
<option value="Wesleyan University">Wesleyan University</option>
<option value="Williams College">Williams College</option>
</optgroup>
<optgroup label="International">
<option value="Cambridge University">Cambridge University</option>
<option value="Hebrew University of Jerusalem">Hebrew University of Jerusalem</option>
<option value="McGill University">McGill University</option>
<option value="Oxford University">Oxford University</option>
<option value="Tel Aviv University">Tel Aviv University</option>
<option value="University of Toronto">University of Toronto</option>
</optgroup>
<optgroup label="Other">
<option value="Community College">Community College</option>
<option value="Trade School">Trade School</option>
<option value="Online University">Online University</option>
<option value="School of Hard Knocks">School of Hard Knocks (Oy...)</option>
<option value="Other">Other/Not Listed</option>
</optgroup>
</select>
<select
value={formData.degree}
onChange={(e) => setFormData({ ...formData, degree: e.target.value })}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value="">Select Degree (optional)</option>
<optgroup label="Undergraduate">
<option value="AA">AA - Associate of Arts</option>
<option value="AS">AS - Associate of Science</option>
<option value="BA">BA - Bachelor of Arts</option>
<option value="BArch">BArch - Bachelor of Architecture</option>
<option value="BBA">BBA - Bachelor of Business Administration</option>
<option value="BEd">BEd - Bachelor of Education</option>
<option value="BEng">BEng - Bachelor of Engineering</option>
<option value="BFA">BFA - Bachelor of Fine Arts</option>
<option value="BS">BS - Bachelor of Science</option>
<option value="BSN">BSN - Bachelor of Science in Nursing</option>
<option value="BSW">BSW - Bachelor of Social Work</option>
</optgroup>
<optgroup label="Graduate (Better!)">
<option value="MA">MA - Master of Arts</option>
<option value="MAT">MAT - Master of Arts in Teaching</option>
<option value="MBA">MBA - Master of Business Administration</option>
<option value="MEd">MEd - Master of Education</option>
<option value="MEng">MEng - Master of Engineering</option>
<option value="MFA">MFA - Master of Fine Arts</option>
<option value="MPA">MPA - Master of Public Administration</option>
<option value="MPH">MPH - Master of Public Health</option>
<option value="MS">MS - Master of Science</option>
<option value="MSN">MSN - Master of Science in Nursing</option>
<option value="MSW">MSW - Master of Social Work</option>
</optgroup>
<optgroup label="Professional (Now we're talking!)">
<option value="DDS">DDS - Doctor of Dental Surgery</option>
<option value="JD">JD - Juris Doctor (Lawyer!)</option>
<option value="MD">MD - Doctor of Medicine (Finally!)</option>
<option value="PharmD">PharmD - Doctor of Pharmacy</option>
<option value="PsyD">PsyD - Doctor of Psychology</option>
<option value="DVM">DVM - Doctor of Veterinary Medicine</option>
</optgroup>
<optgroup label="Doctoral (My bubbeleh the doctor!)">
<option value="DBA">DBA - Doctor of Business Administration</option>
<option value="EdD">EdD - Doctor of Education</option>
<option value="PhD">PhD - Doctor of Philosophy</option>
<option value="ScD">ScD - Doctor of Science</option>
</optgroup>
<optgroup label="Other">
<option value="Certificate">Certificate Program</option>
<option value="Diploma">Diploma</option>
<option value="High School">High School Diploma (Oy...)</option>
<option value="GED">GED (At least you finished!)</option>
<option value="Some College">Some College (No degree? Really?)</option>
</optgroup>
</select>
<select
value={formData.graduationYear}
onChange={(e) => setFormData({ ...formData, graduationYear: e.target.value })}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value="">Graduation Year (optional)</option>
{Array.from({ length: 70 }, (_, i) => currentYear - i).map(year => (
<option key={year} value={year}>{year}</option>
))}
</select>
{formData.degree && !formData.degree.includes('MD') &&
!formData.degree.includes('JD') &&
!formData.degree.includes('PhD') && (
<p className="text-amber-600">
That's nice, but why not a doctor or lawyer?
</p>
)}
<p className="text-sm text-gray-500 italic">
All fields are optional, bubbeleh!
</p>
</div>
)
case 9:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
So what do you do for a living?
</h2>
<p className="text-gray-600">Your cousin has a REAL job, you know!</p>
<input
type="text"
placeholder="Job title (e.g., Software Engineer) (optional)"
value={formData.jobTitle}
onChange={(e) => setFormData({ ...formData, jobTitle: e.target.value })}
onKeyPress={handleKeyPress}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
<input
type="text"
placeholder="Company name (optional)"
value={formData.company}
onChange={(e) => setFormData({ ...formData, company: e.target.value })}
onKeyPress={handleKeyPress}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
<select
value={formData.industry}
onChange={(e) => setFormData({ ...formData, industry: e.target.value })}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value="">Select Industry (optional)</option>
<optgroup label="Professional Fields">
<option value="accounting">Accounting</option>
<option value="advertising">Advertising/Marketing</option>
<option value="aerospace">Aerospace</option>
<option value="agriculture">Agriculture</option>
<option value="architecture">Architecture</option>
<option value="automotive">Automotive</option>
<option value="consulting">Consulting</option>
<option value="education">Education</option>
<option value="engineering">Engineering</option>
<option value="entertainment">Entertainment/Media</option>
<option value="finance">Finance/Banking</option>
<option value="government">Government</option>
<option value="healthcare">Healthcare/Medical</option>
<option value="hospitality">Hospitality/Food Service</option>
<option value="insurance">Insurance</option>
<option value="legal">Legal</option>
<option value="manufacturing">Manufacturing</option>
<option value="nonprofit">Non-profit</option>
<option value="pharmaceutical">Pharmaceutical</option>
<option value="real_estate">Real Estate</option>
<option value="retail">Retail/Sales</option>
<option value="tech">Technology/Software</option>
<option value="telecommunications">Telecommunications</option>
<option value="transportation">Transportation/Logistics</option>
</optgroup>
<optgroup label="Employment Status">
<option value="retired">Retired (Finally relaxing!)</option>
<option value="self_employed">Self-Employed/Entrepreneur</option>
<option value="student">Still Studying</option>
<option value="unemployed">Between Jobs (Oy vey!)</option>
</optgroup>
<optgroup label="Other">
<option value="arts">Arts/Creative</option>
<option value="homemaker">Homemaker</option>
<option value="military">Military</option>
<option value="religious">Religious/Clergy</option>
<option value="other">Something Else</option>
</optgroup>
</select>
<input
type="text"
placeholder="Years at current job (optional)"
value={formData.yearsAtJob}
onChange={(e) => setFormData({ ...formData, yearsAtJob: e.target.value })}
onKeyPress={handleKeyPress}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
{formData.industry === 'unemployed' && (
<p className="text-amber-600">
Oy vey! Get a job already, you schmuck!
</p>
)}
{formData.yearsAtJob && parseInt(formData.yearsAtJob) < 1 && (
<p className="text-amber-600">
Can't even keep a job for a year? What's wrong with you?
</p>
)}
<p className="text-sm text-gray-500 italic">
All fields are optional, but Bubbe will judge you either way!
</p>
</div>
)
case 10:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
Where are you hiding from Bubbe?
</h2>
<p className="text-gray-600">So I can guilt you about never visiting!</p>
{/* Quick location options */}
<div className="space-y-3">
<button
type="button"
onClick={async () => {
// Try to get GPS location
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
async (position) => {
try {
const response = await fetch('/api/location', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'gps',
lat: position.coords.latitude,
lon: position.coords.longitude
})
})
if (response.ok) {
const data = await response.json()
if (data.success && data.location) {
setFormData({
...formData,
city: data.location.city || '',
state: data.location.state || '',
country: data.location.country || ''
})
}
}
} catch (error) {
console.error('Failed to get location:', error)
}
},
(error) => {
console.error('GPS error:', error)
alert('Oy vey! Your browser is being difficult. Type it in manually!')
}
)
} else {
alert('Your browser doesn\'t support GPS. Such old technology!')
}
}}
className="w-full px-4 py-3 bg-amber-500 text-white rounded-lg hover:bg-amber-600 transition-colors"
>
📍 Use My Current Location
</button>
<div className="relative">
<input
type="text"
placeholder="Or enter ZIP code (e.g., 90210)"
onKeyDown={async (e) => {
if (e.key === 'Enter' && e.currentTarget.value) {
try {
const response = await fetch('/api/location', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'zip',
zip: e.currentTarget.value,
countryCode: 'us'
})
})
if (response.ok) {
const data = await response.json()
if (data.success && data.location) {
setFormData({
...formData,
city: data.location.city || '',
state: data.location.state || '',
country: data.location.country || ''
})
} else {
alert('Oy! That ZIP code doesn\'t exist!')
}
}
} catch (error) {
console.error('ZIP lookup failed:', error)
}
}
}}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 text-xs text-gray-500">
Press Enter
</span>
</div>
<div className="text-center text-gray-500 text-sm">— OR —</div>
</div>
{/* Manual input fields */}
<div className="space-y-3">
<input
type="text"
placeholder="City (optional)"
value={formData.city}
onChange={(e) => setFormData({ ...formData, city: e.target.value })}
onKeyPress={handleKeyPress}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
<div className="grid grid-cols-2 gap-3">
<input
type="text"
placeholder="State/Province"
value={formData.state}
onChange={(e) => setFormData({ ...formData, state: e.target.value })}
onKeyPress={handleKeyPress}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
<input
type="text"
placeholder="Country"
value={formData.country}
onChange={(e) => setFormData({ ...formData, country: e.target.value })}
onKeyPress={handleKeyPress}
className="w-full px-4 py-3 border border-amber-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
/>
</div>
</div>
{/* Dynamic location-based Bubbe comments */}
{(() => {
const city = formData.city.toLowerCase()
const state = formData.state.toLowerCase()
// Check for specific cities/areas first
if (city.includes('new york') || city.includes('manhattan') || city.includes('brooklyn') || city.includes('queens') || city.includes('bronx') || city.includes('nyc')) {
return (
<p className="text-green-600 font-bold text-lg animate-pulse">
🎉 NEW YORK! Finally, someone with TASTE! The greatest city in the world! You're a real mensch! 🗽
</p>
)
}
if (city.includes('los angeles') || city.includes('beverly hills') || city.includes('hollywood')) {
return (
<p className="text-amber-600">
LA? All that sunshine and still no spouse? What's wrong with you?!
</p>
)
}
if (city.includes('miami') || city.includes('boca') || state.includes('florida')) {
return (
<p className="text-amber-600">
Florida?! That's where alte kakers go to play shuffleboard and kvetch about their grandchildren!
</p>
)
}
if (city.includes('chicago')) {
return (
<p className="text-amber-600">
Chicago? It's so cold there, no wonder you need a Bubbe to warm your heart!
</p>
)
}
if (city.includes('boston') || city.includes('cambridge')) {
return (
<p className="text-amber-600">
Boston? At least they have good schools there. Did you go to Harvard? No? Then why mention it?
</p>
)
}
if (city.includes('san francisco') || city.includes('silicon valley') || city.includes('palo alto')) {
return (
<p className="text-amber-600">
San Francisco? With those prices, you better be making tech money, bubbeleh!
</p>
)
}
if (city.includes('seattle')) {
return (
<p className="text-amber-600">
Seattle? It rains so much, you'll be crying anyway - might as well talk to Bubbe!
</p>
)
}
if (city.includes('austin')) {
return (
<p className="text-amber-600">
Austin? "Keep it weird?" You want weird? Talk to your cousin Morty!
</p>
)
}
if (city.includes('las vegas') || city.includes('vegas')) {
return (
<p className="text-amber-600">
Vegas?! What happens in Vegas... your mother finds out anyway!
</p>
)
}
if (city.includes('phoenix') || city.includes('scottsdale')) {
return (
<p className="text-amber-600">
Arizona? It's a dry heat, they say. So is my oven, but I don't live in it!
</p>
)
}
if (city.includes('atlanta')) {
return (
<p className="text-amber-600">
Atlanta? Nice Jewish community there. Have you been to the JCC? You should go!
</p>
)
}
if (city.includes('denver') || city.includes('boulder')) {
return (
<p className="text-amber-600">
Colorado? All that altitude making you meshuga? Come down to earth!
</p>
)
}
if (city.includes('dallas') || city.includes('houston')) {
return (
<p className="text-amber-600">
Texas? Everything's bigger there - except your dating pool apparently!
</p>
)
}
if (city.includes('philadelphia') || city.includes('philly')) {
return (
<p className="text-amber-600">
Philly? The birthplace of America... and still no birthplace for your grandchildren?
</p>
)
}
if (city.includes('washington') || city.includes('d.c.') || city.includes('dc')) {
return (
<p className="text-amber-600">
Washington? All those politicians and you can't find ONE to marry?
</p>
)
}
if (city.includes('nashville')) {
return (
<p className="text-amber-600">
Nashville? Country music? Oy vey, what would your ancestors think?
</p>
)
}
if (city.includes('portland')) {
return (
<p className="text-amber-600">
Portland? So hipster! Do they even have kosher food trucks there?
</p>
)
}
// International cities
if (city.includes('london')) {
return (
<p className="text-amber-600">
London? Fancy schmancy! Did you meet the Queen? Oh wait...
</p>
)
}
if (city.includes('paris')) {
return (
<p className="text-amber-600">
Paris? Ooh la la! So romantic... and you're still single?!
</p>
)
}
if (city.includes('tel aviv') || city.includes('jerusalem') || city.includes('israel')) {
return (
<p className="text-amber-600">
Israel! The homeland! Have you been to the Kotel? You should go!
</p>
)
}
if (city.includes('toronto') || city.includes('montreal')) {
return (
<p className="text-amber-600">
Canada? So polite! But can they make a good bagel? I think not!
</p>
)
}
// Generic response for unknown places
if (city.length > 2) {
return (
<p className="text-amber-600">
{formData.city}? Never heard of it. Why don't you move somewhere civilized like New York?
</p>
)
}
return null
})()}
<p className="text-sm text-gray-500 italic">
I'll use this to give you local weather and news, bubbeleh!
</p>
</div>
)
case 11:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
One more thing - How should Bubbe talk to you?
</h2>
<p className="text-gray-600">Choose your Bubbe experience!</p>
{/* Bubbe Personality */}
<div className="space-y-3">
<p className="font-semibold text-gray-700">Bubbe's Personality:</p>
<div className="space-y-2">
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="radio"
name="personality"
checked={formData.bubbePersonality === 'rude'}
onChange={() => setFormData({ ...formData, bubbePersonality: 'rude' })}
className="w-5 h-5 text-amber-600"
/>
<span className="text-gray-700">
<strong>Rude Bubbe</strong> - The classic sarcastic grandma who tells it like it is!
</span>
</label>
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="radio"
name="personality"
checked={formData.bubbePersonality === 'sweet'}
onChange={() => setFormData({ ...formData, bubbePersonality: 'sweet' })}
className="w-5 h-5 text-amber-600"
/>
<span className="text-gray-700">
<strong>Sweet Bubbe</strong> - A kinder, gentler Bubbe (but still honest!)
</span>
</label>
</div>
</div>
{/* Voice Selection */}
<div className="space-y-3">
<p className="font-semibold text-gray-700">Bubbe's Voice:</p>
<div className="space-y-2">
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="radio"
name="voice"
checked={formData.bubbeVoice === 'yU6e7uJLZCE8eA26VAqb'}
onChange={() => setFormData({ ...formData, bubbeVoice: 'yU6e7uJLZCE8eA26VAqb' })}
className="w-5 h-5 text-amber-600"
/>
<span className="text-gray-700">Original Bubbe Voice (Classic)</span>
</label>
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="radio"
name="voice"
checked={formData.bubbeVoice === 'j9Ic6rh0FeC7nBIqew0q'}
onChange={() => setFormData({ ...formData, bubbeVoice: 'j9Ic6rh0FeC7nBIqew0q' })}
className="w-5 h-5 text-amber-600"
/>
<span className="text-gray-700">Softer Bubbe Voice (Gentler)</span>
</label>
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="radio"
name="voice"
checked={formData.bubbeVoice === 'VQHhI9WIBD3z8Ho2ZeUo'}
onChange={() => setFormData({ ...formData, bubbeVoice: 'VQHhI9WIBD3z8Ho2ZeUo' })}
className="w-5 h-5 text-amber-600"
/>
<span className="text-gray-700">Warm Bubbe Voice (Cozy)</span>
</label>
</div>
</div>
{/* Language Settings */}
<div className="space-y-3">
<p className="font-semibold text-gray-700">Language Settings:</p>
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="checkbox"
checked={formData.yiddishEnabled}
onChange={(e) => setFormData({ ...formData, yiddishEnabled: e.target.checked })}
className="w-5 h-5 text-amber-600 rounded"
/>
<span className="text-gray-700">Use Yiddish expressions (oy vey, meshugana, etc.)</span>
</label>
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="checkbox"
checked={formData.translationEnabled}
onChange={(e) => setFormData({ ...formData, translationEnabled: e.target.checked })}
className="w-5 h-5 text-amber-600 rounded"
/>
<span className="text-gray-700">Show translations for Yiddish words</span>
</label>
</div>
</div>
)
case 12:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold text-gray-800">
Perfect! Now Bubbe knows everything!
</h2>
<p className="text-gray-600">Get ready for personalized guilt trips!</p>
<div className="bg-amber-50 rounded-lg p-4 space-y-2 text-sm">
<p><strong>Name:</strong> {formData.preferredName || session?.user?.name || 'Bubbeleh'}</p>
<p><strong>Status:</strong> {formData.relationshipStatus || 'Mysterious'}</p>
{formData.sameGenderPartner && <p>🏳️🌈 With same-gender partner</p>}
{formData.spouseName && <p><strong>Partner:</strong> {formData.spouseName}</p>}
<p><strong>Income:</strong> {formData.incomeLevel || 'Secret'}</p>
{formData.school && <p><strong>School:</strong> {formData.school}</p>}
{formData.degree && <p><strong>Degree:</strong> {formData.degree}</p>}
{formData.jobTitle && <p><strong>Job:</strong> {formData.jobTitle}</p>}
{formData.company && <p><strong>Company:</strong> {formData.company}</p>}
{formData.industry && <p><strong>Industry:</strong> {formData.industry}</p>}
{formData.city && <p><strong>Location:</strong> {formData.city}{formData.state ? `, ${formData.state}` : ''}{formData.country ? `, ${formData.country}` : ''}</p>}
<p><strong>Bubbe Mode:</strong> {formData.bubbePersonality === 'sweet' ? 'Sweet Bubbe' : 'Rude Bubbe'}</p>
<p><strong>Yiddish:</strong> {formData.yiddishEnabled ? 'Enabled' : 'Disabled'}</p>
</div>
<p className="text-center text-amber-600 font-bold">
Click finish to meet your new Bubbe!
</p>
</div>
)
default:
return null
}
}
return (
<div className="fixed inset-0 z-50 bg-gradient-to-br from-amber-50 to-orange-100 overflow-y-auto">
<div className="min-h-full flex items-center justify-center p-4 py-8">
<div className="bg-white rounded-2xl shadow-2xl max-w-md w-full p-8 my-8">
{/* Progress bar */}
<div className="mb-6">
<div className="flex justify-between items-center mb-2">
<span className="text-sm text-gray-500">Step {step} of 12</span>
<span className="text-sm text-amber-600">
{step === 1 ? 'Name' :
step === 2 ? 'Gender' :
step === 3 ? 'Relationship' :
step === 4 ? 'Age' :
step === 5 ? 'Religion' :
step === 6 ? 'Children' :
step === 7 ? 'Income' :
step === 8 ? 'Education' :
step === 9 ? 'Career' :
step === 10 ? 'Location' :
step === 11 ? 'Settings' :
'Summary'}
</span>
</div>
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-amber-400 to-amber-600 transition-all duration-300"
style={{ width: `${(step / 12) * 100}%` }}
/>
</div>
</div>
{/* Current step content */}
{renderStep()}
{/* Navigation buttons */}
<div className="flex justify-between mt-8 gap-3">
{step > 1 && (
<button
onClick={handleBack}
className="px-6 py-3 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"
>
Back
</button>
)}
<button
onClick={handleNext}
className={`flex-1 px-6 py-3 bg-amber-500 text-white rounded-lg hover:bg-amber-600 transition-colors font-medium ${
step === 1 && !formData.preferredName ? '' : ''
}`}
>
{step === 12 ? "Let's talk!" : 'Next'}
</button>
</div>
{/* Skip option */}
<button
onClick={() => onComplete({})}
className="w-full mt-3 text-gray-500 text-sm hover:text-gray-700"
>
Skip for now (but Bubbe will be disappointed!)
</button>
</div>
</div>
</div>
)
}