File size: 3,865 Bytes
9b79a79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { AlertTriangle, Clipboard, ListOrdered, RefreshCw } from 'lucide-react'
import { useToast } from '../store/toast'

function likelyCause(message: string): string {
  const m = message.toLowerCase()
  if (m.includes('powerpoint') || m.includes('ppt')) return 'PowerPoint may be closed, busy, blocked by a dialog, or unavailable on this machine.'
  if (m.includes('moviepy') || m.includes('ffmpeg')) return 'The video engine is missing or ffmpeg is not available.'
  if (m.includes('api') || m.includes('key') || m.includes('model')) return 'The AI provider configuration may be missing or rejected.'
  if (m.includes('youtube') || m.includes('sign in') || m.includes('cookies')) return 'YouTube may require cookies or the video/timestamps may be unavailable.'
  if (m.includes('permission') || m.includes('access') || m.includes('write')) return 'The backend may not have permission to write into the output folder.'
  return 'A backend step failed while processing this run.'
}

function nextAction(message: string): string {
  const m = message.toLowerCase()
  if (m.includes('powerpoint') || m.includes('ppt')) return 'Close PowerPoint windows, dismiss any dialogs, then retry the export.'
  if (m.includes('moviepy') || m.includes('ffmpeg')) return 'Install MoviePy/ffmpeg or switch to screenshots/PPTX where available.'
  if (m.includes('api') || m.includes('key') || m.includes('model')) return 'Open Settings, verify the API key, then run System Check again.'
  if (m.includes('youtube') || m.includes('sign in') || m.includes('cookies')) return 'Export YouTube cookies to backend/config/cookies.txt, then retry.'
  if (m.includes('permission') || m.includes('access') || m.includes('write')) return 'Check output folder permissions and disk space, then retry.'
  return 'Open logs for technical details, then retry when the dependency is fixed.'
}

export default function RunErrorPanel({
  title = 'Run failed',
  message,
  onRetry,
  onOpenLogs,
}: {
  title?: string
  message: string
  onRetry?: () => void
  onOpenLogs?: () => void
}) {
  const toast = useToast()
  const copy = async () => {
    try {
      await navigator.clipboard.writeText(`${title}\n\n${message}`)
      toast.push({ variant: 'success', message: 'Error report copied.' })
    } catch {
      toast.push({ variant: 'error', message: 'Could not copy error report.' })
    }
  }

  return (
    <div className="rounded-md border border-rose-200 bg-rose-50 p-4 text-sm text-rose-900 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-100">
      <div className="flex items-start gap-3">
        <AlertTriangle size={18} className="mt-0.5 shrink-0" />
        <div className="min-w-0 flex-1 space-y-2">
          <div className="font-semibold">{title}</div>
          <div>
            <span className="font-medium">What failed: </span>
            {message || 'The backend did not return a detailed error.'}
          </div>
          <div>
            <span className="font-medium">Likely reason: </span>
            {likelyCause(message)}
          </div>
          <div>
            <span className="font-medium">What to do: </span>
            {nextAction(message)}
          </div>
          <div className="flex flex-wrap gap-2 pt-1">
            {onRetry && (
              <button type="button" className="btn-secondary btn-sm" onClick={onRetry}>
                <RefreshCw size={12} /> Retry
              </button>
            )}
            <button type="button" className="btn-secondary btn-sm" onClick={copy}>
              <Clipboard size={12} /> Copy report
            </button>
            {onOpenLogs && (
              <button type="button" className="btn-secondary btn-sm" onClick={onOpenLogs}>
                <ListOrdered size={12} /> Open logs
              </button>
            )}
          </div>
        </div>
      </div>
    </div>
  )
}