'use client'; import type { CompletionResult } from '@/features/focus-session'; import { copy } from '@/shared/i18n'; import { cn } from '@/shared/lib/cn'; import { useEffect, useState } from 'react'; type EndSessionStage = 'decision' | 'unfinished-confirm'; interface EndSessionConfirmModalProps { open: boolean; currentGoal: string; completionResult?: CompletionResult | null; onClose: () => void; onFinishHere: () => Promise | CompletionResult | null; onSaveAndReturn: () => Promise | CompletionResult | null; onBackToLobby: () => void; } const formatFocusedMinutes = (focusedSeconds: number) => { const safeSeconds = Math.max(0, focusedSeconds); return Math.max(1, Math.round(safeSeconds / 60)); }; export const EndSessionConfirmModal = ({ open, currentGoal, completionResult = null, onClose, onFinishHere, onSaveAndReturn, onBackToLobby, }: EndSessionConfirmModalProps) => { const [stage, setStage] = useState('decision'); const [isSubmitting, setIsSubmitting] = useState(false); const endSessionCopy = copy.space.endSession; const trimmedGoal = currentGoal.trim() || copy.space.focusHud.goalFallback; const activeStage = completionResult ? completionResult.completionSource === 'manual-end' ? 'result-saved' : 'result-success' : stage; const focusedMinutes = completionResult ? formatFocusedMinutes(completionResult.focusedSeconds) : 0; const hasThoughts = Boolean(completionResult && completionResult.thoughts.length > 0); useEffect(() => { if (!open) { // Add a slight delay before resetting state so the fade-out animation can finish smoothly const timer = setTimeout(() => { setStage('decision'); setIsSubmitting(false); }, 700); return () => clearTimeout(timer); } const allowEscape = !isSubmitting && !completionResult; const handleEscape = (event: KeyboardEvent) => { if (event.key === 'Escape' && allowEscape) { onClose(); } }; window.addEventListener('keydown', handleEscape); return () => { window.removeEventListener('keydown', handleEscape); }; }, [completionResult, isSubmitting, onClose, open]); const handleFinishHere = async () => { if (isSubmitting) return; setIsSubmitting(true); try { await onFinishHere(); } finally { setIsSubmitting(false); } }; const handleSaveAndReturn = async () => { if (isSubmitting) return; setIsSubmitting(true); try { await onSaveAndReturn(); } finally { setIsSubmitting(false); } }; return (
{/* Abyssal Backdrop: Direct filter animation to prevent WebKit blur pop-in */}
{ if (activeStage === 'decision' || activeStage === 'unfinished-confirm') { onClose(); } }} className={cn( 'absolute inset-0 transition-all duration-1000 ease-[cubic-bezier(0.16,1,0.3,1)] transform-gpu', open ? 'bg-black/80 backdrop-blur-[40px] pointer-events-auto' : 'bg-transparent backdrop-blur-none pointer-events-none', )} >
{activeStage === 'decision' ? (

{endSessionCopy.decision.eyebrow}

{endSessionCopy.decision.title}

{/* Volumetric Card */}

{endSessionCopy.decision.goalLabel}

{trimmedGoal}

{/* Primary CTA (The Pearl) */} {/* Secondary CTA (Ghost Button) */}
) : null} {activeStage === 'unfinished-confirm' ? (

{endSessionCopy.unfinishedConfirm.eyebrow}

{endSessionCopy.unfinishedConfirm.title}

{endSessionCopy.unfinishedConfirm.description}

{endSessionCopy.decision.goalLabel}

{trimmedGoal}

) : null} {(activeStage === 'result-success' || activeStage === 'result-saved') && completionResult ? (
{/* Stage dependent subtle glow */}

{activeStage === 'result-success' ? endSessionCopy.resultSuccess.eyebrow : endSessionCopy.resultSaved.eyebrow}

{activeStage === 'result-success' ? endSessionCopy.resultSuccess.title : endSessionCopy.resultSaved.title}

{activeStage === 'result-success' ? endSessionCopy.resultSuccess.description : endSessionCopy.resultSaved.description}

{/* The Hero Number */}

{endSessionCopy.resultSuccess.focusedLabel}

{focusedMinutes} m

{activeStage === 'result-saved' && (

{endSessionCopy.resultSaved.goalStatusLabel}

{endSessionCopy.resultSaved.goalStatusValue}

)}

{endSessionCopy.resultSuccess.goalLabel}

{completionResult.goalText}

{hasThoughts ? (

{endSessionCopy.resultSuccess.thoughtsLabel}

{completionResult.thoughts.length}
{completionResult.thoughts.map((thought) => (

{thought.text}

))}
) : null}
) : null}
); };