129 lines
3.5 KiB
TypeScript
129 lines
3.5 KiB
TypeScript
'use client';
|
||
|
||
import { useState } from 'react';
|
||
import BlurOverlay from './BlurOverlay';
|
||
import EditableOutput from './EditableOutput';
|
||
import ExportMenu from './ExportMenu';
|
||
|
||
type State = 'idle' | 'generating' | 'preview' | 'unlocked' | 'done';
|
||
|
||
interface OutputPanelProps {
|
||
tool: string;
|
||
sessionId: string | null;
|
||
preview: string | null;
|
||
fullOutput: string | null;
|
||
isPaid: boolean;
|
||
generating?: boolean;
|
||
onPaymentRequest: () => void;
|
||
paymentLoading?: boolean;
|
||
}
|
||
|
||
export default function OutputPanel({
|
||
tool,
|
||
sessionId,
|
||
preview,
|
||
fullOutput,
|
||
isPaid,
|
||
generating,
|
||
onPaymentRequest,
|
||
paymentLoading,
|
||
}: OutputPanelProps) {
|
||
const [output, setOutput] = useState(fullOutput);
|
||
const [finalizing, setFinalizing] = useState(false);
|
||
|
||
const state: State = generating
|
||
? 'generating'
|
||
: !sessionId
|
||
? 'idle'
|
||
: !preview
|
||
? 'idle'
|
||
: !isPaid
|
||
? 'preview'
|
||
: finalizing
|
||
? 'done'
|
||
: 'unlocked';
|
||
|
||
const displayOutput = output || fullOutput;
|
||
|
||
const handleFinalize = async () => {
|
||
setFinalizing(true);
|
||
try {
|
||
const res = await fetch(`/api/${tool}/finalize`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ session_id: sessionId }),
|
||
});
|
||
const data = await res.json();
|
||
if (data.finalized) setOutput(data.finalized);
|
||
} catch (e) {
|
||
console.error('Finalize failed:', e);
|
||
}
|
||
};
|
||
|
||
if (state === 'idle') {
|
||
return (
|
||
<div className="h-full flex items-center justify-center text-gray-400">
|
||
<div className="text-center">
|
||
<div className="text-5xl mb-4">📝</div>
|
||
<p>Fill in the form and click Generate to get started</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (state === 'generating') {
|
||
return (
|
||
<div className="h-full flex items-center justify-center">
|
||
<div className="text-center">
|
||
<div className="animate-spin text-4xl mb-4">⚙️</div>
|
||
<p className="text-gray-600 font-medium">Generating your output...</p>
|
||
<p className="text-gray-400 text-sm mt-1">This may take 15-30 seconds</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="relative">
|
||
{state === 'preview' && (
|
||
<>
|
||
<div className="prose prose-sm max-w-none bg-gray-50 p-6 rounded-lg border whitespace-pre-wrap font-mono text-sm">
|
||
{preview}
|
||
<span className="text-gray-300">{'...'.repeat(20)}</span>
|
||
</div>
|
||
<BlurOverlay onUnlock={onPaymentRequest} loading={paymentLoading} />
|
||
</>
|
||
)}
|
||
|
||
{(state === 'unlocked' || state === 'done') && sessionId && displayOutput && (
|
||
<div className="space-y-4">
|
||
<div className="flex items-center justify-between">
|
||
<h3 className="font-semibold text-dark">Your Output</h3>
|
||
<div className="flex gap-2">
|
||
{!finalizing && (
|
||
<button
|
||
onClick={handleFinalize}
|
||
className="px-4 py-2 bg-accent text-white rounded-lg text-sm hover:bg-accent/90"
|
||
>
|
||
🎯 Finalize
|
||
</button>
|
||
)}
|
||
<ExportMenu sessionId={sessionId} />
|
||
</div>
|
||
</div>
|
||
|
||
<EditableOutput
|
||
content={displayOutput}
|
||
sessionId={sessionId}
|
||
tool={tool}
|
||
isPaid={true}
|
||
improvementsUsed={0}
|
||
maxImprovements={5}
|
||
onImproved={setOutput}
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|