Added task endpoints to server3 and updated delete milestone to also delete tasks.
This commit is contained in:
parent
b6c6814438
commit
3103b9ab29
@ -9,7 +9,6 @@ import jwt from 'jsonwebtoken';
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
// If you still need the projection logic somewhere else
|
|
||||||
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);
|
||||||
@ -56,7 +55,6 @@ const authenticatePremiumUser = (req, res, next) => {
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
CAREER PROFILE ENDPOINTS
|
CAREER PROFILE ENDPOINTS
|
||||||
(Renamed from planned-path to career-profile)
|
|
||||||
------------------------------------------------------------------ */
|
------------------------------------------------------------------ */
|
||||||
|
|
||||||
// GET the latest selected career profile
|
// GET the latest selected career profile
|
||||||
@ -114,8 +112,7 @@ app.get('/api/premium/career-profile/:careerPathId', authenticatePremiumUser, as
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// POST a new career profile
|
// POST a new career profile (upsert)
|
||||||
|
|
||||||
app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res) => {
|
app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res) => {
|
||||||
const {
|
const {
|
||||||
scenario_title,
|
scenario_title,
|
||||||
@ -143,7 +140,6 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
|
|||||||
const newCareerPathId = uuidv4();
|
const newCareerPathId = uuidv4();
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
|
|
||||||
// Upsert via ON CONFLICT(user_id, career_name)
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
INSERT INTO career_paths (
|
INSERT INTO career_paths (
|
||||||
id,
|
id,
|
||||||
@ -190,33 +186,30 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
|
|||||||
|
|
||||||
updated_at = ?
|
updated_at = ?
|
||||||
`, [
|
`, [
|
||||||
// 18 items for the INSERT columns
|
newCareerPathId,
|
||||||
newCareerPathId, // id
|
req.userId,
|
||||||
req.userId, // user_id
|
scenario_title || null,
|
||||||
scenario_title || null, // scenario_title
|
career_name,
|
||||||
career_name, // career_name
|
status || 'planned',
|
||||||
status || 'planned', // status
|
start_date || now,
|
||||||
start_date || now, // start_date
|
projected_end_date || null,
|
||||||
projected_end_date || null, // projected_end_date
|
college_enrollment_status || null,
|
||||||
college_enrollment_status || null, // college_enrollment_status
|
currently_working || null,
|
||||||
currently_working || null, // currently_working
|
|
||||||
|
|
||||||
planned_monthly_expenses ?? null, // planned_monthly_expenses
|
planned_monthly_expenses ?? null,
|
||||||
planned_monthly_debt_payments ?? null, // planned_monthly_debt_payments
|
planned_monthly_debt_payments ?? null,
|
||||||
planned_monthly_retirement_contribution ?? null,
|
planned_monthly_retirement_contribution ?? null,
|
||||||
planned_monthly_emergency_contribution ?? null,
|
planned_monthly_emergency_contribution ?? null,
|
||||||
planned_surplus_emergency_pct ?? null,
|
planned_surplus_emergency_pct ?? null,
|
||||||
planned_surplus_retirement_pct ?? null,
|
planned_surplus_retirement_pct ?? null,
|
||||||
planned_additional_income ?? null,
|
planned_additional_income ?? null,
|
||||||
|
|
||||||
now, // created_at
|
now,
|
||||||
now, // updated_at
|
now,
|
||||||
|
|
||||||
// Then 1 more param for "updated_at = ?" in the conflict update
|
|
||||||
now
|
now
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Optionally fetch the row's ID or entire row after upsert
|
|
||||||
const result = await db.get(`
|
const result = await db.get(`
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM career_paths
|
FROM career_paths
|
||||||
@ -234,93 +227,67 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// DELETE a career path (scenario) by ID (and associated data)
|
||||||
// Delete a career path (scenario) by ID
|
|
||||||
app.delete('/api/premium/career-profile/:careerPathId', authenticatePremiumUser, async (req, res) => {
|
app.delete('/api/premium/career-profile/:careerPathId', authenticatePremiumUser, async (req, res) => {
|
||||||
const { careerPathId } = req.params;
|
const { careerPathId } = req.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1) Confirm that this career_path belongs to the user
|
// 1) Confirm that this career_path belongs to the user
|
||||||
const existing = await db.get(
|
const existing = await db.get(`
|
||||||
`
|
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM career_paths
|
FROM career_paths
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
AND user_id = ?
|
AND user_id = ?
|
||||||
`,
|
`, [careerPathId, req.userId]);
|
||||||
[careerPathId, req.userId]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
return res.status(404).json({ error: 'Career path not found or not yours.' });
|
return res.status(404).json({ error: 'Career path not found or not yours.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Optionally delete the college_profile for this scenario
|
// 2) Delete the college_profile for this scenario
|
||||||
// (If you always keep 1-to-1 relationship: careerPathId => college_profile)
|
await db.run(`
|
||||||
await db.run(
|
|
||||||
`
|
|
||||||
DELETE FROM college_profiles
|
DELETE FROM college_profiles
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
AND career_path_id = ?
|
AND career_path_id = ?
|
||||||
`,
|
`, [req.userId, careerPathId]);
|
||||||
[req.userId, careerPathId]
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3) Optionally delete scenario’s milestones
|
// 3) Delete scenario’s milestones (and tasks/impacts)
|
||||||
// (and any associated tasks, impacts, etc.)
|
const scenarioMilestones = await db.all(`
|
||||||
// If you store tasks in tasks table, and impacts in milestone_impacts table:
|
|
||||||
|
|
||||||
// First find scenario milestones
|
|
||||||
const scenarioMilestones = await db.all(
|
|
||||||
`
|
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM milestones
|
FROM milestones
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
AND career_path_id = ?
|
AND career_path_id = ?
|
||||||
`,
|
`, [req.userId, careerPathId]);
|
||||||
[req.userId, careerPathId]
|
|
||||||
);
|
|
||||||
const milestoneIds = scenarioMilestones.map((m) => m.id);
|
const milestoneIds = scenarioMilestones.map((m) => m.id);
|
||||||
|
|
||||||
if (milestoneIds.length > 0) {
|
if (milestoneIds.length > 0) {
|
||||||
// Delete tasks for these milestones
|
|
||||||
const placeholders = milestoneIds.map(() => '?').join(',');
|
const placeholders = milestoneIds.map(() => '?').join(',');
|
||||||
await db.run(
|
|
||||||
`
|
// Delete tasks
|
||||||
|
await db.run(`
|
||||||
DELETE FROM tasks
|
DELETE FROM tasks
|
||||||
WHERE milestone_id IN (${placeholders})
|
WHERE milestone_id IN (${placeholders})
|
||||||
`,
|
`, milestoneIds);
|
||||||
milestoneIds
|
|
||||||
);
|
|
||||||
|
|
||||||
// Delete impacts for these milestones
|
// Delete impacts
|
||||||
await db.run(
|
await db.run(`
|
||||||
`
|
|
||||||
DELETE FROM milestone_impacts
|
DELETE FROM milestone_impacts
|
||||||
WHERE milestone_id IN (${placeholders})
|
WHERE milestone_id IN (${placeholders})
|
||||||
`,
|
`, milestoneIds);
|
||||||
milestoneIds
|
|
||||||
);
|
|
||||||
|
|
||||||
// Finally delete the milestones themselves
|
// Finally delete the milestones themselves
|
||||||
await db.run(
|
await db.run(`
|
||||||
`
|
|
||||||
DELETE FROM milestones
|
DELETE FROM milestones
|
||||||
WHERE id IN (${placeholders})
|
WHERE id IN (${placeholders})
|
||||||
`,
|
`, milestoneIds);
|
||||||
milestoneIds
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4) Finally delete the career_path row
|
// 4) Delete the career_path row
|
||||||
await db.run(
|
await db.run(`
|
||||||
`
|
|
||||||
DELETE FROM career_paths
|
DELETE FROM career_paths
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
AND id = ?
|
AND id = ?
|
||||||
`,
|
`, [req.userId, careerPathId]);
|
||||||
[req.userId, careerPathId]
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ message: 'Career path and related data successfully deleted.' });
|
res.json({ message: 'Career path and related data successfully deleted.' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -329,7 +296,6 @@ app.delete('/api/premium/career-profile/:careerPathId', authenticatePremiumUser,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
Milestone ENDPOINTS
|
Milestone ENDPOINTS
|
||||||
------------------------------------------------------------------ */
|
------------------------------------------------------------------ */
|
||||||
@ -355,7 +321,6 @@ app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) =>
|
|||||||
is_universal
|
is_universal
|
||||||
} = m;
|
} = m;
|
||||||
|
|
||||||
// Validate some required fields
|
|
||||||
if (!milestone_type || !title || !date || !career_path_id) {
|
if (!milestone_type || !title || !date || !career_path_id) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error: 'One or more milestones missing required fields',
|
error: 'One or more milestones missing required fields',
|
||||||
@ -393,7 +358,7 @@ app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) =>
|
|||||||
progress || 0,
|
progress || 0,
|
||||||
status || 'planned',
|
status || 'planned',
|
||||||
new_salary || null,
|
new_salary || null,
|
||||||
is_universal ? 1 : 0, // store 1 or 0
|
is_universal ? 1 : 0,
|
||||||
now,
|
now,
|
||||||
now
|
now
|
||||||
]);
|
]);
|
||||||
@ -413,7 +378,6 @@ app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) =>
|
|||||||
tasks: []
|
tasks: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Return array of created milestones
|
|
||||||
return res.status(201).json(createdMilestones);
|
return res.status(201).json(createdMilestones);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,7 +436,6 @@ app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) =>
|
|||||||
now
|
now
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Return the newly created single milestone object
|
|
||||||
const newMilestone = {
|
const newMilestone = {
|
||||||
id,
|
id,
|
||||||
user_id: req.userId,
|
user_id: req.userId,
|
||||||
@ -525,7 +488,6 @@ app.put('/api/premium/milestones/:milestoneId', authenticatePremiumUser, async (
|
|||||||
|
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
|
|
||||||
// Merge fields with existing if not provided
|
|
||||||
const finalMilestoneType = milestone_type || existing.milestone_type;
|
const finalMilestoneType = milestone_type || existing.milestone_type;
|
||||||
const finalTitle = title || existing.title;
|
const finalTitle = title || existing.title;
|
||||||
const finalDesc = description || existing.description;
|
const finalDesc = description || existing.description;
|
||||||
@ -537,7 +499,6 @@ app.put('/api/premium/milestones/:milestoneId', authenticatePremiumUser, async (
|
|||||||
const finalIsUniversal =
|
const finalIsUniversal =
|
||||||
is_universal != null ? (is_universal ? 1 : 0) : existing.is_universal;
|
is_universal != null ? (is_universal ? 1 : 0) : existing.is_universal;
|
||||||
|
|
||||||
// Update row
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
UPDATE milestones
|
UPDATE milestones
|
||||||
SET
|
SET
|
||||||
@ -568,14 +529,12 @@ app.put('/api/premium/milestones/:milestoneId', authenticatePremiumUser, async (
|
|||||||
req.userId
|
req.userId
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Return the updated record with tasks
|
// Return the updated milestone with tasks
|
||||||
const updatedMilestoneRow = await db.get(`
|
const updatedMilestoneRow = await db.get(`
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM milestones
|
FROM milestones
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, [milestoneId]);
|
`, [milestoneId]);
|
||||||
|
|
||||||
// Fetch tasks for this milestone
|
|
||||||
const tasks = await db.all(`
|
const tasks = await db.all(`
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM tasks
|
FROM tasks
|
||||||
@ -597,11 +556,9 @@ app.put('/api/premium/milestones/:milestoneId', authenticatePremiumUser, async (
|
|||||||
// GET all milestones for a given careerPathId
|
// GET all milestones for a given careerPathId
|
||||||
app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) => {
|
app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) => {
|
||||||
const { careerPathId } = req.query;
|
const { careerPathId } = req.query;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// if user wants universal=1 only, e.g. careerPathId=universal
|
// universal milestones
|
||||||
if (careerPathId === 'universal') {
|
if (careerPathId === 'universal') {
|
||||||
// For example, fetch all is_universal=1 for the user:
|
|
||||||
const universalRows = await db.all(`
|
const universalRows = await db.all(`
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM milestones
|
FROM milestones
|
||||||
@ -609,7 +566,6 @@ app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) =>
|
|||||||
AND is_universal = 1
|
AND is_universal = 1
|
||||||
`, [req.userId]);
|
`, [req.userId]);
|
||||||
|
|
||||||
// attach tasks if needed
|
|
||||||
const milestoneIds = universalRows.map(m => m.id);
|
const milestoneIds = universalRows.map(m => m.id);
|
||||||
let tasksByMilestone = {};
|
let tasksByMilestone = {};
|
||||||
if (milestoneIds.length > 0) {
|
if (milestoneIds.length > 0) {
|
||||||
@ -624,6 +580,7 @@ app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) =>
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniMils = universalRows.map(m => ({
|
const uniMils = universalRows.map(m => ({
|
||||||
...m,
|
...m,
|
||||||
tasks: tasksByMilestone[m.id] || []
|
tasks: tasksByMilestone[m.id] || []
|
||||||
@ -675,19 +632,16 @@ app.post('/api/premium/milestone/copy', authenticatePremiumUser, async (req, res
|
|||||||
return res.status(400).json({ error: 'Missing milestoneId or scenarioIds.' });
|
return res.status(400).json({ error: 'Missing milestoneId or scenarioIds.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1) Fetch the original
|
|
||||||
const original = await db.get(`
|
const original = await db.get(`
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM milestones
|
FROM milestones
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
AND user_id = ?
|
AND user_id = ?
|
||||||
`, [milestoneId, req.userId]);
|
`, [milestoneId, req.userId]);
|
||||||
|
|
||||||
if (!original) {
|
if (!original) {
|
||||||
return res.status(404).json({ error: 'Milestone not found or not owned by user.' });
|
return res.status(404).json({ error: 'Milestone not found or not owned by user.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Force is_universal=1 on the original
|
|
||||||
if (original.is_universal !== 1) {
|
if (original.is_universal !== 1) {
|
||||||
await db.run(`
|
await db.run(`
|
||||||
UPDATE milestones
|
UPDATE milestones
|
||||||
@ -695,12 +649,9 @@ app.post('/api/premium/milestone/copy', authenticatePremiumUser, async (req, res
|
|||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
AND user_id = ?
|
AND user_id = ?
|
||||||
`, [ milestoneId, req.userId ]);
|
`, [ milestoneId, req.userId ]);
|
||||||
|
|
||||||
// Also refresh "original" object if you want
|
|
||||||
original.is_universal = 1;
|
original.is_universal = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) If no origin_milestone_id, set it
|
|
||||||
let originId = original.origin_milestone_id || original.id;
|
let originId = original.origin_milestone_id || original.id;
|
||||||
if (!original.origin_milestone_id) {
|
if (!original.origin_milestone_id) {
|
||||||
await db.run(`
|
await db.run(`
|
||||||
@ -711,7 +662,6 @@ app.post('/api/premium/milestone/copy', authenticatePremiumUser, async (req, res
|
|||||||
`, [ originId, milestoneId, req.userId ]);
|
`, [ originId, milestoneId, req.userId ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4) fetch tasks & impacts
|
|
||||||
const tasks = await db.all(`
|
const tasks = await db.all(`
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM tasks
|
FROM tasks
|
||||||
@ -728,13 +678,9 @@ app.post('/api/premium/milestone/copy', authenticatePremiumUser, async (req, res
|
|||||||
const copiesCreated = [];
|
const copiesCreated = [];
|
||||||
|
|
||||||
for (let scenarioId of scenarioIds) {
|
for (let scenarioId of scenarioIds) {
|
||||||
if (scenarioId === original.career_path_id) {
|
if (scenarioId === original.career_path_id) continue; // skip if same scenario
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newMilestoneId = uuidv4();
|
const newMilestoneId = uuidv4();
|
||||||
|
|
||||||
// Always set isUniversal=1 on copies
|
|
||||||
const isUniversal = 1;
|
const isUniversal = 1;
|
||||||
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
@ -814,7 +760,8 @@ app.post('/api/premium/milestone/copy', authenticatePremiumUser, async (req, res
|
|||||||
end_date,
|
end_date,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
`, [
|
`, [
|
||||||
newImpactId,
|
newImpactId,
|
||||||
newMilestoneId,
|
newMilestoneId,
|
||||||
@ -859,22 +806,40 @@ app.delete('/api/premium/milestones/:milestoneId/all', authenticatePremiumUser,
|
|||||||
return res.status(404).json({ error: 'Milestone not found or not owned by user.' });
|
return res.status(404).json({ error: 'Milestone not found or not owned by user.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We'll remove all milestones (the original + copies) referencing the same originId
|
||||||
const originId = existing.origin_milestone_id || existing.id;
|
const originId = existing.origin_milestone_id || existing.id;
|
||||||
|
|
||||||
// 2) Delete all copies referencing that origin
|
// Find all those milestone IDs
|
||||||
await db.run(`
|
const allMilsToDelete = await db.all(`
|
||||||
DELETE FROM milestones
|
SELECT id
|
||||||
|
FROM milestones
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
AND origin_milestone_id = ?
|
AND (id = ? OR origin_milestone_id = ?)
|
||||||
`, [req.userId, originId]);
|
`, [req.userId, originId, originId]);
|
||||||
|
|
||||||
// Also delete the original if it doesn't store itself in origin_milestone_id
|
const milIDs = allMilsToDelete.map(m => m.id);
|
||||||
|
if (milIDs.length > 0) {
|
||||||
|
const placeholders = milIDs.map(() => '?').join(',');
|
||||||
|
|
||||||
|
// Delete tasks for those milestones
|
||||||
|
await db.run(`
|
||||||
|
DELETE FROM tasks
|
||||||
|
WHERE milestone_id IN (${placeholders})
|
||||||
|
`, milIDs);
|
||||||
|
|
||||||
|
// Delete impacts for those milestones
|
||||||
|
await db.run(`
|
||||||
|
DELETE FROM milestone_impacts
|
||||||
|
WHERE milestone_id IN (${placeholders})
|
||||||
|
`, milIDs);
|
||||||
|
|
||||||
|
// Finally remove the milestones themselves
|
||||||
await db.run(`
|
await db.run(`
|
||||||
DELETE FROM milestones
|
DELETE FROM milestones
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
AND id = ?
|
AND (id = ? OR origin_milestone_id = ?)
|
||||||
AND origin_milestone_id IS NULL
|
`, [req.userId, originId, originId]);
|
||||||
`, [req.userId, originId]);
|
}
|
||||||
|
|
||||||
res.json({ message: 'Deleted from all scenarios' });
|
res.json({ message: 'Deleted from all scenarios' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -883,7 +848,7 @@ app.delete('/api/premium/milestones/:milestoneId/all', authenticatePremiumUser,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// DELETE milestone from this scenario only
|
// DELETE milestone from THIS scenario only
|
||||||
app.delete('/api/premium/milestones/:milestoneId', authenticatePremiumUser, async (req, res) => {
|
app.delete('/api/premium/milestones/:milestoneId', authenticatePremiumUser, async (req, res) => {
|
||||||
const { milestoneId } = req.params;
|
const { milestoneId } = req.params;
|
||||||
|
|
||||||
@ -900,18 +865,25 @@ app.delete('/api/premium/milestones/:milestoneId', authenticatePremiumUser, asyn
|
|||||||
return res.status(404).json({ error: 'Milestone not found or not owned by user.' });
|
return res.status(404).json({ error: 'Milestone not found or not owned by user.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Delete the single row
|
// 2) Delete tasks associated with this milestone
|
||||||
|
await db.run(`
|
||||||
|
DELETE FROM tasks
|
||||||
|
WHERE milestone_id = ?
|
||||||
|
`, [milestoneId]);
|
||||||
|
|
||||||
|
// 3) Delete milestone impacts
|
||||||
|
await db.run(`
|
||||||
|
DELETE FROM milestone_impacts
|
||||||
|
WHERE milestone_id = ?
|
||||||
|
`, [milestoneId]);
|
||||||
|
|
||||||
|
// 4) Finally remove the milestone
|
||||||
await db.run(`
|
await db.run(`
|
||||||
DELETE FROM milestones
|
DELETE FROM milestones
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
AND user_id = ?
|
AND user_id = ?
|
||||||
`, [milestoneId, req.userId]);
|
`, [milestoneId, req.userId]);
|
||||||
|
|
||||||
// optionally also remove tasks + impacts if you want
|
|
||||||
// e.g.:
|
|
||||||
// await db.run('DELETE FROM tasks WHERE milestone_id = ?', [milestoneId]);
|
|
||||||
// await db.run('DELETE FROM milestone_impacts WHERE milestone_id = ?', [milestoneId]);
|
|
||||||
|
|
||||||
res.json({ message: 'Milestone deleted from this scenario.' });
|
res.json({ message: 'Milestone deleted from this scenario.' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting single milestone:', err);
|
console.error('Error deleting single milestone:', err);
|
||||||
@ -919,11 +891,9 @@ app.delete('/api/premium/milestones/:milestoneId', authenticatePremiumUser, asyn
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
FINANCIAL PROFILES (Renamed emergency_contribution)
|
FINANCIAL PROFILES
|
||||||
------------------------------------------------------------------ */
|
------------------------------------------------------------------ */
|
||||||
|
|
||||||
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(`
|
const row = await db.get(`
|
||||||
@ -954,7 +924,6 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if row exists
|
|
||||||
const existing = await db.get(`
|
const existing = await db.get(`
|
||||||
SELECT user_id
|
SELECT user_id
|
||||||
FROM financial_profiles
|
FROM financial_profiles
|
||||||
@ -962,7 +931,6 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
`, [req.userId]);
|
`, [req.userId]);
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
// Insert new row
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
INSERT INTO financial_profiles (
|
INSERT INTO financial_profiles (
|
||||||
user_id,
|
user_id,
|
||||||
@ -988,12 +956,11 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
retirement_savings || 0,
|
retirement_savings || 0,
|
||||||
emergency_fund || 0,
|
emergency_fund || 0,
|
||||||
retirement_contribution || 0,
|
retirement_contribution || 0,
|
||||||
emergency_contribution || 0, // store new field
|
emergency_contribution || 0,
|
||||||
extra_cash_emergency_pct || 0,
|
extra_cash_emergency_pct || 0,
|
||||||
extra_cash_retirement_pct || 0
|
extra_cash_retirement_pct || 0
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// Update existing
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
UPDATE financial_profiles
|
UPDATE financial_profiles
|
||||||
SET
|
SET
|
||||||
@ -1017,7 +984,7 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
retirement_savings || 0,
|
retirement_savings || 0,
|
||||||
emergency_fund || 0,
|
emergency_fund || 0,
|
||||||
retirement_contribution || 0,
|
retirement_contribution || 0,
|
||||||
emergency_contribution || 0, // updated field
|
emergency_contribution || 0,
|
||||||
extra_cash_emergency_pct || 0,
|
extra_cash_emergency_pct || 0,
|
||||||
extra_cash_retirement_pct || 0,
|
extra_cash_retirement_pct || 0,
|
||||||
req.userId
|
req.userId
|
||||||
@ -1034,7 +1001,6 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
COLLEGE PROFILES
|
COLLEGE PROFILES
|
||||||
------------------------------------------------------------------ */
|
------------------------------------------------------------------ */
|
||||||
|
|
||||||
app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, res) => {
|
app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, res) => {
|
||||||
const {
|
const {
|
||||||
career_path_id,
|
career_path_id,
|
||||||
@ -1064,11 +1030,8 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const user_id = req.userId;
|
const user_id = req.userId;
|
||||||
// For upsert, we either generate a new ID or (optionally) do a lookup for the old row's ID if you want to preserve it
|
|
||||||
// For simplicity, let's generate a new ID each time. We'll handle the conflict resolution below.
|
|
||||||
const newId = uuidv4();
|
const newId = uuidv4();
|
||||||
|
|
||||||
// Now do an INSERT ... ON CONFLICT(...fields...). In SQLite, we reference 'excluded' for the new values.
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
INSERT INTO college_profiles (
|
INSERT INTO college_profiles (
|
||||||
id,
|
id,
|
||||||
@ -1128,8 +1091,6 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
CURRENT_TIMESTAMP,
|
CURRENT_TIMESTAMP,
|
||||||
CURRENT_TIMESTAMP
|
CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
|
|
||||||
-- The magic:
|
|
||||||
ON CONFLICT(user_id, career_path_id, selected_school, selected_program, program_type)
|
ON CONFLICT(user_id, career_path_id, selected_school, selected_program, program_type)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
is_in_state = excluded.is_in_state,
|
is_in_state = excluded.is_in_state,
|
||||||
@ -1152,7 +1113,6 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
tuition = excluded.tuition,
|
tuition = excluded.tuition,
|
||||||
tuition_paid = excluded.tuition_paid,
|
tuition_paid = excluded.tuition_paid,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
;
|
|
||||||
`, {
|
`, {
|
||||||
':id': newId,
|
':id': newId,
|
||||||
':user_id': user_id,
|
':user_id': user_id,
|
||||||
@ -1181,12 +1141,8 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
':tuition_paid': tuition_paid || 0
|
':tuition_paid': tuition_paid || 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// If it was a conflict, the existing row is updated.
|
|
||||||
// If not, a new row is inserted with ID = newId.
|
|
||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
message: 'College profile upsert done.',
|
message: 'College profile upsert done.'
|
||||||
// You might do an extra SELECT here to find which ID the final row uses if you need it
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving college profile:', error);
|
console.error('Error saving college profile:', error);
|
||||||
@ -1194,10 +1150,9 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.get('/api/premium/college-profile', authenticatePremiumUser, async (req, res) => {
|
app.get('/api/premium/college-profile', authenticatePremiumUser, async (req, res) => {
|
||||||
const { careerPathId } = req.query;
|
const { careerPathId } = req.query;
|
||||||
// find row
|
try {
|
||||||
const row = await db.get(`
|
const row = await db.get(`
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM college_profiles
|
FROM college_profiles
|
||||||
@ -1207,6 +1162,10 @@ app.get('/api/premium/college-profile', authenticatePremiumUser, async (req, res
|
|||||||
LIMIT 1
|
LIMIT 1
|
||||||
`, [req.userId, careerPathId]);
|
`, [req.userId, careerPathId]);
|
||||||
res.json(row || {});
|
res.json(row || {});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching college profile:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to fetch college profile.' });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
@ -1214,11 +1173,16 @@ app.get('/api/premium/college-profile', authenticatePremiumUser, async (req, res
|
|||||||
------------------------------------------------------------------ */
|
------------------------------------------------------------------ */
|
||||||
app.post('/api/premium/financial-projection/:careerPathId', authenticatePremiumUser, async (req, res) => {
|
app.post('/api/premium/financial-projection/:careerPathId', authenticatePremiumUser, async (req, res) => {
|
||||||
const { careerPathId } = req.params;
|
const { careerPathId } = req.params;
|
||||||
const { projectionData, loanPaidOffMonth, finalEmergencySavings, finalRetirementSavings, finalLoanBalance } = req.body;
|
const {
|
||||||
|
projectionData,
|
||||||
|
loanPaidOffMonth,
|
||||||
|
finalEmergencySavings,
|
||||||
|
finalRetirementSavings,
|
||||||
|
finalLoanBalance
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const projectionId = uuidv4();
|
const projectionId = uuidv4();
|
||||||
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
INSERT INTO financial_projections (
|
INSERT INTO financial_projections (
|
||||||
id, user_id, career_path_id, projection_data,
|
id, user_id, career_path_id, projection_data,
|
||||||
@ -1275,13 +1239,14 @@ app.get('/api/premium/financial-projection/:careerPathId', authenticatePremiumUs
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------
|
||||||
|
TASK ENDPOINTS
|
||||||
|
------------------------------------------------------------------ */
|
||||||
|
|
||||||
// POST create a new task
|
// CREATE a new task (already existed, repeated here for clarity)
|
||||||
app.post('/api/premium/tasks', authenticatePremiumUser, async (req, res) => {
|
app.post('/api/premium/tasks', authenticatePremiumUser, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { milestone_id, title, description, due_date } = req.body;
|
const { milestone_id, title, description, due_date } = req.body;
|
||||||
|
|
||||||
// Ensure required fields
|
|
||||||
if (!milestone_id || !title) {
|
if (!milestone_id || !title) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error: 'Missing required fields',
|
error: 'Missing required fields',
|
||||||
@ -1295,7 +1260,6 @@ app.post('/api/premium/tasks', authenticatePremiumUser, async (req, res) => {
|
|||||||
FROM milestones
|
FROM milestones
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, [milestone_id]);
|
`, [milestone_id]);
|
||||||
|
|
||||||
if (!milestone || milestone.user_id !== req.userId) {
|
if (!milestone || milestone.user_id !== req.userId) {
|
||||||
return res.status(403).json({ error: 'Milestone not found or not yours.' });
|
return res.status(403).json({ error: 'Milestone not found or not yours.' });
|
||||||
}
|
}
|
||||||
@ -1303,7 +1267,6 @@ app.post('/api/premium/tasks', authenticatePremiumUser, async (req, res) => {
|
|||||||
const taskId = uuidv4();
|
const taskId = uuidv4();
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
|
|
||||||
// Insert the new task
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
INSERT INTO tasks (
|
INSERT INTO tasks (
|
||||||
id,
|
id,
|
||||||
@ -1327,7 +1290,6 @@ app.post('/api/premium/tasks', authenticatePremiumUser, async (req, res) => {
|
|||||||
now
|
now
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Return the newly created task as JSON
|
|
||||||
const newTask = {
|
const newTask = {
|
||||||
id: taskId,
|
id: taskId,
|
||||||
milestone_id,
|
milestone_id,
|
||||||
@ -1337,7 +1299,6 @@ app.post('/api/premium/tasks', authenticatePremiumUser, async (req, res) => {
|
|||||||
due_date: due_date || null,
|
due_date: due_date || null,
|
||||||
status: 'not_started'
|
status: 'not_started'
|
||||||
};
|
};
|
||||||
|
|
||||||
res.status(201).json(newTask);
|
res.status(201).json(newTask);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error creating task:', err);
|
console.error('Error creating task:', err);
|
||||||
@ -1345,57 +1306,90 @@ app.post('/api/premium/tasks', authenticatePremiumUser, async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET tasks for a milestone
|
// UPDATE an existing task
|
||||||
app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) => {
|
app.put('/api/premium/tasks/:taskId', authenticatePremiumUser, async (req, res) => {
|
||||||
const { careerPathId } = req.query;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Fetch the milestones for this user + path
|
const { taskId } = req.params;
|
||||||
const milestones = await db.all(`
|
const { title, description, due_date, status } = req.body;
|
||||||
SELECT *
|
|
||||||
FROM milestones
|
|
||||||
WHERE user_id = ?
|
|
||||||
AND career_path_id = ?
|
|
||||||
`, [req.userId, careerPathId]);
|
|
||||||
|
|
||||||
// 2. For each milestone, fetch tasks (or do a single join—see note below)
|
// Check ownership
|
||||||
// We'll do it in Node code for clarity:
|
const existing = await db.get(`
|
||||||
const milestoneIds = milestones.map(m => m.id);
|
SELECT user_id
|
||||||
let tasksByMilestone = {};
|
|
||||||
if (milestoneIds.length > 0) {
|
|
||||||
const tasks = await db.all(`
|
|
||||||
SELECT *
|
|
||||||
FROM tasks
|
FROM tasks
|
||||||
WHERE milestone_id IN (${milestoneIds.map(() => '?').join(',')})
|
WHERE id = ?
|
||||||
`, milestoneIds);
|
`, [taskId]);
|
||||||
|
|
||||||
// Group tasks by milestone_id
|
if (!existing || existing.user_id !== req.userId) {
|
||||||
tasksByMilestone = tasks.reduce((acc, t) => {
|
return res.status(404).json({ error: 'Task not found or not owned by you.' });
|
||||||
if (!acc[t.milestone_id]) acc[t.milestone_id] = [];
|
|
||||||
acc[t.milestone_id].push(t);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Attach tasks to each milestone object
|
const now = new Date().toISOString();
|
||||||
const milestonesWithTasks = milestones.map(m => ({
|
await db.run(`
|
||||||
...m,
|
UPDATE tasks
|
||||||
tasks: tasksByMilestone[m.id] || []
|
SET
|
||||||
}));
|
title = COALESCE(?, title),
|
||||||
|
description = COALESCE(?, description),
|
||||||
|
due_date = COALESCE(?, due_date),
|
||||||
|
status = COALESCE(?, status),
|
||||||
|
updated_at = ?
|
||||||
|
WHERE id = ?
|
||||||
|
`, [
|
||||||
|
title || null,
|
||||||
|
description || null,
|
||||||
|
due_date || null,
|
||||||
|
status || null,
|
||||||
|
now,
|
||||||
|
taskId
|
||||||
|
]);
|
||||||
|
|
||||||
res.json({ milestones: milestonesWithTasks });
|
// Return the updated task
|
||||||
|
const updatedTask = await db.get(`
|
||||||
|
SELECT *
|
||||||
|
FROM tasks
|
||||||
|
WHERE id = ?
|
||||||
|
`, [taskId]);
|
||||||
|
|
||||||
|
res.json(updatedTask);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching milestones with tasks:', err);
|
console.error('Error updating task:', err);
|
||||||
res.status(500).json({ error: 'Failed to fetch milestones.' });
|
res.status(500).json({ error: 'Failed to update task.' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/************************************************************************
|
// DELETE a task
|
||||||
* MILESTONE IMPACTS ENDPOINTS
|
app.delete('/api/premium/tasks/:taskId', authenticatePremiumUser, async (req, res) => {
|
||||||
************************************************************************/
|
try {
|
||||||
|
const { taskId } = req.params;
|
||||||
|
|
||||||
|
// Verify ownership
|
||||||
|
const existing = await db.get(`
|
||||||
|
SELECT user_id
|
||||||
|
FROM tasks
|
||||||
|
WHERE id = ?
|
||||||
|
`, [taskId]);
|
||||||
|
|
||||||
|
if (!existing || existing.user_id !== req.userId) {
|
||||||
|
return res.status(404).json({ error: 'Task not found or not owned by you.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.run(`
|
||||||
|
DELETE FROM tasks
|
||||||
|
WHERE id = ?
|
||||||
|
`, [taskId]);
|
||||||
|
|
||||||
|
res.json({ message: 'Task deleted successfully.' });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting task:', err);
|
||||||
|
res.status(500).json({ error: 'Failed to delete task.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------
|
||||||
|
MILESTONE IMPACTS ENDPOINTS
|
||||||
|
------------------------------------------------------------------ */
|
||||||
|
|
||||||
app.get('/api/premium/milestone-impacts', authenticatePremiumUser, async (req, res) => {
|
app.get('/api/premium/milestone-impacts', authenticatePremiumUser, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Example: GET /api/premium/milestone-impacts?milestone_id=12345
|
|
||||||
const { milestone_id } = req.query;
|
const { milestone_id } = req.query;
|
||||||
if (!milestone_id) {
|
if (!milestone_id) {
|
||||||
return res.status(400).json({ error: 'milestone_id is required.' });
|
return res.status(400).json({ error: 'milestone_id is required.' });
|
||||||
@ -1411,7 +1405,6 @@ app.get('/api/premium/milestone-impacts', authenticatePremiumUser, async (req, r
|
|||||||
return res.status(404).json({ error: 'Milestone not found or not owned by this user.' });
|
return res.status(404).json({ error: 'Milestone not found or not owned by this user.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch all impacts for that milestone
|
|
||||||
const impacts = await db.all(`
|
const impacts = await db.all(`
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
@ -1448,7 +1441,6 @@ app.post('/api/premium/milestone-impacts', authenticatePremiumUser, async (req,
|
|||||||
updated_at
|
updated_at
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// Basic checks
|
|
||||||
if (!milestone_id || !impact_type) {
|
if (!milestone_id || !impact_type) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error: 'milestone_id and impact_type are required.'
|
error: 'milestone_id and impact_type are required.'
|
||||||
@ -1465,13 +1457,11 @@ app.post('/api/premium/milestone-impacts', authenticatePremiumUser, async (req,
|
|||||||
return res.status(403).json({ error: 'Milestone not found or not owned by this user.' });
|
return res.status(403).json({ error: 'Milestone not found or not owned by this user.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate UUID for this new Impact
|
|
||||||
const newUUID = uuidv4();
|
const newUUID = uuidv4();
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
const finalCreated = created_at || now;
|
const finalCreated = created_at || now;
|
||||||
const finalUpdated = updated_at || now;
|
const finalUpdated = updated_at || now;
|
||||||
|
|
||||||
// Insert row WITH that UUID into the "id" column
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
INSERT INTO milestone_impacts (
|
INSERT INTO milestone_impacts (
|
||||||
id,
|
id,
|
||||||
@ -1497,7 +1487,6 @@ app.post('/api/premium/milestone-impacts', authenticatePremiumUser, async (req,
|
|||||||
finalUpdated
|
finalUpdated
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Fetch & return the inserted row
|
|
||||||
const insertedRow = await db.get(`
|
const insertedRow = await db.get(`
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
@ -1520,9 +1509,7 @@ app.post('/api/premium/milestone-impacts', authenticatePremiumUser, async (req,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/************************************************************************
|
// UPDATE an existing milestone impact
|
||||||
* UPDATE an existing milestone impact (PUT)
|
|
||||||
************************************************************************/
|
|
||||||
app.put('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser, async (req, res) => {
|
app.put('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { impactId } = req.params;
|
const { impactId } = req.params;
|
||||||
@ -1535,7 +1522,7 @@ app.put('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser, asy
|
|||||||
end_date = null
|
end_date = null
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// 1) Check this impact belongs to user
|
// check ownership
|
||||||
const existing = await db.get(`
|
const existing = await db.get(`
|
||||||
SELECT mi.id, m.user_id
|
SELECT mi.id, m.user_id
|
||||||
FROM milestone_impacts mi
|
FROM milestone_impacts mi
|
||||||
@ -1547,8 +1534,6 @@ app.put('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser, asy
|
|||||||
}
|
}
|
||||||
|
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
|
|
||||||
// 2) Update
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
UPDATE milestone_impacts
|
UPDATE milestone_impacts
|
||||||
SET
|
SET
|
||||||
@ -1571,7 +1556,6 @@ app.put('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser, asy
|
|||||||
impactId
|
impactId
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 3) Return updated
|
|
||||||
const updatedRow = await db.get(`
|
const updatedRow = await db.get(`
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
@ -1594,14 +1578,12 @@ app.put('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser, asy
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/************************************************************************
|
// DELETE an existing milestone impact
|
||||||
* DELETE an existing milestone impact
|
|
||||||
************************************************************************/
|
|
||||||
app.delete('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser, async (req, res) => {
|
app.delete('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { impactId } = req.params;
|
const { impactId } = req.params;
|
||||||
|
|
||||||
// 1) check ownership
|
// check ownership
|
||||||
const existing = await db.get(`
|
const existing = await db.get(`
|
||||||
SELECT mi.id, m.user_id
|
SELECT mi.id, m.user_id
|
||||||
FROM milestone_impacts mi
|
FROM milestone_impacts mi
|
||||||
@ -1613,7 +1595,6 @@ app.delete('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser,
|
|||||||
return res.status(404).json({ error: 'Impact not found or not owned by user.' });
|
return res.status(404).json({ error: 'Impact not found or not owned by user.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Delete
|
|
||||||
await db.run(`
|
await db.run(`
|
||||||
DELETE FROM milestone_impacts
|
DELETE FROM milestone_impacts
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
@ -1626,6 +1607,9 @@ app.delete('/api/premium/milestone-impacts/:impactId', authenticatePremiumUser,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------
|
||||||
|
FALLBACK (404 for unmatched routes)
|
||||||
|
------------------------------------------------------------------ */
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
console.warn(`No route matched for ${req.method} ${req.originalUrl}`);
|
console.warn(`No route matched for ${req.method} ${req.originalUrl}`);
|
||||||
res.status(404).json({ error: 'Not found' });
|
res.status(404).json({ error: 'Not found' });
|
||||||
|
Loading…
Reference in New Issue
Block a user