promptenhancer / src /components /ComparisonView.tsx
Tobs248's picture
# Rolle
eee3ce2 verified
Raw
History Blame Contribute Delete
8.08 kB
tsx
import React, { useCallback } from 'react'
import { useAppStore } from '@/store/useAppStore'
import { Copy, ArrowRight, CheckCircle } from 'lucide-react'
/**
* Component for side-by-side comparison of original and optimized prompts
*/
export function ComparisonView() {
const {
result,
copiedStates,
setCopiedState
} = useAppStore()
if (!result) return null
/**
* Handle copy to clipboard with feedback
*/
const handleCopy = useCallback(async (text: string, type: 'original' | 'optimized') => {
try {
await navigator.clipboard.writeText(text)
setCopiedState(type, true)
setTimeout(() => {
setCopiedState(type, false)
}, 2000)
} catch (error) {
console.error('Failed to copy text:', error)
setCopiedState(type, true)
setTimeout(() => {
setCopiedState(type, false)
}, 2000)
}
}, [setCopiedState])
/**
* Calculate differences
*/
const originalWords = result.originalPrompt.split(/\s+/).length
const optimizedWords = result.optimizedPrompt.split(/\s+/).length
const wordsIncrease = optimizedWords - originalWords
const wordsIncreasePercent = Math.round((wordsIncrease / originalWords) * 100)
return (
<div className="space-y-6">
{/* Header */}
<div className="card p-6">
<div className="flex items-center justify-between">
<h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100">
Prompt-Vergleich
</h3>
<div className="flex items-center space-x-4 text-sm text-gray-600 dark:text-gray-400">
<span>+{wordsIncrease} Wörter (+{wordsIncreasePercent}%)</span>
<div className="flex items-center space-x-1 text-green-600 dark:text-green-400">
<CheckCircle className="w-4 h-4" />
<span>Optimiert für {result.targetModel.replace('-', ' ')}</span>
</div>
</div>
</div>
</div>
{/* Side-by-side comparison */}
<div className="grid lg:grid-cols-2 gap-6">
{/* Original Prompt */}
<div className="card p-6">
<div className="flex items-center justify-between mb-4">
<div>
<h4 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
Original
</h4>
<p className="text-sm text-gray-600 dark:text-gray-400">
{result.originalPrompt.length} Zeichen, {originalWords} Wörter
</p>
</div>
<button
onClick={() => handleCopy(result.originalPrompt, 'original')}
className={`flex items-center space-x-2 px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${
copiedStates.original
? 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300'
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
}`}
>
{copiedStates.original ? (
<>
<CheckCircle className="w-4 h-4" />
<span>Kopiert!</span>
</>
) : (
<>
<Copy className="w-4 h-4" />
<span>Kopieren</span>
</>
)}
</button>
</div>
<div className="relative">
<textarea
value={result.originalPrompt}
readOnly
className="w-full h-80 p-4 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg resize-none text-sm leading-relaxed"
placeholder="Original Prompt..."
/>
<div className="absolute bottom-2 right-2 text-xs text-gray-500 dark:text-gray-400">
V1
</div>
</div>
</div>
{/* Optimization Arrow */}
<div className="hidden lg:flex items-center justify-center">
<div className="flex items-center space-x-4 text-primary-600 dark:text-primary-400">
<ArrowRight className="w-8 h-8" />
<div className="text-center">
<div className="text-sm font-medium">Optimiert</div>
<div className="text-xs text-gray-500 dark:text-gray-400">
+{result.executionTime}ms
</div>
</div>
</div>
</div>
{/* Optimized Prompt */}
<div className="card p-6">
<div className="flex items-center justify-between mb-4">
<div>
<h4 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
Optimiert
</h4>
<p className="text-sm text-gray-600 dark:text-gray-400">
{result.optimizedPrompt.length} Zeichen, {optimizedWords} Wörter
</p>
</div>
<button
onClick={() => handleCopy(result.optimizedPrompt, 'optimized')}
className={`flex items-center space-x-2 px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${
copiedStates.optimized
? 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300'
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
}`}
>
{copiedStates.optimized ? (
<>
<CheckCircle className="w-4 h-4" />
<span>Kopiert!</span>
</>
) : (
<>
<Copy className="w-4 h-4" />
<span>Kopieren</span>
</>
)}
</button>
</div>
<div className="relative">
<textarea
value={result.optimizedPrompt}
readOnly
className="w-full h-80 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-700 rounded-lg resize-none text-sm leading-relaxed"
placeholder="Optimierter Prompt..."
/>
<div className="absolute bottom-2 right-2 text-xs text-green-600 dark:text-green-400 font-medium">
V2 - Optimiert
</div>
</div>
</div>
</div>
{/* Mobile optimization arrow */}
<div className="lg:hidden flex items-center justify-center py-4">
<div className="flex items-center space-x-2 text-primary-600 dark:text-primary-400">
<ArrowRight className="w-6 h-6 rotate-90" />
<span className="text-sm font-medium">Optimiert</span>
</div>
</div>
{/* Improvements List */}
<div className="card p-6">
<h4 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
Angewendete Verbesserungen
</h4>
<div className="grid sm:grid-cols-2 gap-4">
{result.improvements.map((improvement, index) => (
<div key={index} className="flex items-start space-x-3 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg">
<CheckCircle className="w-5 h-5 text-green-500 flex-shrink-0 mt-0.5" />
<span className="text-sm text-gray-700 dark:text-gray-300">{improvement}</span>
</div>
))}
</div>
</div>
{/* Final Action Button */}
<div className="text-center">
<button
onClick={() => handleCopy(result.optimizedPrompt, 'optimized')}
className="btn-primary px-8 py-3 text-lg flex items-center space-x-2 mx-auto"
>
{copiedStates.optimized ? (
<>
<CheckCircle className="w-5 h-5" />
<span>Kopiert!</span>
</>
) : (
<>
<Copy className="w-5 h-5" />
<span>Optimierten Prompt verwenden</span>
</>
)}
</button>
</div>
</div>
)
}
</html>