'use client'; import { useEffect, useState, useRef, Suspense } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; interface Scenario { name: string; desc: string; icon: string; class: 'blue' | 'green'; stepId: string; } interface UploadedFile { key: string; name: string; size: number; preview: string; } function PromptContent() { const router = useRouter(); const searchParams = useSearchParams(); const scenarioId = searchParams.get('scenario') || 'nano-banana'; const scenarios: Record = { 'nano-banana': { name: 'Nano Banana', desc: 'Генерация изображений по промпту', icon: '🎨', class: 'blue', stepId: '1', }, 'demo-scenario': { name: 'Demo Scenario', desc: 'Тестовый сценарий с подтверждением', icon: '🧪', class: 'green', stepId: 'collect-input', }, }; const scenario = scenarios[scenarioId] || scenarios['nano-banana']; const [prompt, setPrompt] = useState(''); const [selectedFile, setSelectedFile] = useState(null); const [preview, setPreview] = useState(null); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const [status, setStatus] = useState(''); const [csrfToken, setCsrfToken] = useState(''); const fileInputRef = useRef(null); const fileUploadRef = useRef(null); useEffect(() => { loadCsrfToken(); setupDragDrop(); }, []); async function loadCsrfToken() { try { const response = await fetch('/api/auth/csrf', { credentials: 'same-origin' }); const data = await response.json(); setCsrfToken(data.csrfToken || ''); } catch (err) { console.error('Failed to load CSRF token:', err); } } function setupDragDrop() { const el = fileUploadRef.current; if (!el) return; ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { el.addEventListener(eventName, preventDefaults, false); }); ['dragenter', 'dragover'].forEach(eventName => { el.addEventListener(eventName, () => el.classList.add('dragover'), false); }); ['dragleave', 'drop'].forEach(eventName => { el.addEventListener(eventName, () => el.classList.remove('dragover'), false); }); el.addEventListener('drop', handleDrop, false); } function preventDefaults(e: Event) { e.preventDefault(); e.stopPropagation(); } function handleDrop(e: DragEvent) { const dt = e.dataTransfer; if (dt?.files.length) { handleFileSelect(dt.files[0]); } } function handleFileChange(e: React.ChangeEvent) { if (e.target.files?.length) { handleFileSelect(e.target.files[0]); } } function handleFileSelect(file: File) { const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp']; if (!allowedTypes.includes(file.type)) { setError('Недопустимый тип файла. Разрешены: JPEG, PNG, GIF, WebP'); return; } const maxSize = 10 * 1024 * 1024; if (file.size > maxSize) { setError('Файл слишком большой. Максимум: 10MB'); return; } setSelectedFile(file); const reader = new FileReader(); reader.onload = (e) => setPreview(e.target?.result as string); reader.readAsDataURL(file); setError(''); } function removeFile() { setSelectedFile(null); setPreview(null); if (fileInputRef.current) fileInputRef.current.value = ''; } function formatFileSize(bytes: number): string { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; } async function uploadFileToS3(file: File, generationUuid: string, generationStepId: string): Promise { const formData = new FormData(); formData.append('file', file); formData.append('generationUuid', generationUuid); formData.append('generationStepId', generationStepId); const response = await fetch('/api/upload/image', { method: 'POST', headers: { 'x-csrf-token': csrfToken }, body: formData, credentials: 'same-origin', }); const data = await response.json(); if (!response.ok) throw new Error(data.message || 'Ошибка загрузки файла'); return data.data.s3Key; } async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setLoading(true); setError(''); setStatus('Запуск сценария...'); try { // Шаг 1: Запуск сценария const startResponse = await fetch(`/api/scenario/${scenarioId}/start`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-csrf-token': csrfToken, }, credentials: 'same-origin', body: JSON.stringify({}), }); const startData = await startResponse.json(); if (!startResponse.ok) throw new Error(startData.message || 'Ошибка запуска сценария'); const generationUuid = startData.generationUuid; // Шаг 2: Загрузка файла (если есть) let uploadedFileKey: string | undefined; if (selectedFile) { setStatus('Загрузка файла...'); const stepRecordResponse = await fetch(`/api/scenario/${scenarioId}/step/${scenario.stepId}/record`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-csrf-token': csrfToken, }, credentials: 'same-origin', body: JSON.stringify({ prompt }), }); const stepRecordData = await stepRecordResponse.json(); const generationStepId = stepRecordData.stepRecordId; uploadedFileKey = await uploadFileToS3(selectedFile, generationUuid, generationStepId); } // Шаг 3: Выполнение шага const stepPayload: Record = { prompt }; if (uploadedFileKey) stepPayload.imageKey = uploadedFileKey; setStatus('Генерация...'); const stepResponse = await fetch(`/api/scenario/${scenarioId}/step/${scenario.stepId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-csrf-token': csrfToken, }, credentials: 'same-origin', body: JSON.stringify(stepPayload), }); const stepData = await stepResponse.json(); if (!stepResponse.ok) throw new Error(stepData.message || 'Ошибка выполнения шага'); router.push(`/result?generationUuid=${generationUuid}`); } catch (err: unknown) { setError(err instanceof Error ? err.message : 'Неизвестная ошибка'); setLoading(false); setStatus(''); } } return (

Создание

{scenario.icon}
{scenario.name}
{scenario.desc}
{error &&
{error}
}