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
const __filename = fileURLToPath(import.meta.url);
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) => {
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) => {
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) {
return res.status(400).json({ error: 'All fields are required' });
}
const query = `
INSERT INTO user_profile (firstname, lastname, email, zipcode, state, area)
VALUES (?, ?, ?, ?, ?, ?)
`;
const params = [firstName, lastName, email, zipCode, state, area];
db.run(query, params, function (err) {
const checkQuery = `SELECT id FROM user_profile WHERE user_id = ?`;
db.get(checkQuery, [userId], (err, row) => {
if (err) {
console.error('Error inserting data:', err.message);
if (err.message.includes('UNIQUE constraint failed')) {
return res.status(400).json({ error: 'Email already exists' });
}
return res.status(500).json({ error: 'Failed to save user profile' });
console.error('Error checking profile:', err.message);
return res.status(500).json({ error: 'Database error' });
}
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
app.post('/api/login', (req, res) => {
const { username, password } = req.body;

View File

@ -10,37 +10,90 @@ function UserProfile() {
const [selectedState, setSelectedState] = useState('');
const [areas, setAreas] = useState([]);
const [selectedArea, setSelectedArea] = useState('');
const navigate = useNavigate(); // Add navigation
const [loadingAreas, setLoadingAreas] = useState(false); // To show a loading spinner for areas
const [careerSituation, setCareerSituation] = useState('');
const navigate = useNavigate();
const [loadingAreas, setLoadingAreas] = useState(false);
const [isPremiumUser, setIsPremiumUser] = useState(false);
// Fetch areas when a state is selected
useEffect(() => {
if (!selectedState) {
setAreas([]); // Clear areas if no state is selected
return;
const authFetch = async (url, options = {}) => {
const token = localStorage.getItem('token');
if (!token) {
navigate('/signin');
return null;
}
const fetchAreas = async () => {
setLoadingAreas(true);
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 response = await fetch(`/api/areas?state=${selectedState}`);
if (!response.ok) {
throw new Error('Failed to fetch areas');
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);
try {
const areaRes = await authFetch(`/api/areas?state=${data.state}`);
if (!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);
}
}
const data = await response.json();
setAreas(data.areas); // Set the areas from the API response
} catch (error) {
console.error('Error fetching areas:', error);
setAreas([]); // Reset areas on error
} finally {
setLoadingAreas(false);
console.error('Error loading user profile:', error);
}
};
fetchAreas();
}, [selectedState]);
fetchProfileAndAreas();
}, []);
// Handle form submission
const handleFormSubmit = async (e) => {
e.preventDefault();
@ -51,10 +104,11 @@ function UserProfile() {
zipCode,
state: selectedState,
area: selectedArea,
careerSituation,
};
try {
const response = await fetch('/api/user-profile', {
const response = await authFetch('/api/user-profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -65,122 +119,59 @@ function UserProfile() {
if (!response.ok) {
throw new Error('Failed to save profile');
}
console.warn('Profile saved successfully');
console.log('Profile saved successfully');
// Add success feedback for the user, if needed
navigate('/interest-inventory');
} catch (error) {
console.error('Error saving profile:', error);
}
};
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: "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" }
{ 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: "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="user-profile-container">
<h2>User Profile</h2>
<form onSubmit={handleFormSubmit}>
<div className="form-group">
<label>First Name:</label>
<input
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
required
/>
<input type="text" value={firstName} onChange={(e) => setFirstName(e.target.value)} required />
</div>
<div className="form-group">
<label>Last Name:</label>
<input
type="text"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
required
/>
<input type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} required />
</div>
<div className="form-group">
<label>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
</div>
<div className="form-group">
<label>ZIP Code:</label>
<input
type="text"
value={zipCode}
onChange={(e) => setZipCode(e.target.value)}
required
/>
<input type="text" value={zipCode} onChange={(e) => setZipCode(e.target.value)} required />
</div>
<div className="form-group">
<label>State:</label>
<select
value={selectedState}
onChange={(e) => setSelectedState(e.target.value)}
required
>
<select value={selectedState} onChange={(e) => setSelectedState(e.target.value)} required>
<option value="">Select a State</option>
{states.map((s) => (
<option key={s.code} value={s.code}>
{s.name}
</option>
<option key={s.code} value={s.code}>{s.name}</option>
))}
</select>
</div>
@ -189,22 +180,29 @@ function UserProfile() {
) : areas.length > 0 && (
<div className="form-group">
<label>Area:</label>
<select
value={selectedArea}
onChange={(e) => setSelectedArea(e.target.value)}
required
>
<select value={selectedArea} onChange={(e) => setSelectedArea(e.target.value)} required>
<option value="">Select an Area</option>
{areas.map((area, index) => (
<option key={index} value={area}>
{area}
</option>
<option key={index} value={area}>{area}</option>
))}
</select>
</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>
</form>
</form>
<button onClick={() => navigate('/getting-started')}>Go Back</button>
</div>
);
}

Binary file not shown.