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 { simulateFinancialProjection } from '../src/utils/FinancialProjectionService.js';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
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
|
||||
app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) => {
|
||||
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
|
||||
app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) => {
|
||||
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) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
@ -335,36 +330,8 @@ app.delete('/api/premium/milestones/:id', authenticatePremiumUser, async (req, r
|
||||
}
|
||||
});
|
||||
|
||||
// Archive current career to history
|
||||
app.post('/api/premium/career-history', authenticatePremiumUser, async (req, res) => {
|
||||
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' });
|
||||
}
|
||||
});
|
||||
|
||||
//Financial Profile premium services
|
||||
//Get financial profile
|
||||
app.get('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
|
||||
try {
|
||||
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)
|
||||
|
||||
// Save or update financial profile
|
||||
app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
|
||||
const {
|
||||
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 {
|
||||
const history = await db.all(
|
||||
`SELECT * FROM career_history WHERE user_id = ? ORDER BY start_date DESC;`,
|
||||
[req.userId]
|
||||
const careerPathId = uuidv4();
|
||||
|
||||
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) {
|
||||
console.error('Error fetching career history:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch career history' });
|
||||
console.error('Error saving career onboarding data:', error);
|
||||
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 MilestoneTracker from "./components/MilestoneTracker.js";
|
||||
import Paywall from "./components/Paywall.js";
|
||||
import OnboardingContainer from './components/PremiumOnboarding/OnboardingContainer.js';
|
||||
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
@ -51,6 +53,8 @@ function App() {
|
||||
<Route path="/profile" element={<UserProfile />} />
|
||||
<Route path="/milestone-tracker" element={<MilestoneTracker />} />
|
||||
<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