Spaces:
Running
Running
| tsx | |
| import React, { useCallback } from 'react' | |
| import { useAppStore } from '@/store/useAppStore' | |
| import { Copy, CheckCircle, Clock, Hash, Sparkles } from 'lucide-react' | |
| import { ANIMATION_DURATIONS } from '@/utils/constants' | |
| /** | |
| * Component displaying the optimized prompt result | |
| */ | |
| export function OptimizationResult() { | |
| const { | |
| result, | |
| setCopiedState, | |
| copiedStates | |
| } = 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) | |
| // Reset copied state after animation | |
| setTimeout(() => { | |
| setCopiedState(type, false) | |
| }, ANIMATION_DURATIONS.COPY_FEEDBACK) | |
| } catch (error) { | |
| console.error('Failed to copy text:', error) | |
| // Fallback for older browsers | |
| const textArea = document.createElement('textarea') | |
| textArea.value = text | |
| document.body.appendChild(textArea) | |
| textArea.select() | |
| document.execCommand('copy') | |
| document.body.removeChild(textArea) | |
| setCopiedState(type, true) | |
| setTimeout(() => { | |
| setCopiedState(type, false) | |
| }, ANIMATION_DURATIONS.COPY_FEEDBACK) | |
| } | |
| }, [setCopiedState]) | |
| /** | |
| * Calculate improvement metrics | |
| */ | |
| const improvementMetrics = { | |
| lengthIncrease: result.optimizedPrompt.length - result.originalPrompt.length, | |
| lengthIncreasePercent: Math.round(((result.optimizedPrompt.length - result.originalPrompt.length) / result.originalPrompt.length) * 100), | |
| wordIncrease: result.optimizedPrompt.split(/\s+/).length - result.originalPrompt.split(/\s+/).length | |
| } | |
| return ( | |
| <div className="space-y-6"> | |
| {/* Result Header */} | |
| <div className="card p-6"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <div className="flex items-center space-x-3"> | |
| <div className="w-10 h-10 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center"> | |
| <CheckCircle className="w-6 h-6 text-green-600 dark:text-green-400" /> | |
| </div> | |
| <div> | |
| <h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100"> | |
| Optimierung abgeschlossen | |
| </h3> | |
| <p className="text-sm text-gray-600 dark:text-gray-400"> | |
| Für {result.targetModel.replace('-', ' ')} optimiert in {result.executionTime}ms | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Quick Stats */} | |
| <div className="grid grid-cols-3 gap-4 mb-4"> | |
| <div className="text-center p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"> | |
| <div className="text-2xl font-bold text-primary-600 dark:text-primary-400"> | |
| +{improvementMetrics.lengthIncrease} | |
| </div> | |
| <div className="text-xs text-gray-600 dark:text-gray-400">Zeichen</div> | |
| </div> | |
| <div className="text-center p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"> | |
| <div className="text-2xl font-bold text-secondary-600 dark:text-secondary-400"> | |
| +{improvementMetrics.wordIncrease} | |
| </div> | |
| <div className="text-xs text-gray-600 dark:text-gray-400">Wörter</div> | |
| </div> | |
| <div className="text-center p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"> | |
| <div className="text-2xl font-bold text-green-600 dark:text-green-400"> | |
| {improvementMetrics.lengthIncreasePercent}% | |
| </div> | |
| <div className="text-xs text-gray-600 dark:text-gray-400">Erweiterung</div> | |
| </div> | |
| </div> | |
| {/* Performance Info */} | |
| <div className="flex items-center justify-between text-sm text-gray-600 dark:text-gray-400"> | |
| <div className="flex items-center space-x-4"> | |
| <div className="flex items-center space-x-1"> | |
| <Clock className="w-4 h-4" /> | |
| <span>{result.executionTime}ms</span> | |
| </div> | |
| {result.tokenEstimate && ( | |
| <div className="flex items-center space-x-1"> | |
| <Hash className="w-4 h-4" /> | |
| <span>~{result.tokenEstimate} Tokens</span> | |
| </div> | |
| )} | |
| </div> | |
| <div className="flex items-center space-x-1 text-green-600 dark:text-green-400"> | |
| <Sparkles className="w-4 h-4" /> | |
| <span>Optimiert</span> | |
| </div> | |
| </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> | |
| <ul className="space-y-2"> | |
| {result.improvements.map((improvement, index) => ( | |
| <li key={index} className="flex items-start space-x-2"> | |
| <CheckCircle className="w-4 h-4 text-green-500 flex-shrink-0 mt-0.5" /> | |
| <span className="text-sm text-gray-700 dark:text-gray-300">{improvement}</span> | |
| </li> | |
| ))} | |
| </ul> | |
| </div> | |
| {/* Optimized Prompt Output */} | |
| <div className="card p-6"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <h4 className="text-lg font-semibold text-gray-900 dark:text-gray-100"> | |
| Optimierter Prompt | |
| </h4> | |
| <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-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg resize-none text-sm leading-relaxed font-mono" | |
| placeholder="Optimierter Prompt wird hier angezeigt..." | |
| /> | |
| <div className="absolute bottom-2 right-2 text-xs text-gray-500 dark:text-gray-400"> | |
| {result.optimizedPrompt.length} Zeichen | |
| </div> | |
| </div> | |
| </div> | |
| {/* Action Buttons */} | |
| <div className="flex flex-col sm:flex-row gap-4"> | |
| <button | |
| onClick={() => handleCopy(result.optimizedPrompt, 'optimized')} | |
| className="flex-1 btn-primary flex items-center justify-center space-x-2" | |
| > | |
| {copiedStates.optimized ? ( | |
| <> | |
| <CheckCircle className="w-4 h-4" /> | |
| <span>Kopiert!</span> | |
| </> | |
| ) : ( | |
| <> | |
| <Copy className="w-4 h-4" /> | |
| <span>Optimierten Prompt kopieren</span> | |
| </> | |
| )} | |
| </button> | |
| <button | |
| onClick={() => window.open(`https://chat.openai.com/?model=${result.targetModel}`, '_blank')} | |
| className="btn-secondary flex items-center justify-center space-x-2" | |
| > | |
| <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> | |
| </svg> | |
| <span>In Chat öffnen</span> | |
| </button> | |
| </div> | |
| </div> | |
| ) | |
| } | |
| </html> |