diff --git a/src/components/MilestoneTimeline.js b/src/components/MilestoneTimeline.js
index 996c37b..60e2ade 100644
--- a/src/components/MilestoneTimeline.js
+++ b/src/components/MilestoneTimeline.js
@@ -3,13 +3,13 @@ import React, { useEffect, useState, useCallback } from 'react';
const today = new Date();
-const MilestoneTimeline = ({
+export default function MilestoneTimeline({
careerPathId,
authFetch,
activeView,
setActiveView,
- onMilestoneUpdated // optional callback if you want the parent to be notified of changes
-}) => {
+ onMilestoneUpdated
+}) {
const [milestones, setMilestones] = useState({ Career: [], Financial: [] });
// "new or edit" milestone form data
@@ -22,8 +22,6 @@ const MilestoneTimeline = ({
impacts: [],
isUniversal: 0
});
-
- // We'll track which existing impacts are removed so we can do a DELETE if needed
const [impactsToDelete, setImpactsToDelete] = useState([]);
const [showForm, setShowForm] = useState(false);
@@ -33,17 +31,17 @@ const MilestoneTimeline = ({
const [showTaskForm, setShowTaskForm] = useState(null);
const [newTask, setNewTask] = useState({ title: '', description: '', due_date: '' });
- // For the Copy wizard
+ // The copy wizard
const [scenarios, setScenarios] = useState([]);
const [copyWizardMilestone, setCopyWizardMilestone] = useState(null);
// ------------------------------------------------------------------
- // 1) Impact Helper Functions (define them first to avoid scoping errors)
+ // 1) HELPER FUNCTIONS (defined above usage)
// ------------------------------------------------------------------
- // Insert a new blank impact into newMilestone.impacts
- const addNewImpact = () => {
- setNewMilestone(prev => ({
+ // Insert a new blank impact
+ function addNewImpact() {
+ setNewMilestone((prev) => ({
...prev,
impacts: [
...prev.impacts,
@@ -56,53 +54,33 @@ const MilestoneTimeline = ({
}
]
}));
- };
+ }
// Remove an impact from newMilestone.impacts
- const removeImpact = (idx) => {
- setNewMilestone(prev => {
+ function removeImpact(idx) {
+ setNewMilestone((prev) => {
const newImpacts = [...prev.impacts];
const removed = newImpacts[idx];
- if (removed.id) {
- // queue up for DB DELETE
- setImpactsToDelete(old => [...old, removed.id]);
+ if (removed && removed.id) {
+ // queue for DB DELETE
+ setImpactsToDelete((old) => [...old, removed.id]);
}
newImpacts.splice(idx, 1);
return { ...prev, impacts: newImpacts };
});
- };
+ }
// Update a specific impact property
- const updateImpact = (idx, field, value) => {
- setNewMilestone(prev => {
+ function updateImpact(idx, field, value) {
+ setNewMilestone((prev) => {
const newImpacts = [...prev.impacts];
newImpacts[idx] = { ...newImpacts[idx], [field]: value };
return { ...prev, impacts: newImpacts };
});
- };
+ }
// ------------------------------------------------------------------
- // 2) Load scenarios (for copy wizard)
- // ------------------------------------------------------------------
- useEffect(() => {
- async function loadScenarios() {
- try {
- const res = await authFetch('/api/premium/career-profile/all');
- if (res.ok) {
- const data = await res.json();
- setScenarios(data.careerPaths || []);
- } else {
- console.error('Failed to load scenarios. Status:', res.status);
- }
- } catch (err) {
- console.error('Error loading scenarios for copy wizard:', err);
- }
- }
- loadScenarios();
- }, [authFetch]);
-
- // ------------------------------------------------------------------
- // 3) Fetch milestones for the current scenario
+ // 2) fetchMilestones => local state
// ------------------------------------------------------------------
const fetchMilestones = useCallback(async () => {
if (!careerPathId) return;
@@ -114,12 +92,12 @@ const MilestoneTimeline = ({
}
const data = await res.json();
if (!data.milestones) {
- console.warn('No milestones returned:', data);
+ console.warn('No milestones field in response:', data);
return;
}
const categorized = { Career: [], Financial: [] };
- data.milestones.forEach(m => {
+ data.milestones.forEach((m) => {
if (categorized[m.milestone_type]) {
categorized[m.milestone_type].push(m);
} else {
@@ -138,9 +116,27 @@ const MilestoneTimeline = ({
}, [fetchMilestones]);
// ------------------------------------------------------------------
- // 4) "Edit" an existing milestone => load impacts
+ // 3) Load scenarios for copy wizard
// ------------------------------------------------------------------
- const handleEditMilestone = async (m) => {
+ useEffect(() => {
+ async function loadScenarios() {
+ try {
+ const res = await authFetch('/api/premium/career-profile/all');
+ if (res.ok) {
+ const data = await res.json();
+ setScenarios(data.careerPaths || []);
+ }
+ } catch (err) {
+ console.error('Error loading scenarios for copy wizard:', err);
+ }
+ }
+ loadScenarios();
+ }, [authFetch]);
+
+ // ------------------------------------------------------------------
+ // 4) Edit Milestone => fetch impacts
+ // ------------------------------------------------------------------
+ async function handleEditMilestone(m) {
try {
setImpactsToDelete([]);
@@ -158,7 +154,7 @@ const MilestoneTimeline = ({
date: m.date || '',
progress: m.progress || 0,
newSalary: m.new_salary || '',
- impacts: fetchedImpacts.map(imp => ({
+ impacts: fetchedImpacts.map((imp) => ({
id: imp.id,
impact_type: imp.impact_type || 'ONE_TIME',
direction: imp.direction || 'subtract',
@@ -171,16 +167,15 @@ const MilestoneTimeline = ({
setEditingMilestone(m);
setShowForm(true);
-
} catch (err) {
- console.error('Error editing milestone:', err);
+ console.error('Error in handleEditMilestone:', err);
}
- };
+ }
// ------------------------------------------------------------------
- // 5) Save (create or update) a milestone => handle impacts if needed
+ // 5) Save (create/update) => handle impacts
// ------------------------------------------------------------------
- const saveMilestone = async () => {
+ async function saveMilestone() {
if (!activeView) return;
const url = editingMilestone
@@ -219,7 +214,6 @@ const MilestoneTimeline = ({
const savedMilestone = await res.json();
console.log('Milestone saved/updated:', savedMilestone);
- // If financial => handle impacts
if (activeView === 'Financial') {
// 1) Delete old impacts
for (const impactId of impactsToDelete) {
@@ -232,7 +226,6 @@ const MilestoneTimeline = ({
}
}
}
-
// 2) Insert/Update new impacts
for (let i = 0; i < newMilestone.impacts.length; i++) {
const imp = newMilestone.impacts[i];
@@ -253,7 +246,7 @@ const MilestoneTimeline = ({
});
if (!impRes.ok) {
const errImp = await impRes.json();
- console.error('Failed updating existing impact:', errImp);
+ console.error('Failed updating impact:', errImp);
}
} else {
// new => POST
@@ -277,20 +270,10 @@ const MilestoneTimeline = ({
}
}
- // optional local state update to avoid re-fetch
- setMilestones((prev) => {
- const newState = { ...prev };
- if (editingMilestone) {
- newState[activeView] = newState[activeView].map(m =>
- m.id === editingMilestone.id ? savedMilestone : m
- );
- } else {
- newState[activeView].push(savedMilestone);
- }
- return newState;
- });
+ // Optionally re-fetch or update local
+ await fetchMilestones();
- // reset the form
+ // reset form
setShowForm(false);
setEditingMilestone(null);
setNewMilestone({
@@ -304,22 +287,18 @@ const MilestoneTimeline = ({
});
setImpactsToDelete([]);
- // optionally re-fetch from DB
- // await fetchMilestones();
-
if (onMilestoneUpdated) {
onMilestoneUpdated();
}
-
} catch (err) {
console.error('Error saving milestone:', err);
}
- };
+ }
// ------------------------------------------------------------------
- // 6) addTask => attach a new task to an existing milestone
+ // 6) Add Task
// ------------------------------------------------------------------
- const addTask = async (milestoneId) => {
+ async function addTask(milestoneId) {
try {
const taskPayload = {
milestone_id: milestoneId,
@@ -343,32 +322,18 @@ const MilestoneTimeline = ({
const createdTask = await res.json();
console.log('Task created:', createdTask);
- // update local state
- setMilestones((prev) => {
- const newState = { ...prev };
- ['Career', 'Financial'].forEach((cat) => {
- newState[cat] = newState[cat].map((m) => {
- if (m.id === milestoneId) {
- return {
- ...m,
- tasks: [...(m.tasks || []), createdTask]
- };
- }
- return m;
- });
- });
- return newState;
- });
+ // Re-fetch so the timeline shows the new task
+ await fetchMilestones();
setNewTask({ title: '', description: '', due_date: '' });
setShowTaskForm(null);
} catch (err) {
console.error('Error adding task:', err);
}
- };
+ }
// ------------------------------------------------------------------
- // 7) "Copy" wizard -> after copying => re-fetch or local update
+ // 7) Copy Wizard => now with brute force refresh
// ------------------------------------------------------------------
function CopyMilestoneWizard({ milestone, scenarios, onClose, authFetch }) {
const [selectedScenarios, setSelectedScenarios] = useState([]);
@@ -376,13 +341,9 @@ const MilestoneTimeline = ({
if (!milestone) return null;
function toggleScenario(scenarioId) {
- setSelectedScenarios(prev => {
- if (prev.includes(scenarioId)) {
- return prev.filter(id => id !== scenarioId);
- } else {
- return [...prev, scenarioId];
- }
- });
+ setSelectedScenarios((prev) =>
+ prev.includes(scenarioId) ? prev.filter((id) => id !== scenarioId) : [...prev, scenarioId]
+ );
}
async function handleCopy() {
@@ -397,16 +358,10 @@ const MilestoneTimeline = ({
});
if (!res.ok) throw new Error('Failed to copy milestone');
- const data = await res.json();
- console.log('Copied milestone to new scenarios:', data);
+ // Brute force page refresh
+ window.location.reload();
- onClose(); // close wizard
-
- // re-fetch or update local
- await fetchMilestones();
- if (onMilestoneUpdated) {
- onMilestoneUpdated();
- }
+ onClose();
} catch (err) {
console.error('Error copying milestone:', err);
}
@@ -418,7 +373,7 @@ const MilestoneTimeline = ({
Copy Milestone to Other Scenarios
Milestone: {milestone.title}
- {scenarios.map(s => (
+ {scenarios.map((s) => (