From 1d585c2041b7b80fa308ed65f5d15ec15c28de13 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 12 May 2025 13:01:39 +0000 Subject: [PATCH] Fixed Upgrade button and Chatbot intro --- backend/server.js | 45 +++-- backend/server3.js | 32 ++++ src/App.js | 140 +++++----------- src/components/Chatbot.js | 2 +- src/components/Paywall.js | 45 +++-- .../PremiumOnboarding/CollegeOnboarding.js | 13 +- src/components/SignUp.js | 156 ++++++++++++------ user_profile.db | Bin 110592 -> 110592 bytes 8 files changed, 243 insertions(+), 190 deletions(-) diff --git a/backend/server.js b/backend/server.js index e579444..35c0643 100755 --- a/backend/server.js +++ b/backend/server.js @@ -73,21 +73,32 @@ app.use((req, res, next) => { // Route for user registration app.post('/api/register', async (req, res) => { - const { userId, username, password } = req.body; + const { + userId, + username, + password, + firstname, + lastname, + email, + zipcode, + state, + area + } = req.body; - if (!userId || !username || !password) { + // Validate all required fields + if (!userId || !username || !password || !firstname || !lastname || !email || !zipcode || !state || !area) { return res.status(400).json({ error: 'All fields are required' }); } try { - const hashedPassword = await bcrypt.hash(password, 10); // Hash the password - - // Step 1: Insert into user_auth + const hashedPassword = await bcrypt.hash(password, 10); + + // Insert into user_auth const authQuery = ` - INSERT INTO user_auth (username, hashed_password) - VALUES (?, ?) + INSERT INTO user_auth (user_id, username, hashed_password) + VALUES (?, ?, ?) `; - db.run(authQuery, [username, hashedPassword], function (err) { + db.run(authQuery, [userId, username, hashedPassword], function (err) { if (err) { console.error('Error inserting into user_auth:', err.message); if (err.message.includes('UNIQUE constraint failed')) { @@ -95,22 +106,19 @@ app.post('/api/register', async (req, res) => { } return res.status(500).json({ error: 'Failed to register user' }); } - - const user_id = this.lastID; // Retrieve the auto-generated id from user_auth - - // Step 2: Insert into user_profile + + // Insert into user_profile with actual provided values const profileQuery = ` - INSERT INTO user_profile (id, user_id, firstname, lastname, email, zipcode, state, area) - VALUES (?, ?, NULL, NULL, NULL, NULL, NULL, NULL) + INSERT INTO user_profile (user_id, firstname, lastname, email, zipcode, state, area) + VALUES (?, ?, ?, ?, ?, ?, ?) `; - db.run(profileQuery, [user_id, user_id], (err) => { + db.run(profileQuery, [userId, firstname, lastname, email, zipcode, state, area], (err) => { if (err) { console.error('Error inserting into user_profile:', err.message); return res.status(500).json({ error: 'Failed to create user profile' }); } - - // Return success response after both inserts - res.status(201).json({ message: 'User registered successfully', user_id }); + + res.status(201).json({ message: 'User registered successfully', userId }); }); }); } catch (error) { @@ -119,6 +127,7 @@ app.post('/api/register', async (req, res) => { } }); + // Route to save or update user profile app.post('/api/user-profile', (req, res) => { const token = req.headers.authorization?.split(' ')[1]; diff --git a/backend/server3.js b/backend/server3.js index b664559..2c3dc49 100644 --- a/backend/server3.js +++ b/backend/server3.js @@ -66,6 +66,38 @@ const authenticatePremiumUser = (req, res, next) => { } }; +/* ------------------------------------------------------------------ +PREMIUM UPGRADE ENDPOINT +------------------------------------------------------------------ */ +app.post('/api/activate-premium', (req, res) => { + 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 query = ` + UPDATE user_profile + SET is_premium = 1, is_pro_premium = 1 + WHERE user_id = ? + `; + db.run(query, [userId], (err) => { + if (err) { + console.error('Error updating premium status:', err.message); + return res.status(500).json({ error: 'Failed to activate premium' }); + } + res.status(200).json({ message: 'Premium activated successfully' }); + }); +}); + + /* ------------------------------------------------------------------ CAREER PROFILE ENDPOINTS ------------------------------------------------------------------ */ diff --git a/src/App.js b/src/App.js index 93907df..2d8f92d 100644 --- a/src/App.js +++ b/src/App.js @@ -7,6 +7,7 @@ import { useLocation, Link, } from 'react-router-dom'; +import { Button } from './components/ui/button.js'; // Import all components import PremiumRoute from './components/PremiumRoute.js'; @@ -103,168 +104,114 @@ function App() {

AptivaAI - Career Guidance Platform (beta)

- - {/* Navigation Menu */} + {isAuthenticated && ( )} - - {/* "Upgrade to Premium" button if not premium/pro and on a free path */} - {showPremiumCTA && isAuthenticated && !canAccessPremium && ( + + {/* Grouped Logout and Upgrade buttons */} + {isAuthenticated && ( +
+ + {showPremiumCTA && !canAccessPremium && ( + + + )} +
)} - + {/* Main Content */}
{/* Default to /signin */} } /> - + {/* Public routes */} } /> - + {/* Paywall (public) */} } /> - + {/* Authenticated routes */} {isAuthenticated && ( <> @@ -284,7 +231,7 @@ function App() { } /> } /> } /> - + {/* Premium-only routes */} } /> - - {/* 4) The new Resume Optimizer route */} + + {/* Resume Optimizer route */} )} - + {/* 404 / Fallback */} } />
- + {/* Session Handler */} ); + } export default App; diff --git a/src/components/Chatbot.js b/src/components/Chatbot.js index a2cca1e..d050e65 100644 --- a/src/components/Chatbot.js +++ b/src/components/Chatbot.js @@ -7,7 +7,7 @@ const Chatbot = ({ context }) => { { role: "assistant", content: - "Hi! I’m here to help you with career suggestions, ROI analysis, and any questions you have about your career. How can I assist you today?", + "Hi! I’m here to help you with suggestions, analyzing career options, and any questions you have about your career. How can I assist you today?", }, ]); const [input, setInput] = useState(""); diff --git a/src/components/Paywall.js b/src/components/Paywall.js index 05bf806..8d8a0cc 100644 --- a/src/components/Paywall.js +++ b/src/components/Paywall.js @@ -1,19 +1,38 @@ import React from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; +import { Button } from './ui/button.js'; const Paywall = () => { const navigate = useNavigate(); const { state } = useLocation(); - // Extract the selectedCareer from location state const { selectedCareer } = state || {}; - const handleSubscribe = () => { - // Once the user subscribes, navigate to MilestoneTracker - navigate('/PremiumOnboarding', { - state: { - selectedCareer, - }, - }); + const handleSubscribe = async () => { + const token = localStorage.getItem('token'); + if (!token) { + navigate('/signin'); + return; + } + + try { + const response = await fetch('/api/activate-premium', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + } + }); + + if (response.ok) { + navigate('/PremiumOnboarding', { state: { selectedCareer } }); + } else if (response.status === 401) { + navigate('/GettingStarted', { state: { selectedCareer } }); + } else { + console.error('Failed to activate premium:', await response.text()); + } + } catch (err) { + console.error('Error activating premium:', err); + } }; return ( @@ -25,8 +44,14 @@ const Paywall = () => {
  • ✅ Detailed College Guidance & Analysis
  • - - + + + ); }; diff --git a/src/components/PremiumOnboarding/CollegeOnboarding.js b/src/components/PremiumOnboarding/CollegeOnboarding.js index 9575068..51c6602 100644 --- a/src/components/PremiumOnboarding/CollegeOnboarding.js +++ b/src/components/PremiumOnboarding/CollegeOnboarding.js @@ -434,7 +434,7 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId })
    - + -
    - - -
    diff --git a/src/components/SignUp.js b/src/components/SignUp.js index 10a3666..a630183 100644 --- a/src/components/SignUp.js +++ b/src/components/SignUp.js @@ -1,18 +1,82 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import { Button } from './ui/button.js'; function SignUp() { const navigate = useNavigate(); + const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); + const [firstname, setFirstname] = useState(''); + const [lastname, setLastname] = useState(''); + const [email, setEmail] = useState(''); + const [zipcode, setZipcode] = useState(''); + const [state, setState] = useState(''); + const [area, setArea] = useState(''); + const [areas, setAreas] = useState([]); const [error, setError] = useState(''); - const [success, setSuccess] = useState(false); - const handleSignUp = async (event) => { - event.preventDefault(); + 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' }, + ]; - if (!username || !password) { - setError('Please enter a username and password'); + useEffect(() => { + const fetchAreas = async () => { + if (!state) { + setAreas([]); + return; + } + try { + const res = await fetch(`/api/areas?state=${state}`); + const data = await res.json(); + setAreas(data.areas || []); + } catch (err) { + console.error('Error fetching areas:', err); + setAreas([]); + } + }; + + fetchAreas(); + }, [state]); + + const handleSignUp = async (e) => { + e.preventDefault(); + setError(''); + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/; + const zipRegex = /^\d{5}$/; + const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/; + + if (!username || !password || !firstname || !lastname || !email || !zipcode || !state || !area) { + setError('All fields are required.'); + return; + } + if (!emailRegex.test(email)) { + setError('Enter a valid email address.'); + return; + } + if (!zipRegex.test(zipcode)) { + setError('ZIP code must be exactly 5 digits.'); + return; + } + if (!passwordRegex.test(password)) { + setError('Password must include at least 8 characters, one uppercase, one lowercase, one number, and one special character.'); return; } @@ -21,71 +85,57 @@ function SignUp() { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - userId: Math.floor(Math.random() * 10000000), // Temporary ID logic - username, - password, + userId: Math.floor(Math.random() * 1000000000), + username, password, firstname, lastname, email, zipcode, state, area, }), }); - if (response.ok) { - setSuccess(true); - console.log('User registered successfully'); - // Redirect to GettingStarted after successful sign-up - navigate('/getting-started'); - } else { - const data = await response.json(); - setError(data.error || 'Failed to register user'); + const data = await response.json(); + + if (!response.ok) { + setError(data.error || 'Registration failed. Please try again.'); + return; } + + navigate('/getting-started'); } catch (err) { - console.error('Error during registration:', err.message); - setError('An error occurred while registering. Please try again.'); + console.error(err); + setError('An unexpected error occurred. Please try again later.'); } }; return ( -
    -
    -

    Sign Up

    +
    +
    +

    Sign Up

    {error && ( -

    +

    {error} -

    +
    )} - {/* - Success is briefly shown, but you navigate away immediately - after a successful response. You may keep or remove this. - */} - {success && ( -

    - Registration successful! -

    - )} +
    + setUsername(e.target.value)} /> + setPassword(e.target.value)} /> + setFirstname(e.target.value)} /> + setLastname(e.target.value)} /> + setEmail(e.target.value)} /> + setZipcode(e.target.value)} /> - - setUsername(e.target.value)} - className="w-full rounded border border-gray-300 p-2 focus:border-blue-500 focus:outline-none" - /> + - setPassword(e.target.value)} - className="w-full rounded border border-gray-300 p-2 focus:border-blue-500 focus:outline-none" - /> + - +
    diff --git a/user_profile.db b/user_profile.db index a694451c0d7e3f6138e5ae6313a5441f144b7971..093ed49620580be31cd18a72ac8e10104ea51510 100644 GIT binary patch delta 4104 zcmb_fU2GKB6`onI*RyNyUOO&0)KJD01Z_O)d++>j1;sejDirJji>V2A=I4%gVRy&f zT|+HZ+6)hQ;sN3ZKlF!Gt<)w>6S*issVY?pPe@T8qN?Jh3XN2$R7(5MR%srp_S{{A zv2kFNcC^~rz4y+&-}%lt-#PQb-NXxb6ITbtOV_rKiI-NM{|QRPGOr3~Eb}VLtwi6? zG@`AYeZrPOGSD{>mxQQOk_72USSyT$&V>?NJYVqO2POyS%1*JA^}@1>38v$tqxF(g zsXNL^r{XWlC+Dlh%3Mu35!NoTQfXKoA5GrV4rm&ogs4<+{-8Slr|Y^#bQpuXM)y&j z&r+(v4JI^|QdpwuDv5GppRjG43dmx~p`{}`_cYo*dsm@2S2*bv!mtDI&8sqMssWZt0&pfj{(~fZjv*(Cat) z@Q=~b%E-{b)(#>*%2emly^$TM_XYG<^genQ{rX1V1?TwOqCY-S{mz98-|+p(W3|AmhNDJy{?I9MM*G?~D^qiG-!_)soS2Ye z=tBX0ggyjRe?-qDI|jt&zHzBzAjyA`G&(Lwso74HPya2Q>$%eXyUZ)y{i)fNXgt-3 zh}55tpB>x}K=<>n<`o3KVcXbOm}**z=3}BbrtK>RF$@#frNr^M1z)cOVYTeki(y4R z#k@i#EQNE6@?pcSs$r?b{0doz+|21Tr{Sz>&<)73k;6vTHm&I!S4!Qn zMw1gtauhfrbefRqNKSx7BZFX2|C mAk~n3iVYmh^bhyTn#HOOfbfX&ODX-q*(Gj zr^@81uzCSJGS))+fYpuAm*y)yhasT>24Dv7SXo`g8!?ldYGzHFOy?*N+?9{%(LNDC z>Vrc=YMC$h*?R#3jw{x1E#kM}3f_)O{oRI!QI+y9m{1gqLYw3?y zqTT66G$h6HqtW1qnB3J)B<3p+K-F5Yz9@%*JOzm=kF&5kS9Ew>LRiX&{ROX;S|H6l zrvg^1L8dt+`B`?6mBRTlcykyun|Z}XQ1s-J3vM&-Myt%JWe@RfN?~Cl#=`RaLY-AdJ|TEV{c^Fw^K#AN zmU?g-A2lVu2&Rl3T|+kY_!xT(vL=1N>3X`GPJmK)Bh&a-Z~V4^ZbOxL4K2NOz2DSc z+LaMjRR?aW+KN+6Aw3Q(COrr4evh1MbI>*# z4}=+9V5_C@NVSE3|9QRPn3u;*hAt;MwsS6yZlpqNQein>;2I870$q0$&2T-%;Z36M zdrUVOH3;U}(vI+aW>IdMdV1B=_(L|{nta%x)z)B>q4dW>JZgyCPUTqBPDqB1m?VnQ za_XG;z0YqFTEg9_y}c=E`PMmcIMY&f-WLGLGt1(vxMlgN8S(Hy^xfD)yAs${O7dNM zXT)E033pGYq^(<*Z_kKdZZx-OwouXDPW}w+G{}RmSy|v2ehIv~1cniq3@bL)R7G<= zsyG2Q6bd;>EI)wPE~mYUUJfhuLTRzt2QHh_B0BIeH(a`Mcw|44V3$i`!EWUCMU z;*(!LI&ZR2ye>bLK;2M#dCi5&%j>VU61|&Q{@{nA(r8EfnuW3*Z*=mCx~XPtkc>EA U$TLd*w^3J0 zB+jwrbH2a#`QGPwpU-Ev2F`8`yfB6qE*_jm3meb;3}+C2M8l8Z?_zJGRmAz$92(L_ zN5z4mCv=0fpO`qbE-9zt*wc^I)>azdT3KCy~CIYeK^?Twj&uF;Eg``b5W#xq+Q{t$2B_wa8whZe5j_FG5H z!&+8POc-m8sJ`s4HJ=?si9@LScDi1xtgJ@KoTh0AU)AtcSn^l6ef9Cj2N1rg;hUR7 zXa9jG(uo5`Gio$hF8@m{nZ1y}$8-P8dD#oupX^=8{B)yrA+z2(oEk3a?%1q$XlQm8 z7K|?$Da&#hBPz54BFnr&e5N8ItWZ$qM8dE5#^mwnjQLa;RsH%(&6G1-%*6XWz3Nx1 zQMtEuNw_${xF>9nv!asDVzt&>j;i$JOuq$DN{b#p3q#7T+^c(vOT4 z3T$L{Hr>0l&ie#SS;pji?YYxy&8TiZ^?ailJ=ZYjs{SfGHs;sr^{CqHV_*!P+&Bzl zZNECc&e#d2JSO63wk`Y72zO$zN_s*TsUsI#59<(qeypJF3{P!aiLzTJVF0U2C2it_ zWlAE&6_<*Zt+Lty?s(KZQ>%Z|oU4b=t~8^t*%-PLACFOu04nNuOnTHUDq+W{HZQF1 z)$ivqS}Hs#iqheWj4c93KQHO3o0qm19Np;&~O1?#252_i|^dW z8?C?QGR2f$f?i}yc4=Bq-EP=T;s#*u_j~hqGkgyWJ$0|)?hamR0F8Wk2B2}rcFV%@ ziABRQkxt+fhXxkmfufa)O#E{)hitkJUZ2kn!Q z^CRauA3fI3b}xU@^`QLwx^_&EnY4ksSAU#b7*66P4PU`a_P(n`Qozy%51KoS}F7IA!65J!q23@sUjTn@woy98{0{olVZ)7qe>;gk8tu4PU1 zqx?hLB`PYutT5sV2377mk_0}th;Io$kdzAL(DuuJqV43n4-B`yif~DvAh`k>(@L|m zMydVhH^=w49zv}z#66>95gOc||5Ven zA8FZ-avx`JX3oU7@yp)!BDY&zf!fjKyOH<1m*bXt?4WXyg282NHEhL197!SGYa1uCF5ouGGIAvNPfmN|A zn+Yx$H70K%D{(ZF&Rh!)gp|^sasJ0Z=_(rjwjSkq}xro*R|i>18FED zE-mVSWc><}TqqJmvO++wuIoc-R|K3+yGMHBf8`WlS)j-}kj72tQYdC0Ol8~vf$4ez z*pJCib%vJE;RELqclPU`4%Epnp&xdPWi*^kT?b3uI{DyWCSC6QehE$I!7;iArjqB7 zez5cD5?XBEU)|ez`xN?Ge>hH;@7vdzTt*L1akGCofaxYa0|ufYBL?$gH(qHwUUvc5 zP*3X=IcNL3V_n5SX;Rs1ic2zv#DOh4uEr#eH2in`4t@bg_)$EK^Bb*y|D~6A|1%7jN&N`Sz1x9)EvlhQ=CF>hXkeOGz%*s=j*YYPmzNM(-%XLl=gN1Y xU1&T$QJ&n5#FDYahO*+Cf|o&k;Jr{bJ|#h@Lc;x!h17CHIa1eNxrCk^{11)$h|K^1