334 lines
11 KiB
JavaScript
334 lines
11 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
function UserProfile() {
|
|
const [firstName, setFirstName] = useState('');
|
|
const [lastName, setLastName] = useState('');
|
|
const [email, setEmail] = useState('');
|
|
const [zipCode, setZipCode] = useState('');
|
|
const [selectedState, setSelectedState] = useState('');
|
|
const [areas, setAreas] = useState([]);
|
|
const [selectedArea, setSelectedArea] = useState('');
|
|
const [careerSituation, setCareerSituation] = useState('');
|
|
const [loadingAreas, setLoadingAreas] = useState(false);
|
|
const [isPremiumUser, setIsPremiumUser] = useState(false);
|
|
|
|
const navigate = useNavigate();
|
|
|
|
const authFetch = async (url, options = {}) => {
|
|
const token = localStorage.getItem('token');
|
|
if (!token) {
|
|
navigate('/signin');
|
|
return null;
|
|
}
|
|
|
|
const res = await fetch(url, {
|
|
...options,
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
},
|
|
});
|
|
|
|
if ([401, 403].includes(res.status)) {
|
|
console.warn('Token invalid or expired. Redirecting to Sign In.');
|
|
navigate('/signin');
|
|
return null;
|
|
}
|
|
|
|
return res;
|
|
};
|
|
|
|
useEffect(() => {
|
|
const fetchProfileAndAreas = async () => {
|
|
try {
|
|
const token = localStorage.getItem('token');
|
|
if (!token) return;
|
|
|
|
const res = await authFetch('/api/user-profile', {
|
|
method: 'GET',
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
if (!res || !res.ok) return;
|
|
|
|
const data = await res.json();
|
|
|
|
setFirstName(data.firstname || '');
|
|
setLastName(data.lastname || '');
|
|
setEmail(data.email || '');
|
|
setZipCode(data.zipcode || '');
|
|
setSelectedState(data.state || '');
|
|
setSelectedArea(data.area || '');
|
|
setCareerSituation(data.career_situation || '');
|
|
|
|
if (data.is_premium === 1) {
|
|
setIsPremiumUser(true);
|
|
}
|
|
|
|
if (data.state) {
|
|
setLoadingAreas(true);
|
|
try {
|
|
const areaRes = await authFetch(`/api/areas?state=${data.state}`);
|
|
if (!areaRes || !areaRes.ok) {
|
|
throw new Error('Failed to fetch areas');
|
|
}
|
|
const areaData = await areaRes.json();
|
|
setAreas(areaData.areas);
|
|
} catch (areaErr) {
|
|
console.error('Error fetching areas:', areaErr);
|
|
setAreas([]);
|
|
} finally {
|
|
setLoadingAreas(false);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading user profile:', error);
|
|
}
|
|
};
|
|
|
|
fetchProfileAndAreas();
|
|
}, []); // only runs once
|
|
|
|
const handleFormSubmit = async (e) => {
|
|
e.preventDefault();
|
|
|
|
const profileData = {
|
|
firstName,
|
|
lastName,
|
|
email,
|
|
zipCode,
|
|
state: selectedState,
|
|
area: selectedArea,
|
|
careerSituation,
|
|
};
|
|
|
|
try {
|
|
const response = await authFetch('/api/user-profile', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(profileData),
|
|
});
|
|
|
|
if (!response || !response.ok) {
|
|
throw new Error('Failed to save profile');
|
|
}
|
|
console.log('Profile saved successfully');
|
|
} catch (error) {
|
|
console.error('Error saving profile:', error);
|
|
}
|
|
};
|
|
|
|
// FULL list of states, including all 50 states (+ DC if desired)
|
|
const states = [
|
|
{ name: 'Alabama', code: 'AL' },
|
|
{ name: 'Alaska', code: 'AK' },
|
|
{ name: 'Arizona', code: 'AZ' },
|
|
{ name: 'Arkansas', code: 'AR' },
|
|
{ name: 'California', code: 'CA' },
|
|
{ name: 'Colorado', code: 'CO' },
|
|
{ name: 'Connecticut', code: 'CT' },
|
|
{ name: 'Delaware', code: 'DE' },
|
|
{ name: 'District of Columbia', code: 'DC' },
|
|
{ name: 'Florida', code: 'FL' },
|
|
{ name: 'Georgia', code: 'GA' },
|
|
{ name: 'Hawaii', code: 'HI' },
|
|
{ name: 'Idaho', code: 'ID' },
|
|
{ name: 'Illinois', code: 'IL' },
|
|
{ name: 'Indiana', code: 'IN' },
|
|
{ name: 'Iowa', code: 'IA' },
|
|
{ name: 'Kansas', code: 'KS' },
|
|
{ name: 'Kentucky', code: 'KY' },
|
|
{ name: 'Louisiana', code: 'LA' },
|
|
{ name: 'Maine', code: 'ME' },
|
|
{ name: 'Maryland', code: 'MD' },
|
|
{ name: 'Massachusetts', code: 'MA' },
|
|
{ name: 'Michigan', code: 'MI' },
|
|
{ name: 'Minnesota', code: 'MN' },
|
|
{ name: 'Mississippi', code: 'MS' },
|
|
{ name: 'Missouri', code: 'MO' },
|
|
{ name: 'Montana', code: 'MT' },
|
|
{ name: 'Nebraska', code: 'NE' },
|
|
{ name: 'Nevada', code: 'NV' },
|
|
{ name: 'New Hampshire', code: 'NH' },
|
|
{ name: 'New Jersey', code: 'NJ' },
|
|
{ name: 'New Mexico', code: 'NM' },
|
|
{ name: 'New York', code: 'NY' },
|
|
{ name: 'North Carolina', code: 'NC' },
|
|
{ name: 'North Dakota', code: 'ND' },
|
|
{ name: 'Ohio', code: 'OH' },
|
|
{ name: 'Oklahoma', code: 'OK' },
|
|
{ name: 'Oregon', code: 'OR' },
|
|
{ name: 'Pennsylvania', code: 'PA' },
|
|
{ name: 'Rhode Island', code: 'RI' },
|
|
{ name: 'South Carolina', code: 'SC' },
|
|
{ name: 'South Dakota', code: 'SD' },
|
|
{ name: 'Tennessee', code: 'TN' },
|
|
{ name: 'Texas', code: 'TX' },
|
|
{ name: 'Utah', code: 'UT' },
|
|
{ name: 'Vermont', code: 'VT' },
|
|
{ name: 'Virginia', code: 'VA' },
|
|
{ name: 'Washington', code: 'WA' },
|
|
{ name: 'West Virginia', code: 'WV' },
|
|
{ name: 'Wisconsin', code: 'WI' },
|
|
{ name: 'Wyoming', code: 'WY' },
|
|
];
|
|
|
|
return (
|
|
<div className="flex min-h-screen items-center justify-center bg-gray-50 p-4">
|
|
<div className="w-full max-w-lg rounded-lg bg-white p-6 shadow-md">
|
|
<h2 className="mb-4 text-center text-2xl font-semibold">User Profile</h2>
|
|
|
|
<form onSubmit={handleFormSubmit} className="space-y-4">
|
|
{/* First Name */}
|
|
<div>
|
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
First Name:
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={firstName}
|
|
onChange={(e) => setFirstName(e.target.value)}
|
|
required
|
|
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
|
/>
|
|
</div>
|
|
|
|
{/* Last Name */}
|
|
<div>
|
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
Last Name:
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={lastName}
|
|
onChange={(e) => setLastName(e.target.value)}
|
|
required
|
|
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
|
/>
|
|
</div>
|
|
|
|
{/* Email */}
|
|
<div>
|
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
Email:
|
|
</label>
|
|
<input
|
|
type="email"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
required
|
|
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
|
/>
|
|
</div>
|
|
|
|
{/* ZIP Code */}
|
|
<div>
|
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
ZIP Code:
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={zipCode}
|
|
onChange={(e) => setZipCode(e.target.value)}
|
|
required
|
|
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
|
/>
|
|
</div>
|
|
|
|
{/* State Dropdown */}
|
|
<div>
|
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
State:
|
|
</label>
|
|
<select
|
|
value={selectedState}
|
|
onChange={(e) => setSelectedState(e.target.value)}
|
|
required
|
|
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
|
>
|
|
<option value="">Select a State</option>
|
|
{states.map((s) => (
|
|
<option key={s.code} value={s.code}>
|
|
{s.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Areas Dropdown */}
|
|
{loadingAreas ? (
|
|
<p className="text-sm text-gray-500">Loading areas...</p>
|
|
) : (
|
|
areas.length > 0 && (
|
|
<div>
|
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
Area:
|
|
</label>
|
|
<select
|
|
value={selectedArea}
|
|
onChange={(e) => setSelectedArea(e.target.value)}
|
|
required
|
|
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
|
>
|
|
<option value="">Select an Area</option>
|
|
{areas.map((area, index) => (
|
|
<option key={index} value={area}>
|
|
{area}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
)
|
|
)}
|
|
|
|
{/* Premium-Only Field */}
|
|
{isPremiumUser && (
|
|
<div>
|
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
What best describes your current career situation?
|
|
</label>
|
|
<select
|
|
value={careerSituation}
|
|
onChange={(e) => setCareerSituation(e.target.value)}
|
|
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
|
>
|
|
<option value="">Select One</option>
|
|
<option value="highSchool">High School Student</option>
|
|
<option value="collegeStudent">College Student</option>
|
|
<option value="transitioningPro">Transitioning Professional</option>
|
|
<option value="advancingPro">Advancing Professional</option>
|
|
</select>
|
|
</div>
|
|
)}
|
|
|
|
{/* Form Buttons */}
|
|
<div className="mt-6 flex items-center justify-end space-x-3">
|
|
<button
|
|
type="submit"
|
|
className="rounded bg-blue-600 px-5 py-2 text-white transition-colors hover:bg-green-700"
|
|
>
|
|
Save Profile
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => navigate('/getting-started')}
|
|
className="rounded bg-gray-300 px-5 py-2 text-gray-700 hover:bg-gray-400"
|
|
>
|
|
Go Back
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default UserProfile;
|