"use client"; import React, { useState, useEffect } from "react"; import { X, Wand2, Image as ImageIcon, CheckCircle2, AlertCircle, Loader2, Sparkles, Download } from "lucide-react"; import { correctImage } from "@/lib/api/endpoints"; import type { ImageCorrectResponse, AdCreativeDB } from "@/types/api"; import { getImageUrlFallback } from "@/lib/utils/formatters"; import { ProgressBar } from "@/components/ui/ProgressBar"; import { Button } from "@/components/ui/Button"; import { Input } from "@/components/ui/Input"; import { Card, CardContent } from "@/components/ui/Card"; interface CorrectionModalProps { isOpen: boolean; onClose: () => void; adId: string; imageUrl?: string | null; initialInstructions?: string; ad?: AdCreativeDB | null; onSuccess?: (result: ImageCorrectResponse, keepCorrected: boolean) => void; } type CorrectionStep = "idle" | "input" | "analyzing" | "correcting" | "regenerating" | "complete" | "error"; export const CorrectionModal: React.FC = ({ isOpen, onClose, adId, imageUrl, initialInstructions = "", ad, onSuccess, }) => { const [step, setStep] = useState("idle"); const [progress, setProgress] = useState(0); const [result, setResult] = useState(null); const [error, setError] = useState(null); const [userInstructions, setUserInstructions] = useState(""); const [useAutoAnalyze, setUseAutoAnalyze] = useState(false); const [keepCorrected, setKeepCorrected] = useState(true); const [correctedImgUseFallback, setCorrectedImgUseFallback] = useState(false); useEffect(() => { if (result) setCorrectedImgUseFallback(false); }, [result]); useEffect(() => { if (isOpen) { setStep("input"); setProgress(0); setResult(null); setError(null); setUserInstructions(initialInstructions); setUseAutoAnalyze(false); } else { // Reset state when modal closes setStep("idle"); setProgress(0); setResult(null); setError(null); setUserInstructions(""); setUseAutoAnalyze(false); setKeepCorrected(true); } }, [isOpen]); const handleCorrection = async () => { if (!userInstructions && !useAutoAnalyze) { setError("Please specify what you want to correct or enable auto-analysis"); return; } setStep("analyzing"); setProgress(0); setError(null); setResult(null); try { // Simulate progress updates const progressInterval = setInterval(() => { setProgress((prev) => { if (prev < 90) { return prev + 5; } return prev; }); }, 500); setStep("analyzing"); await new Promise((resolve) => setTimeout(resolve, 1000)); setStep("correcting"); await new Promise((resolve) => setTimeout(resolve, 1000)); setStep("regenerating"); await new Promise((resolve) => setTimeout(resolve, 2000)); // Actually perform the correction const response = await correctImage({ image_id: adId, image_url: imageUrl || undefined, user_instructions: userInstructions || undefined, auto_analyze: useAutoAnalyze, }); clearInterval(progressInterval); setProgress(100); if (response.status === "success") { setStep("complete"); setResult(response); // Don't call onSuccess immediately - let user see the corrections first // onSuccess will be called when user clicks "Done" } else { setStep("error"); setError(response.error || "Correction failed"); } } catch (err: any) { setStep("error"); setError(err.response?.data?.detail || err.message || "Failed to correct image"); setProgress(0); } }; const getStepLabel = () => { switch (step) { case "input": return "Specify Corrections"; case "analyzing": return "Analyzing image..."; case "correcting": return "Generating corrections..."; case "regenerating": return "Regenerating with image..."; case "complete": return "Correction complete!"; case "error": return "Error occurred"; default: return "Starting correction..."; } }; const getStepIcon = () => { switch (step) { case "complete": return ; case "error": return ; default: return ; } }; if (!isOpen) return null; return (
{/* Header */}

Correct Image

{step === "input" ? "Specify what you want to correct" : "Analyzing and correcting your ad creative"}

{/* Content */}
{/* Input Step */} {step === "input" && (
setUserInstructions(e.target.value)} className="w-full" />

Be specific about what you want to change. Only the specified changes will be made.

setUseAutoAnalyze(e.target.checked)} className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" />
{useAutoAnalyze && (

Auto-Analysis Mode

AI will analyze the image for spelling mistakes and visual issues, then suggest corrections.

)}
)} {/* Progress Section */} {step !== "input" && step !== "complete" && step !== "error" && (
{getStepIcon()}

{getStepLabel()}

)} {/* Error State */} {step === "error" && (

Correction Failed

{error}

)} {/* Success State */} {step === "complete" && result && (

Correction Complete!

Your image has been corrected successfully

{/* Original Image */}
{!keepCorrected &&
}

Original

setKeepCorrected(false)}> {(imageUrl || ad?.r2_url || ad?.image_url) ? ( Original ) : (
No image
)}
{/* Corrected Image */}
{keepCorrected &&
}

Corrected

setKeepCorrected(true)}> {result.corrected_image && (result.corrected_image.image_url || result.corrected_image.filename) && (() => { const { primary, fallback } = getImageUrlFallback(result.corrected_image?.image_url, result.corrected_image?.filename, result.corrected_image?.r2_url); const src = (correctedImgUseFallback && fallback ? fallback : primary ?? fallback) ?? ""; return src ? ( Corrected { if (fallback) setCorrectedImgUseFallback(true); }} /> ) : null; })()}
NEW
{/* Show corrections details */} {result.corrections && (

Correction Details

{result.corrections.spelling_corrections && result.corrections.spelling_corrections.length > 0 && (

✏️ Spelling Corrections

{result.corrections.spelling_corrections.map((correction, idx) => (
{correction.detected} {correction.corrected}
{correction.context && (

{correction.context}

)}
))}
)} {result.corrections.visual_corrections && result.corrections.visual_corrections.length > 0 && (

🎨 Visual Improvements

{result.corrections.visual_corrections.map((correction, idx) => (

{correction.issue}

{correction.suggestion}

{correction.priority && ( {correction.priority} )}
))}
)} {result.corrections.corrected_prompt && (

Corrected Prompt

{result.corrections.corrected_prompt}

)} {result.analysis && (

Detailed AI Analysis

{result.analysis}
)} {/* Show message if no corrections were made */} {(!result.corrections.spelling_corrections || result.corrections.spelling_corrections.length === 0) && (!result.corrections.visual_corrections || result.corrections.visual_corrections.length === 0) && (

No specific corrections were identified. The image was regenerated based on your instructions.

)}
)}
)}
{/* Footer */}
{step === "input" && ( )} {step === "error" && ( )}
); };