@@ -76,6 +76,7 @@ interface Flight {
7676 current_phase ?: string | null ;
7777 last_update ?: string | null ;
7878 landing_detected ?: boolean ;
79+ stationary_notification_sent ?: boolean ;
7980 telemetry_count ?: number ;
8081}
8182
@@ -108,6 +109,10 @@ export default function FlightDetail() {
108109 const [ debugData , setDebugData ] = useState < { type : string ; data : unknown } | null > ( null ) ;
109110 const [ debugLoading , setDebugLoading ] = useState ( false ) ;
110111
112+ // Completion popup state
113+ const [ showCompletionPopup , setShowCompletionPopup ] = useState ( false ) ;
114+ const [ completingFlight , setCompletingFlight ] = useState ( false ) ;
115+
111116 const fetchFlightDetails = useCallback ( async ( ) => {
112117 try {
113118 const res = await fetch (
@@ -197,6 +202,13 @@ export default function FlightDetail() {
197202 return ( ) => clearInterval ( interval ) ;
198203 } , [ flight ?. is_active , flight ?. duration_minutes , lastFetchTime ] ) ;
199204
205+ // Show completion popup when flight is stationary
206+ useEffect ( ( ) => {
207+ if ( flight ?. stationary_notification_sent && ! showCompletionPopup && ! completingFlight ) {
208+ setShowCompletionPopup ( true ) ;
209+ }
210+ } , [ flight ?. stationary_notification_sent , showCompletionPopup , completingFlight ] ) ;
211+
200212 const handleShare = async ( ) => {
201213 setShareLoading ( true ) ;
202214 try {
@@ -226,6 +238,33 @@ export default function FlightDetail() {
226238 }
227239 } ;
228240
241+ const handleCompleteFlight = async ( ) => {
242+ setCompletingFlight ( true ) ;
243+ try {
244+ const res = await fetch (
245+ `${ import . meta. env . VITE_SERVER_URL } /api/logbook/flights/${ flightId } /complete` ,
246+ {
247+ method : 'POST' ,
248+ credentials : 'include'
249+ }
250+ ) ;
251+
252+ if ( res . ok ) {
253+ setShowCompletionPopup ( false ) ;
254+ fetchFlightDetails ( ) ;
255+ navigate ( '/logbook' ) ;
256+ } else {
257+ const data = await res . json ( ) ;
258+ alert ( data . error || 'Failed to complete flight' ) ;
259+ }
260+ } catch ( err ) {
261+ console . error ( 'Failed to complete flight:' , err ) ;
262+ alert ( 'Failed to complete flight' ) ;
263+ } finally {
264+ setCompletingFlight ( false ) ;
265+ }
266+ } ;
267+
229268 const formatDuration = ( minutes : number | null , isLive : boolean = false ) => {
230269 if ( minutes === null || minutes === undefined ) return 'N/A' ;
231270 if ( minutes < 1 && ! isLive ) return 'N/A' ;
@@ -645,6 +684,59 @@ export default function FlightDetail() {
645684
646685 < div className = "container mx-auto max-w-6xl px-4 py-8" >
647686
687+ { /* Completion Popup Modal */ }
688+ { showCompletionPopup && (
689+ < div className = "fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 px-4" >
690+ < div className = "bg-gradient-to-br from-gray-900 to-gray-800 border-2 border-green-600/50 rounded-2xl p-8 max-w-md w-full shadow-2xl shadow-green-500/20" >
691+ < div className = "flex items-center gap-4 mb-6" >
692+ < div className = "p-3 bg-green-500/20 rounded-xl" >
693+ < Check className = "h-8 w-8 text-green-400" />
694+ </ div >
695+ < div >
696+ < h2 className = "text-2xl font-bold text-white" > Flight Ready to Complete</ h2 >
697+ < p className = "text-gray-400 text-sm mt-1" > You've arrived at the gate</ p >
698+ </ div >
699+ </ div >
700+
701+ < p className = "text-gray-300 mb-6" >
702+ Your flight < span className = "font-bold text-blue-300" > { flight ?. callsign } </ span > has been stationary for over 1 minute.
703+ Would you like to complete your flight now?
704+ </ p >
705+
706+ < div className = "flex gap-3" >
707+ < button
708+ onClick = { ( ) => setShowCompletionPopup ( false ) }
709+ disabled = { completingFlight }
710+ className = "flex-1 px-4 py-3 bg-gray-700 hover:bg-gray-600 text-white rounded-lg font-semibold transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
711+ >
712+ Not Yet
713+ </ button >
714+ < button
715+ onClick = { handleCompleteFlight }
716+ disabled = { completingFlight }
717+ className = "flex-1 px-4 py-3 bg-green-600 hover:bg-green-500 text-white rounded-lg font-semibold transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
718+ >
719+ { completingFlight ? (
720+ < >
721+ < RefreshCw className = "h-4 w-4 animate-spin" />
722+ Completing...
723+ </ >
724+ ) : (
725+ < >
726+ < Check className = "h-4 w-4" />
727+ Complete Flight
728+ </ >
729+ ) }
730+ </ button >
731+ </ div >
732+
733+ < p className = "text-xs text-gray-500 mt-4 text-center" >
734+ Your flight will automatically complete if you leave the game
735+ </ p >
736+ </ div >
737+ </ div >
738+ ) }
739+
648740 { /* Real-Time Status - Only for active flights */ }
649741 { isActive && (
650742 < div className = "bg-gradient-to-br from-green-900/20 via-blue-900/20 to-purple-900/20 backdrop-blur-sm rounded-xl border-2 border-green-700/40 p-6 mb-6 shadow-lg shadow-green-500/5" >
0 commit comments