diff --git a/backend/server.js b/backend/server.js
index 82273d2..66fd620 100755
--- a/backend/server.js
+++ b/backend/server.js
@@ -219,10 +219,20 @@ app.post('/api/signin', async (req, res) => {
return res.status(400).json({ error: 'Both username and password are required' });
}
- const query = `SELECT user_auth.user_id, user_auth.hashed_password, user_profile.zipcode
- FROM user_auth
- LEFT JOIN user_profile ON user_auth.user_id = user_profile.user_id
- WHERE user_auth.username = ?`;
+ // JOIN user_profile to fetch is_premium, email, or whatever columns you need
+ const query = `
+ SELECT
+ user_auth.user_id,
+ user_auth.hashed_password,
+ user_profile.zipcode,
+ user_profile.is_premium,
+ user_profile.email,
+ user_profile.firstname,
+ user_profile.lastname
+ FROM user_auth
+ LEFT JOIN user_profile ON user_auth.user_id = user_profile.user_id
+ WHERE user_auth.username = ?
+ `;
db.get(query, [username], async (err, row) => {
if (err) {
@@ -230,33 +240,43 @@ app.post('/api/signin', async (req, res) => {
return res.status(500).json({ error: 'Failed to query user authentication data' });
}
- console.log('Row data:', row); // Log the result of the query
-
+ // If no matching username
if (!row) {
return res.status(401).json({ error: 'Invalid username or password' });
}
- // Verify password
+ // Verify the password using bcrypt
const isMatch = await bcrypt.compare(password, row.hashed_password);
if (!isMatch) {
return res.status(401).json({ error: 'Invalid username or password' });
}
- // Ensure that you're using the correct user_id
- const { user_id, zipcode } = row;
+ // user_id from the row
+ const { user_id } = row;
- console.log('UserID:', user_id);
- console.log('ZIP Code:', zipcode); // Log the ZIP code to ensure it's correct
-
- // Send correct token with user_id
+ // Generate JWT
const token = jwt.sign({ userId: user_id }, SECRET_KEY, { expiresIn: '2h' });
- // You can optionally return the ZIP code or any other data as part of the response
- res.status(200).json({ message: 'Login successful', token, userId: user_id, zipcode });
+ // Return user object including is_premium and other columns
+ // The front end can store this in state (e.g. setUser).
+ res.status(200).json({
+ message: 'Login successful',
+ token,
+ userId: user_id,
+ user: {
+ user_id,
+ firstname: row.firstname,
+ lastname: row.lastname,
+ email: row.email,
+ zipcode: row.zipcode,
+ is_premium: row.is_premium,
+ }
+ });
});
});
+
// Route to fetch user profile
app.get('/api/user-profile', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
diff --git a/src/App.js b/src/App.js
index 124febe..cee6881 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { Routes, Route, Navigate, useNavigate, useLocation } from 'react-router-dom';
+import PremiumRoute from './components/PremiumRoute.js';
import SessionExpiredHandler from './components/SessionExpiredHandler.js';
import GettingStarted from './components/GettingStarted.js';
import SignIn from './components/SignIn.js';
@@ -8,8 +9,8 @@ import InterestInventory from './components/InterestInventory.js';
import Dashboard from './components/Dashboard.js';
import UserProfile from './components/UserProfile.js';
import FinancialProfileForm from './components/FinancialProfileForm.js';
-import MilestoneTracker from "./components/MilestoneTracker.js";
-import Paywall from "./components/Paywall.js";
+import MilestoneTracker from './components/MilestoneTracker.js';
+import Paywall from './components/Paywall.js';
import OnboardingContainer from './components/PremiumOnboarding/OnboardingContainer.js';
import MultiScenarioView from './components/MultiScenarioView.js';
@@ -17,18 +18,22 @@ function App() {
const navigate = useNavigate();
const location = useLocation();
+ // Track whether user is authenticated
const [isAuthenticated, setIsAuthenticated] = useState(() => {
return !!localStorage.getItem('token');
});
- // Hide the Upgrade CTA on these paths:
+ // Track the user object, including is_premium
+ const [user, setUser] = useState(null);
+
+ // We hide the "Upgrade to Premium" CTA if we're already on certain premium routes
const premiumPaths = [
'/milestone-tracker',
'/paywall',
'/financial-profile',
'/multi-scenario',
+ '/premium-onboarding'
];
-
const showPremiumCTA = !premiumPaths.includes(location.pathname);
return (
@@ -51,28 +56,69 @@ function App() {
{/* Main Content */}
+ {/* Default to /signin if no path */}
} />
+
+ {/* Public routes */}
}
+ element={
+
+ }
/>
} />
+
+ {/* Paywall - open to everyone for subscription */}
} />
+ {/* Authenticated routes */}
{isAuthenticated && (
<>
} />
} />
} />
} />
- } />
- } />
- } />
- } />
+
+ {/* Premium-only routes use */}
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
>
)}
- {/* Fallback */}
+ {/* Fallback if route not found */}
} />
diff --git a/src/components/CareerSearch.js b/src/components/CareerSearch.js
index c7c3492..ad4d8eb 100644
--- a/src/components/CareerSearch.js
+++ b/src/components/CareerSearch.js
@@ -84,7 +84,7 @@ const CareerSearch = ({ onCareerSelected }) => {
);
diff --git a/src/components/Paywall.js b/src/components/Paywall.js
index 2513c0c..05bf806 100644
--- a/src/components/Paywall.js
+++ b/src/components/Paywall.js
@@ -1,12 +1,19 @@
-// Paywall.js
import React from 'react';
-import { useNavigate } from 'react-router-dom';
+import { useLocation, useNavigate } from 'react-router-dom';
const Paywall = () => {
const navigate = useNavigate();
+ const { state } = useLocation();
+ // Extract the selectedCareer from location state
+ const { selectedCareer } = state || {};
const handleSubscribe = () => {
- navigate('/milestone-tracker');
+ // Once the user subscribes, navigate to MilestoneTracker
+ navigate('/PremiumOnboarding', {
+ state: {
+ selectedCareer,
+ },
+ });
};
return (
diff --git a/src/components/PopoutPanel.js b/src/components/PopoutPanel.js
index 9cf6d33..5db9c75 100644
--- a/src/components/PopoutPanel.js
+++ b/src/components/PopoutPanel.js
@@ -111,15 +111,16 @@ function PopoutPanel({
closePanel();
}
- // Original PlanMyPath logic
async function handlePlanMyPath() {
if (!token) {
alert("You need to be logged in to create a career path.");
return;
}
+
try {
+ // 1) Fetch existing career profiles (a.k.a. "careerPaths")
const allPathsResponse = await fetch(
- `${process.env.REACT_APP_API_URL}/premium/planned-path/all`,
+ `${process.env.REACT_APP_API_URL}/premium/career-profile/all`,
{
method: "GET",
headers: {
@@ -128,47 +129,67 @@ function PopoutPanel({
},
}
);
- if (!allPathsResponse.ok) throw new Error(`HTTP error ${allPathsResponse.status}`);
-
- const { careerPath } = await allPathsResponse.json();
- const match = careerPath.find((path) => path.career_name === data.title);
-
+
+ if (!allPathsResponse.ok) {
+ throw new Error(`HTTP error ${allPathsResponse.status}`);
+ }
+
+ // The server returns { careerPaths: [...] }
+ const { careerPaths } = await allPathsResponse.json();
+
+ // 2) Check if there's already a career path with the same name
+ const match = careerPaths.find((cp) => cp.career_name === data.title);
+
if (match) {
+ // If a path already exists for this career, confirm with the user
const decision = window.confirm(
- `A career path for "${data.title}" already exists.\n\n` +
+ `A career path (scenario) for "${data.title}" already exists.\n\n` +
`Click OK to RELOAD the existing path.\nClick Cancel to CREATE a new one.`
);
if (decision) {
+ // Reload existing path → go to Paywall
navigate("/paywall", {
state: {
- selectedCareer: { career_path_id: match.id, career_name: data.title },
+ selectedCareer: {
+ career_path_id: match.id, // 'id' is the primary key from the DB
+ career_name: data.title,
+ },
},
});
return;
}
}
-
- const newCareerPath = {
- career_path_id: uuidv4(),
- career_name: data.title,
- };
+
+ // 3) Otherwise, create a new career profile using POST /premium/career-profile
const newResponse = await fetch(
- `${process.env.REACT_APP_API_URL}/premium/planned-path`,
+ `${process.env.REACT_APP_API_URL}/premium/career-profile`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
- body: JSON.stringify(newCareerPath),
+ body: JSON.stringify({
+ // The server expects at least career_name
+ career_name: data.title,
+ // Optionally pass scenario_title, start_date, etc.
+ }),
}
);
- if (!newResponse.ok) throw new Error("Failed to create new career path.");
-
- navigate("/milestone-tracker", {
+
+ if (!newResponse.ok) {
+ throw new Error("Failed to create new career path.");
+ }
+
+ // The server returns something like { message: 'Career profile upserted.', career_path_id: 'xxx-xxx' }
+ const result = await newResponse.json();
+ const newlyCreatedId = result?.career_path_id;
+
+ // 4) Navigate to /paywall, passing the newly created career_path_id
+ navigate("/paywall", {
state: {
selectedCareer: {
- career_path_id: newCareerPath.career_path_id,
+ career_path_id: newlyCreatedId,
career_name: data.title,
},
},
@@ -177,6 +198,7 @@ function PopoutPanel({
console.error("Error in Plan My Path:", error);
}
}
+
// Filter & sort schools
const filteredAndSortedSchools = [...schools]
diff --git a/src/components/PremiumRoute.js b/src/components/PremiumRoute.js
new file mode 100644
index 0000000..5257388
--- /dev/null
+++ b/src/components/PremiumRoute.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import { Navigate } from 'react-router-dom';
+
+function PremiumRoute({ user, children }) {
+ if (!user) {
+ // Not even logged in; go to sign in
+ return ;
+ }
+
+ if (!user.is_premium) {
+ // Logged in but not premium; go to paywall
+ return ;
+ }
+
+ // User is logged in and has premium
+ return children;
+}
+
+export default PremiumRoute;
diff --git a/src/components/SignIn.js b/src/components/SignIn.js
index e672e4a..f784ae0 100644
--- a/src/components/SignIn.js
+++ b/src/components/SignIn.js
@@ -1,7 +1,7 @@
import React, { useRef, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
-function SignIn({ setIsAuthenticated }) {
+function SignIn({ setIsAuthenticated, setUser }) {
const navigate = useNavigate();
const usernameRef = useRef('');
const passwordRef = useRef('');
@@ -32,12 +32,22 @@ function SignIn({ setIsAuthenticated }) {
}
const data = await response.json();
- const { token, userId } = data;
+ // Destructure user, which includes is_premium, etc.
+ const { token, userId, user } = data;
+ // Store token & userId in localStorage
localStorage.setItem('token', token);
localStorage.setItem('userId', userId);
+ // Mark user as authenticated
setIsAuthenticated(true);
+
+ // Store the full user object in state, so we can check user.is_premium, etc.
+ if (setUser && user) {
+ setUser(user);
+ }
+
+ // Navigate to next screen
navigate('/getting-started');
} catch (error) {
setError(error.message);
@@ -48,13 +58,13 @@ function SignIn({ setIsAuthenticated }) {
Sign In
-
+
{error && (
{error}
)}
-
+
-
+
Don’t have an account?{' '}