From 6a6416499aaf34d0ae6b2346d9455d2d5ecc6d30 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 31 Mar 2025 16:29:40 +0000 Subject: [PATCH] Updated UserProfile for career situation, expired token (no modal), and updated user-profile backend call to accmmodate updating profile. --- backend/server.js | 69 +++++++--- src/components/UserProfile.js | 236 +++++++++++++++++----------------- user_profile.db | Bin 57344 -> 57344 bytes 3 files changed, 170 insertions(+), 135 deletions(-) diff --git a/backend/server.js b/backend/server.js index cad17c6..6128d9b 100755 --- a/backend/server.js +++ b/backend/server.js @@ -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; diff --git a/src/components/UserProfile.js b/src/components/UserProfile.js index e8a650b..2e1e3cc 100644 --- a/src/components/UserProfile.js +++ b/src/components/UserProfile.js @@ -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 (

User Profile

- setFirstName(e.target.value)} - required - /> + setFirstName(e.target.value)} required />
- setLastName(e.target.value)} - required - /> + setLastName(e.target.value)} required />
- setEmail(e.target.value)} - required - /> + setEmail(e.target.value)} required />
- setZipCode(e.target.value)} - required - /> + setZipCode(e.target.value)} required />
- setSelectedState(e.target.value)} required> {states.map((s) => ( - + ))}
@@ -189,22 +180,29 @@ function UserProfile() { ) : areas.length > 0 && (
- setSelectedArea(e.target.value)} required> {areas.map((area, index) => ( - + ))}
)} + {isPremiumUser && ( +
+ + +
+ )} -
+ +
); } diff --git a/user_profile.db b/user_profile.db index 505218f0996d41564d9fa9b04d1d60dc7b36270f..ec9e0adc720511a2f0824477bfe38743e7c98339 100644 GIT binary patch delta 166 zcmZoTz}#?vd4jZHHUk3#9}x2ZF%uArPSi2x&t}joTExp5!obbc#K6?FF;9evt0|O` zU0hX_u_bqN9IF|lgpNXTVo_>pQG9V`Noit9W`3SRh-*ZM=H^?>Rg5eg3m6?YCoX%>o*f NnU@|=l)p&f006=rE0q8M delta 123 zcmZoTz}#?vd4jZH8Uq6Z9}x2ZF%uArOw=*vPh-$4TExq-fPtI&76WU@#yk