diff --git a/src/pages/owner/OwnerProjectDetail.jsx b/src/pages/owner/OwnerProjectDetail.jsx
index d406792..e50a2e5 100644
--- a/src/pages/owner/OwnerProjectDetail.jsx
+++ b/src/pages/owner/OwnerProjectDetail.jsx
@@ -2,6 +2,7 @@ import React, { useState, useMemo, useRef, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useAuth } from '../../context/AuthContext';
import { useMockStore } from '../../data/mockStore';
+import { LIFECYCLE_STAGES, REWORK_STAGE, nextStage, prevStage, stageToPct } from '../../data/lifecycle';
import { SpotlightCard } from '../../components/SpotlightCard';
import { AnimatedCounter } from '../../components/AnimatedCounter';
import { motion } from 'framer-motion';
@@ -190,6 +191,7 @@ const OwnerProjectDetail = () => {
updateOrgCommissionDefaults, setUserCommissionOverride,
removeUserCommissionOverride, updateProject,
addJobTeamMember, removeJobTeamMember, updateJobTeamMember,
+ advanceLifecycleStage,
} = useMockStore();
const navigate = useNavigate();
const { can } = usePermissions();
@@ -554,41 +556,164 @@ const OwnerProjectDetail = () => {
{/* Project Progression */}
-
+
Project Progression
-
- {/* Stepper Line */}
-
-
-
- {/* Stepper Nodes */}
- {["Lead", "Inspection Scheduled", "Damage Verified", "Scope Approved", "Project Completed"].map((step, i) => {
- const isActive = (i * 25) <= (project.completionPercentage || 0);
- return (
-
-
- {isActive ? : }
-
-
- {step}
-
+ {/* Current stage label */}
+ {(() => {
+ const currentStage = project.lifecycleStage || LIFECYCLE_STAGES[0];
+ const isRework = currentStage === REWORK_STAGE;
+ const currentIdx = LIFECYCLE_STAGES.indexOf(currentStage);
+ // Condensed milestone labels for stepper display
+ const STEPPER_STAGES = ['Lead', 'Damage Verified', 'Contract Signed', 'Work In Progress', 'Project Completed', 'Final Payment Received — Closed'];
+ const STEPPER_LABELS = ['Lead', 'Damage Verified', 'Contract Signed', 'Work In Progress', 'Completed', 'Closed'];
+ // Consistency rule: WIP uses stored %, all others use stageToPct
+ const displayPct = currentStage === 'Work In Progress'
+ ? (project.completionPercentage || 0)
+ : stageToPct(currentStage);
+ const isOwnerAdmin = ['OWNER', 'ADMIN'].includes(user?.role);
+ const nextStageName = nextStage(currentStage);
+ const isAtFinalStage = currentStage === 'Final Payment Received — Closed';
+ const isFinalInspectionStage = currentStage === 'Final Inspection Scheduled' || currentStage === 'Final Inspection In Progress';
+
+ return (
+ <>
+
+
+
+ {currentStage}
+
+ {displayPct}% complete
+
+
+ {/* Stepper */}
+
+
+
+
+ {STEPPER_STAGES.map((step, i) => {
+ const stepIdx = LIFECYCLE_STAGES.indexOf(step);
+ const isActive = isRework
+ ? stepIdx <= LIFECYCLE_STAGES.indexOf('Work In Progress')
+ : stepIdx <= currentIdx;
+ const isCurrent = step === currentStage;
+ return (
+
+
+ {isActive ? : }
+
+
+ {STEPPER_LABELS[i]}
+
+
+ );
+ })}
- );
- })}
-
-
-
-
-
{project.completionPercentage || 0}% CMP
-
+
+
+ {/* Rework indicator */}
+ {isRework && (
+
+
+ Rework in progress — project re-enters Final Inspection after rework is resolved
+
+ )}
+
+ {/* Progress bar */}
+
+
+ {/* Owner/Admin Advance controls */}
+ {isOwnerAdmin && (
+
+ {!isAtFinalStage && (
+
+ )}
+ {isFinalInspectionStage && (
+
+ )}
+ {isAtFinalStage && (
+
+ Project fully closed
+
+ )}
+
+ )}
+
+ {/* Stage History Timeline */}
+ {(project.stageHistory || []).length > 0 && (
+
+
Stage History
+
+
+
+ {[...(project.stageHistory)].reverse().map((entry, i) => (
+
+
+
+
{entry.stage}
+
+ {entry.date}
+ {entry.by && {entry.by}}
+
+ {entry.note && (
+
{entry.note}
+ )}
+
+
+ ))}
+
+
+
+ )}
+ >
+ );
+ })()}
{/* Relationship Intelligence */}