Updated UserProfile for career situation, expired token (no modal), and updated user-profile backend call to accmmodate updating profile.

This commit is contained in:
Josh 2025-03-31 16:29:40 +00:00
parent f7f959c426
commit 6a6416499a
3 changed files with 170 additions and 135 deletions

View File

@ -12,7 +12,7 @@ import jwt from 'jsonwebtoken'; // For token-based authentication
// Derive __dirname for ES modules // Derive __dirname for ES modules
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
const dbPath = path.resolve('/home/jcoakley/aptiva-dev1-app/backend/user_profile.db'); const dbPath = path.resolve('/home/jcoakley/aptiva-dev1-app/user_profile.db');
const db = new sqlite3.Database(dbPath, (err) => { const db = new sqlite3.Database(dbPath, (err) => {
if (err) { if (err) {
@ -119,32 +119,69 @@ app.post('/api/register', async (req, res) => {
} }
}); });
// Route to save user profile data // Route to save or update user profile
app.post('/api/user-profile', (req, res) => { app.post('/api/user-profile', (req, res) => {
const { firstName, lastName, email, zipCode, state, area } = req.body; const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Authorization token is required' });
}
let userId;
try {
const decoded = jwt.verify(token, SECRET_KEY);
userId = decoded.userId;
} catch (error) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
const { firstName, lastName, email, zipCode, state, area, careerSituation } = req.body;
if (!firstName || !lastName || !email || !zipCode || !state || !area) { if (!firstName || !lastName || !email || !zipCode || !state || !area) {
return res.status(400).json({ error: 'All fields are required' }); return res.status(400).json({ error: 'All fields are required' });
} }
const query = ` const checkQuery = `SELECT id FROM user_profile WHERE user_id = ?`;
INSERT INTO user_profile (firstname, lastname, email, zipcode, state, area) db.get(checkQuery, [userId], (err, row) => {
VALUES (?, ?, ?, ?, ?, ?)
`;
const params = [firstName, lastName, email, zipCode, state, area];
db.run(query, params, function (err) {
if (err) { if (err) {
console.error('Error inserting data:', err.message); console.error('Error checking profile:', err.message);
if (err.message.includes('UNIQUE constraint failed')) { return res.status(500).json({ error: 'Database error' });
return res.status(400).json({ error: 'Email already exists' });
} }
return res.status(500).json({ error: 'Failed to save user profile' });
const params = [firstName, lastName, email, zipCode, state, area, careerSituation || null, userId];
if (row) {
// Profile exists → UPDATE
const updateQuery = `
UPDATE user_profile
SET firstname = ?, lastname = ?, email = ?, zipcode = ?, state = ?, area = ?, career_situation = ?
WHERE user_id = ?
`;
db.run(updateQuery, params, function (err) {
if (err) {
console.error('Error updating profile:', err.message);
return res.status(500).json({ error: 'Failed to update user profile' });
}
res.status(200).json({ message: 'User profile updated successfully' });
});
} else {
// Profile doesn't exist → INSERT
const insertQuery = `
INSERT INTO user_profile (firstname, lastname, email, zipcode, state, area, career_situation, user_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`;
db.run(insertQuery, params, function (err) {
if (err) {
console.error('Error inserting profile:', err.message);
return res.status(500).json({ error: 'Failed to create user profile' });
}
res.status(201).json({ message: 'User profile created successfully', id: this.lastID });
});
} }
res.status(201).json({ message: 'User profile saved successfully', id: this.lastID });
}); });
}); });
// Route for login // Route for login
app.post('/api/login', (req, res) => { app.post('/api/login', (req, res) => {
const { username, password } = req.body; const { username, password } = req.body;

View File

@ -10,37 +10,90 @@ function UserProfile() {
const [selectedState, setSelectedState] = useState(''); const [selectedState, setSelectedState] = useState('');
const [areas, setAreas] = useState([]); const [areas, setAreas] = useState([]);
const [selectedArea, setSelectedArea] = useState(''); const [selectedArea, setSelectedArea] = useState('');
const navigate = useNavigate(); // Add navigation const [careerSituation, setCareerSituation] = useState('');
const [loadingAreas, setLoadingAreas] = useState(false); // To show a loading spinner for areas const navigate = useNavigate();
const [loadingAreas, setLoadingAreas] = useState(false);
const [isPremiumUser, setIsPremiumUser] = useState(false);
// Fetch areas when a state is selected
useEffect(() => { const authFetch = async (url, options = {}) => {
if (!selectedState) { const token = localStorage.getItem('token');
setAreas([]); // Clear areas if no state is selected if (!token) {
return; navigate('/signin');
return null;
} }
const fetchAreas = async () => { 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;
};
// Single useEffect for both profile and areas
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.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); setLoadingAreas(true);
try { try {
const response = await fetch(`/api/areas?state=${selectedState}`); const areaRes = await authFetch(`/api/areas?state=${data.state}`);
if (!response.ok) { if (!areaRes.ok) throw new Error('Failed to fetch areas');
throw new Error('Failed to fetch areas'); const areaData = await areaRes.json();
} setAreas(areaData.areas);
const data = await response.json(); } catch (areaErr) {
setAreas(data.areas); // Set the areas from the API response console.error('Error fetching areas:', areaErr);
} catch (error) { setAreas([]);
console.error('Error fetching areas:', error);
setAreas([]); // Reset areas on error
} finally { } finally {
setLoadingAreas(false); setLoadingAreas(false);
} }
}
} catch (error) {
console.error('Error loading user profile:', error);
}
}; };
fetchAreas(); fetchProfileAndAreas();
}, [selectedState]); }, []);
// Handle form submission
const handleFormSubmit = async (e) => { const handleFormSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
@ -51,10 +104,11 @@ function UserProfile() {
zipCode, zipCode,
state: selectedState, state: selectedState,
area: selectedArea, area: selectedArea,
careerSituation,
}; };
try { try {
const response = await fetch('/api/user-profile', { const response = await authFetch('/api/user-profile', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -65,122 +119,59 @@ function UserProfile() {
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to save profile'); throw new Error('Failed to save profile');
} }
console.warn('Profile saved successfully');
console.log('Profile saved successfully'); console.log('Profile saved successfully');
// Add success feedback for the user, if needed
navigate('/interest-inventory');
} catch (error) { } catch (error) {
console.error('Error saving profile:', error); console.error('Error saving profile:', error);
} }
}; };
const states = [ const states = [
{ name: "Alabama", code: "AL" }, { name: "Alabama", code: "AL" }, { name: "Alaska", code: "AK" }, { name: "Arizona", code: "AZ" },
{ name: "Alaska", code: "AK" }, { name: "Arkansas", code: "AR" }, { name: "California", code: "CA" }, { name: "Colorado", code: "CO" },
{ name: "Arizona", code: "AZ" }, { name: "Connecticut", code: "CT" }, { name: "Delaware", code: "DE" }, { name: "Florida", code: "FL" },
{ name: "Arkansas", code: "AR" }, { name: "Georgia", code: "GA" }, { name: "Hawaii", code: "HI" }, { name: "Idaho", code: "ID" },
{ name: "California", code: "CA" }, { name: "Illinois", code: "IL" }, { name: "Indiana", code: "IN" }, { name: "Iowa", code: "IA" },
{ name: "Colorado", code: "CO" }, { name: "Kansas", code: "KS" }, { name: "Kentucky", code: "KY" }, { name: "Louisiana", code: "LA" },
{ name: "Connecticut", code: "CT" }, { name: "Maine", code: "ME" }, { name: "Maryland", code: "MD" }, { name: "Massachusetts", code: "MA" },
{ name: "Delaware", code: "DE" }, { name: "Michigan", code: "MI" }, { name: "Minnesota", code: "MN" }, { name: "Mississippi", code: "MS" },
{ name: "Florida", code: "FL" }, { name: "Missouri", code: "MO" }, { name: "Montana", code: "MT" }, { name: "Nebraska", code: "NE" },
{ name: "Georgia", code: "GA" }, { name: "Nevada", code: "NV" }, { name: "New Hampshire", code: "NH" }, { name: "New Jersey", code: "NJ" },
{ name: "Hawaii", code: "HI" }, { name: "New Mexico", code: "NM" }, { name: "New York", code: "NY" }, { name: "North Carolina", code: "NC" },
{ name: "Idaho", code: "ID" }, { name: "North Dakota", code: "ND" }, { name: "Ohio", code: "OH" }, { name: "Oklahoma", code: "OK" },
{ name: "Illinois", code: "IL" }, { name: "Oregon", code: "OR" }, { name: "Pennsylvania", code: "PA" }, { name: "Rhode Island", code: "RI" },
{ name: "Indiana", code: "IN" }, { name: "South Carolina", code: "SC" }, { name: "South Dakota", code: "SD" }, { name: "Tennessee", code: "TN" },
{ name: "Iowa", code: "IA" }, { name: "Texas", code: "TX" }, { name: "Utah", code: "UT" }, { name: "Vermont", code: "VT" },
{ name: "Kansas", code: "KS" }, { name: "Virginia", code: "VA" }, { name: "Washington", code: "WA" }, { name: "West Virginia", code: "WV" },
{ name: "Kentucky", code: "KY" }, { name: "Wisconsin", code: "WI" }, { name: "Wyoming", code: "WY" }
{ 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 ( return (
<div className="user-profile-container"> <div className="user-profile-container">
<h2>User Profile</h2> <h2>User Profile</h2>
<form onSubmit={handleFormSubmit}> <form onSubmit={handleFormSubmit}>
<div className="form-group"> <div className="form-group">
<label>First Name:</label> <label>First Name:</label>
<input <input type="text" value={firstName} onChange={(e) => setFirstName(e.target.value)} required />
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
required
/>
</div> </div>
<div className="form-group"> <div className="form-group">
<label>Last Name:</label> <label>Last Name:</label>
<input <input type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} required />
type="text"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
required
/>
</div> </div>
<div className="form-group"> <div className="form-group">
<label>Email:</label> <label>Email:</label>
<input <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div> </div>
<div className="form-group"> <div className="form-group">
<label>ZIP Code:</label> <label>ZIP Code:</label>
<input <input type="text" value={zipCode} onChange={(e) => setZipCode(e.target.value)} required />
type="text"
value={zipCode}
onChange={(e) => setZipCode(e.target.value)}
required
/>
</div> </div>
<div className="form-group"> <div className="form-group">
<label>State:</label> <label>State:</label>
<select <select value={selectedState} onChange={(e) => setSelectedState(e.target.value)} required>
value={selectedState}
onChange={(e) => setSelectedState(e.target.value)}
required
>
<option value="">Select a State</option> <option value="">Select a State</option>
{states.map((s) => ( {states.map((s) => (
<option key={s.code} value={s.code}> <option key={s.code} value={s.code}>{s.name}</option>
{s.name}
</option>
))} ))}
</select> </select>
</div> </div>
@ -189,22 +180,29 @@ function UserProfile() {
) : areas.length > 0 && ( ) : areas.length > 0 && (
<div className="form-group"> <div className="form-group">
<label>Area:</label> <label>Area:</label>
<select <select value={selectedArea} onChange={(e) => setSelectedArea(e.target.value)} required>
value={selectedArea}
onChange={(e) => setSelectedArea(e.target.value)}
required
>
<option value="">Select an Area</option> <option value="">Select an Area</option>
{areas.map((area, index) => ( {areas.map((area, index) => (
<option key={index} value={area}> <option key={index} value={area}>{area}</option>
{area}
</option>
))} ))}
</select> </select>
</div> </div>
)} )}
{isPremiumUser && (
<div className="form-group">
<label>What best describes your current career situation?</label>
<select value={careerSituation} onChange={(e) => setCareerSituation(e.target.value)}>
<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>
)}
<button type="submit">Save Profile</button> <button type="submit">Save Profile</button>
</form> </form>
<button onClick={() => navigate('/getting-started')}>Go Back</button>
</div> </div>
); );
} }

Binary file not shown.