Added PremiumOnboarding components and backend endpoints. Tested, working
This commit is contained in:
parent
12fba3e3f0
commit
c9cdceb4b0
@ -11,10 +11,6 @@ import path from 'path';
|
|||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { simulateFinancialProjection } from '../src/utils/FinancialProjectionService.js';
|
import { simulateFinancialProjection } from '../src/utils/FinancialProjectionService.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
@ -132,6 +128,8 @@ app.post('/api/premium/planned-path', authenticatePremiumUser, async (req, res)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Milestones premium services
|
||||||
// Save a new milestone
|
// Save a new milestone
|
||||||
app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) => {
|
app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) => {
|
||||||
const rawMilestones = Array.isArray(req.body.milestones) ? req.body.milestones : [req.body];
|
const rawMilestones = Array.isArray(req.body.milestones) ? req.body.milestones : [req.body];
|
||||||
@ -220,8 +218,6 @@ app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Get all milestones
|
// Get all milestones
|
||||||
app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) => {
|
app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@ -243,8 +239,7 @@ app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update an existing milestone
|
||||||
/// Update an existing milestone
|
|
||||||
app.put('/api/premium/milestones/:id', authenticatePremiumUser, async (req, res) => {
|
app.put('/api/premium/milestones/:id', authenticatePremiumUser, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
@ -335,36 +330,8 @@ app.delete('/api/premium/milestones/:id', authenticatePremiumUser, async (req, r
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Archive current career to history
|
//Financial Profile premium services
|
||||||
app.post('/api/premium/career-history', authenticatePremiumUser, async (req, res) => {
|
//Get financial profile
|
||||||
const { career_path_id, company } = req.body;
|
|
||||||
|
|
||||||
if (!career_path_id || !company) {
|
|
||||||
return res.status(400).json({ error: 'Career path ID and company are required' });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const career = await db.get(`SELECT * FROM career_path WHERE id = ? AND user_id = ?`, [career_path_id, req.userId]);
|
|
||||||
|
|
||||||
if (!career) {
|
|
||||||
return res.status(404).json({ error: 'Career path not found' });
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.run(
|
|
||||||
`INSERT INTO career_history (user_id, job_title, company, start_date)
|
|
||||||
VALUES (?, ?, ?, DATE('now'))`,
|
|
||||||
[req.userId, career.job_title, company]
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.run(`DELETE FROM career_path WHERE id = ?`, [career_path_id]);
|
|
||||||
|
|
||||||
res.status(201).json({ message: 'Career moved to history successfully' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error moving career to history:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to update career history' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
|
app.get('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const row = await db.get(`SELECT * FROM financial_profile WHERE user_id = ?`, [req.userId]);
|
const row = await db.get(`SELECT * FROM financial_profile WHERE user_id = ?`, [req.userId]);
|
||||||
@ -377,6 +344,7 @@ app.get('/api/premium/financial-profile', authenticatePremiumUser, async (req, r
|
|||||||
|
|
||||||
// Backend code (server3.js)
|
// Backend code (server3.js)
|
||||||
|
|
||||||
|
// Save or update financial profile
|
||||||
app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
|
app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
|
||||||
const {
|
const {
|
||||||
currentSalary, additionalIncome, monthlyExpenses, monthlyDebtPayments,
|
currentSalary, additionalIncome, monthlyExpenses, monthlyDebtPayments,
|
||||||
@ -480,21 +448,122 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//PreimumOnboarding
|
||||||
|
//Career onboarding
|
||||||
|
app.post('/api/premium/onboarding/career', authenticatePremiumUser, async (req, res) => {
|
||||||
|
const { career_name, status, start_date, projected_end_date } = req.body;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Retrieve career history
|
|
||||||
app.get('/api/premium/career-history', authenticatePremiumUser, async (req, res) => {
|
|
||||||
try {
|
try {
|
||||||
const history = await db.all(
|
const careerPathId = uuidv4();
|
||||||
`SELECT * FROM career_history WHERE user_id = ? ORDER BY start_date DESC;`,
|
|
||||||
[req.userId]
|
await db.run(`
|
||||||
|
INSERT INTO career_path (id, user_id, career_name, status, start_date, projected_end_date)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
|
[careerPathId, req.userId, career_name, status || 'planned', start_date || new Date().toISOString(), projected_end_date || null]
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ careerHistory: history });
|
res.status(201).json({ message: 'Career onboarding data saved.', careerPathId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching career history:', error);
|
console.error('Error saving career onboarding data:', error);
|
||||||
res.status(500).json({ error: 'Failed to fetch career history' });
|
res.status(500).json({ error: 'Failed to save career onboarding data.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Financial onboarding
|
||||||
|
app.post('/api/premium/onboarding/financial', authenticatePremiumUser, async (req, res) => {
|
||||||
|
const {
|
||||||
|
current_salary, additional_income, monthly_expenses, monthly_debt_payments,
|
||||||
|
retirement_savings, retirement_contribution, emergency_fund
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.run(`
|
||||||
|
INSERT INTO financial_profile (
|
||||||
|
id, user_id, current_salary, additional_income, monthly_expenses,
|
||||||
|
monthly_debt_payments, retirement_savings, retirement_contribution, emergency_fund, updated_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`,
|
||||||
|
[
|
||||||
|
uuidv4(), req.userId, current_salary, additional_income, monthly_expenses,
|
||||||
|
monthly_debt_payments, retirement_savings, retirement_contribution, emergency_fund
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(201).json({ message: 'Financial onboarding data saved.' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving financial onboarding data:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to save financial onboarding data.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//College onboarding
|
||||||
|
app.post('/api/premium/onboarding/college', authenticatePremiumUser, async (req, res) => {
|
||||||
|
const {
|
||||||
|
in_college, expected_graduation, selected_school, selected_program,
|
||||||
|
program_type, is_online, credit_hours_per_year, hours_completed
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.run(`
|
||||||
|
INSERT INTO financial_profile (
|
||||||
|
id, user_id, in_college, expected_graduation, selected_school,
|
||||||
|
selected_program, program_type, is_online, credit_hours_per_year,
|
||||||
|
hours_completed, updated_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`,
|
||||||
|
[
|
||||||
|
uuidv4(), req.userId, in_college ? 1 : 0, expected_graduation, selected_school,
|
||||||
|
selected_program, program_type, is_online, credit_hours_per_year,
|
||||||
|
hours_completed
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(201).json({ message: 'College onboarding data saved.' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving college onboarding data:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to save college onboarding data.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Financial Projection Premium Services
|
||||||
|
// Save financial projection for a specific careerPathId
|
||||||
|
app.post('/api/premium/financial-projection/:careerPathId', authenticatePremiumUser, async (req, res) => {
|
||||||
|
const { careerPathId } = req.params;
|
||||||
|
const { projectionData } = req.body; // JSON containing detailed financial projections
|
||||||
|
|
||||||
|
try {
|
||||||
|
const projectionId = uuidv4();
|
||||||
|
|
||||||
|
await db.run(`
|
||||||
|
INSERT INTO financial_projections (id, user_id, career_path_id, projection_json)
|
||||||
|
VALUES (?, ?, ?, ?)`,
|
||||||
|
[projectionId, req.userId, careerPathId, JSON.stringify(projectionData)]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(201).json({ message: 'Financial projection saved.', projectionId });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving financial projection:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to save financial projection.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get financial projection for a specific careerPathId
|
||||||
|
app.get('/api/premium/financial-projection/:careerPathId', authenticatePremiumUser, async (req, res) => {
|
||||||
|
const { careerPathId } = req.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const projection = await db.get(`
|
||||||
|
SELECT projection_json FROM financial_projections
|
||||||
|
WHERE user_id = ? AND career_path_id = ?`,
|
||||||
|
[req.userId, careerPathId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!projection) {
|
||||||
|
return res.status(404).json({ error: 'Projection not found.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json(JSON.parse(projection.projection_json));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching financial projection:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to fetch financial projection.' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ import UserProfile from './components/UserProfile.js';
|
|||||||
import FinancialProfileForm from './components/FinancialProfileForm.js';
|
import FinancialProfileForm from './components/FinancialProfileForm.js';
|
||||||
import MilestoneTracker from "./components/MilestoneTracker.js";
|
import MilestoneTracker from "./components/MilestoneTracker.js";
|
||||||
import Paywall from "./components/Paywall.js";
|
import Paywall from "./components/Paywall.js";
|
||||||
|
import OnboardingContainer from './components/PremiumOnboarding/OnboardingContainer.js';
|
||||||
|
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@ -51,6 +53,8 @@ function App() {
|
|||||||
<Route path="/profile" element={<UserProfile />} />
|
<Route path="/profile" element={<UserProfile />} />
|
||||||
<Route path="/milestone-tracker" element={<MilestoneTracker />} />
|
<Route path="/milestone-tracker" element={<MilestoneTracker />} />
|
||||||
<Route path="/financial-profile" element={<FinancialProfileForm />} />
|
<Route path="/financial-profile" element={<FinancialProfileForm />} />
|
||||||
|
<Route path="/premium-onboarding" element={<OnboardingContainer />} />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
36
src/components/PremiumOnboarding/CareerOnboarding.js
Normal file
36
src/components/PremiumOnboarding/CareerOnboarding.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// CareerOnboarding.js
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
const CareerOnboarding = ({ nextStep, prevStep }) => {
|
||||||
|
const [careerData, setCareerData] = useState({
|
||||||
|
currentJob: '',
|
||||||
|
industry: '',
|
||||||
|
employmentStatus: '',
|
||||||
|
careerGoal: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
setCareerData({ ...careerData, [e.target.name]: e.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Career Details</h2>
|
||||||
|
<input name="currentJob" placeholder="Current Job Title" onChange={handleChange} />
|
||||||
|
<input name="industry" placeholder="Industry" onChange={handleChange} />
|
||||||
|
<select name="employmentStatus" onChange={handleChange}>
|
||||||
|
<option value="">Employment Status</option>
|
||||||
|
<option value="FT">Full-Time</option>
|
||||||
|
<option value="PT">Part-Time</option>
|
||||||
|
<option value="Unemployed">Unemployed</option>
|
||||||
|
<option value="Student">Student</option>
|
||||||
|
</select>
|
||||||
|
<input name="careerGoal" placeholder="Career Goal (optional)" onChange={handleChange} />
|
||||||
|
|
||||||
|
<button onClick={prevStep}>Back</button>
|
||||||
|
<button onClick={nextStep}>Next: Financial →</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CareerOnboarding;
|
35
src/components/PremiumOnboarding/CollegeOnboarding.js
Normal file
35
src/components/PremiumOnboarding/CollegeOnboarding.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// CollegeOnboarding.js
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
const CollegeOnboarding = ({ nextStep, prevStep }) => {
|
||||||
|
const [collegeData, setCollegeData] = useState({
|
||||||
|
studentStatus: '',
|
||||||
|
school: '',
|
||||||
|
program: '',
|
||||||
|
creditHoursPerYear: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
setCollegeData({ ...collegeData, [e.target.name]: e.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>College Plans (optional)</h2>
|
||||||
|
<select name="studentStatus" onChange={handleChange}>
|
||||||
|
<option value="">Student Status</option>
|
||||||
|
<option value="Enrolled">Currently Enrolled</option>
|
||||||
|
<option value="Prospective">Prospective Student</option>
|
||||||
|
<option value="None">Not Applicable</option>
|
||||||
|
</select>
|
||||||
|
<input name="school" placeholder="School Name" onChange={handleChange} />
|
||||||
|
<input name="program" placeholder="Program Name" onChange={handleChange} />
|
||||||
|
<input name="creditHoursPerYear" placeholder="Credit Hours per Year" type="number" onChange={handleChange} />
|
||||||
|
|
||||||
|
<button onClick={prevStep}>← Previous: Financial</button>
|
||||||
|
<button onClick={nextStep}>Finish Onboarding</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CollegeOnboarding;
|
30
src/components/PremiumOnboarding/FinancialOnboarding.js
Normal file
30
src/components/PremiumOnboarding/FinancialOnboarding.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// FinancialOnboarding.js
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
const FinancialOnboarding = ({ nextStep, prevStep }) => {
|
||||||
|
const [financialData, setFinancialData] = useState({
|
||||||
|
salary: '',
|
||||||
|
expenses: '',
|
||||||
|
savings: '',
|
||||||
|
debts: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
setFinancialData({ ...financialData, [e.target.name]: e.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Financial Details</h2>
|
||||||
|
<input name="salary" placeholder="Annual Salary" type="number" onChange={handleChange} />
|
||||||
|
<input name="expenses" placeholder="Monthly Expenses" type="number" onChange={handleChange} />
|
||||||
|
<input name="savings" placeholder="Total Savings" type="number" onChange={handleChange} />
|
||||||
|
<input name="debts" placeholder="Outstanding Debts" type="number" onChange={handleChange} />
|
||||||
|
|
||||||
|
<button onClick={prevStep}>← Previous: Career</button>
|
||||||
|
<button onClick={nextStep}>Next: College →</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FinancialOnboarding;
|
28
src/components/PremiumOnboarding/OnboardingContainer.js
Normal file
28
src/components/PremiumOnboarding/OnboardingContainer.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// OnboardingContainer.js
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import PremiumWelcome from './PremiumWelcome.js';
|
||||||
|
import CareerOnboarding from './CareerOnboarding.js';
|
||||||
|
import FinancialOnboarding from './FinancialOnboarding.js';
|
||||||
|
import CollegeOnboarding from './CollegeOnboarding.js';
|
||||||
|
|
||||||
|
const OnboardingContainer = () => {
|
||||||
|
const [step, setStep] = useState(0);
|
||||||
|
|
||||||
|
const nextStep = () => setStep(step + 1);
|
||||||
|
const prevStep = () => setStep(step - 1);
|
||||||
|
|
||||||
|
const onboardingSteps = [
|
||||||
|
<PremiumWelcome nextStep={nextStep} />,
|
||||||
|
<CareerOnboarding nextStep={nextStep} prevStep={prevStep} />,
|
||||||
|
<FinancialOnboarding nextStep={nextStep} prevStep={prevStep} />,
|
||||||
|
<CollegeOnboarding nextStep={nextStep} prevStep={prevStep} />,
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="onboarding-container">
|
||||||
|
{onboardingSteps[step]}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OnboardingContainer;
|
14
src/components/PremiumOnboarding/PremiumWelcome.js
Normal file
14
src/components/PremiumOnboarding/PremiumWelcome.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// PremiumWelcome.js
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const PremiumWelcome = ({ nextStep }) => (
|
||||||
|
<div>
|
||||||
|
<h2>Welcome to AptivaAI Premium!</h2>
|
||||||
|
<p>
|
||||||
|
Let's get started by gathering some quick information to personalize your experience.
|
||||||
|
</p>
|
||||||
|
<button onClick={nextStep}>Get Started</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default PremiumWelcome;
|
BIN
user_profile.db
BIN
user_profile.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user