Initial commit
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
'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;
|
||||
onPaymentRequest: () => void;
|
||||
paymentLoading?: boolean;
|
||||
}
|
||||
|
||||
export default function OutputPanel({
|
||||
tool,
|
||||
sessionId,
|
||||
preview,
|
||||
fullOutput,
|
||||
isPaid,
|
||||
onPaymentRequest,
|
||||
paymentLoading,
|
||||
}: OutputPanelProps) {
|
||||
const [output, setOutput] = useState(fullOutput);
|
||||
const [finalizing, setFinalizing] = useState(false);
|
||||
|
||||
const state: State = !sessionId
|
||||
? 'idle'
|
||||
: !preview
|
||||
? 'generating'
|
||||
: !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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user