Files
ngo-toolkit/components/EditableOutput.tsx
T
2026-03-18 18:26:24 +08:00

121 lines
3.6 KiB
TypeScript

'use client';
import { useState } from 'react';
interface EditableOutputProps {
content: string;
sessionId: string;
tool: string;
isPaid: boolean;
improvementsUsed: number;
maxImprovements: number;
onImproved: (newContent: string) => void;
}
export default function EditableOutput({
content,
sessionId,
tool,
isPaid,
improvementsUsed,
maxImprovements,
onImproved,
}: EditableOutputProps) {
const [feedback, setFeedback] = useState('');
const [loading, setLoading] = useState(false);
const [validating, setValidating] = useState(false);
const [validation, setValidation] = useState<string | null>(null);
const handleImprove = async () => {
if (!feedback.trim()) return;
setLoading(true);
try {
const res = await fetch(`/api/${tool}/improve-section`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_id: sessionId, section: content, feedback }),
});
const data = await res.json();
if (data.improved) {
onImproved(data.improved);
setFeedback('');
}
} catch (e) {
console.error('Improve failed:', e);
} finally {
setLoading(false);
}
};
const handleValidate = async () => {
setValidating(true);
try {
const res = await fetch(`/api/${tool}/validate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_id: sessionId }),
});
const data = await res.json();
if (data.validation) setValidation(data.validation);
} catch (e) {
console.error('Validate failed:', e);
} finally {
setValidating(false);
}
};
const remaining = maxImprovements - improvementsUsed;
return (
<div className="space-y-4">
<div className="prose prose-sm max-w-none bg-gray-50 p-6 rounded-lg border whitespace-pre-wrap font-mono text-sm">
{content}
</div>
{isPaid && (
<>
<div className="flex gap-2">
<button
onClick={handleValidate}
disabled={validating}
className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm hover:bg-blue-700 disabled:opacity-50"
>
{validating ? 'Analysing...' : '🔍 Analyse Quality'}
</button>
<span className="text-xs text-gray-400 self-center">
{remaining} improvements remaining
</span>
</div>
{validation && (
<div className="bg-blue-50 border border-blue-200 p-4 rounded-lg">
<h4 className="font-semibold text-blue-800 mb-2">Quality Assessment</h4>
<div className="text-sm whitespace-pre-wrap">{validation}</div>
</div>
)}
{remaining > 0 && (
<div className="flex gap-2">
<input
type="text"
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
placeholder="Describe what to improve..."
className="flex-1 border rounded-lg px-3 py-2 text-sm"
onKeyDown={(e) => e.key === 'Enter' && handleImprove()}
/>
<button
onClick={handleImprove}
disabled={loading || !feedback.trim()}
className="px-4 py-2 bg-accent text-white rounded-lg text-sm hover:bg-accent/90 disabled:opacity-50"
>
{loading ? 'Improving...' : '✨ Improve'}
</button>
</div>
)}
</>
)}
</div>
);
}