192 lines
8.7 KiB
TypeScript
192 lines
8.7 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
|
import { GeneratedContent, WizardState } from '../types';
|
|
import { Copy, Check, Instagram, Image as ImageIcon, MessageSquare, Edit2, RefreshCw, X } from 'lucide-react';
|
|
import TripMap from './TripMap';
|
|
import { UI_TEXT } from '../_EDITABLE_CONFIG/ui_text';
|
|
|
|
interface ExtendedStepResultProps {
|
|
content: GeneratedContent;
|
|
onRegenerate: (slideCount: number, feedback: string) => void;
|
|
isRegenerating: boolean;
|
|
tripData?: WizardState['tripData'];
|
|
eventType?: WizardState['eventType'];
|
|
}
|
|
|
|
const StepResult: React.FC<ExtendedStepResultProps> = ({ content, onRegenerate, isRegenerating, tripData, eventType }) => {
|
|
const [copiedSection, setCopiedSection] = useState<string | null>(null);
|
|
const [copiedSlideIndex, setCopiedSlideIndex] = useState<number | null>(null);
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
const [slideCount, setSlideCount] = useState(content.slides.length || 12);
|
|
const [feedback, setFeedback] = useState("");
|
|
|
|
const copyToClipboard = (text: string, sectionId: string) => {
|
|
navigator.clipboard.writeText(text);
|
|
setCopiedSection(sectionId);
|
|
setTimeout(() => setCopiedSection(null), 2000);
|
|
};
|
|
|
|
const copySlideText = (text: string, index: number) => {
|
|
navigator.clipboard.writeText(text);
|
|
setCopiedSlideIndex(index);
|
|
setTimeout(() => setCopiedSlideIndex(null), 2000);
|
|
};
|
|
|
|
const handleApplyChanges = () => {
|
|
onRegenerate(slideCount, feedback);
|
|
setIsEditing(false);
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-12 animate-fade-in pb-20 relative">
|
|
|
|
<div className="text-center max-w-2xl mx-auto relative">
|
|
<h2 className="text-4xl font-bold tracking-tight text-gray-900 mb-3">{UI_TEXT.stepResult.title}</h2>
|
|
<p className="text-gray-500 text-lg mb-6">{UI_TEXT.stepResult.subtitle}</p>
|
|
|
|
{!isEditing && !isRegenerating && (
|
|
<button
|
|
onClick={() => setIsEditing(true)}
|
|
className="inline-flex items-center space-x-2 text-sm font-bold text-gray-600 bg-gray-100 hover:bg-gray-200 px-5 py-2.5 rounded-full transition-colors"
|
|
>
|
|
<Edit2 size={16} />
|
|
<span>{UI_TEXT.stepResult.editBtn}</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{(isEditing || isRegenerating) && (
|
|
<div className="bg-white border-2 border-[#EA4420]/20 rounded-xl p-6 shadow-sm mb-10 animate-fade-in relative overflow-hidden">
|
|
{isRegenerating && (
|
|
<div className="absolute inset-0 bg-white/80 z-10 flex flex-col items-center justify-center backdrop-blur-[1px]">
|
|
<RefreshCw size={32} className="text-[#EA4420] animate-spin mb-3" />
|
|
<p className="font-bold text-gray-800">{UI_TEXT.stepResult.editPanel.regenerating}</p>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex justify-between items-start mb-6">
|
|
<h3 className="text-xl font-bold text-gray-900 flex items-center gap-2">
|
|
<Edit2 size={20} className="text-[#EA4420]" />
|
|
{UI_TEXT.stepResult.editPanel.title}
|
|
</h3>
|
|
{!isRegenerating && (
|
|
<button onClick={() => setIsEditing(false)} className="text-gray-400 hover:text-gray-600">
|
|
<X size={24} />
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-6">
|
|
<div>
|
|
<label className="flex justify-between text-sm font-bold text-gray-700 mb-3">
|
|
<span>{UI_TEXT.stepResult.editPanel.slidesLabel}</span>
|
|
<span className="text-[#EA4420]">{slideCount}</span>
|
|
</label>
|
|
<input
|
|
type="range"
|
|
min="3"
|
|
max="20"
|
|
value={slideCount}
|
|
onChange={(e) => setSlideCount(parseInt(e.target.value))}
|
|
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-[#EA4420]"
|
|
/>
|
|
<div className="flex justify-between text-xs text-gray-400 mt-2 font-medium">
|
|
<span>3 (Minimum)</span>
|
|
<span>20 (Maksimum)</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-bold text-gray-700 mb-2">{UI_TEXT.stepResult.editPanel.feedbackLabel}</label>
|
|
<textarea
|
|
value={feedback}
|
|
onChange={(e) => setFeedback(e.target.value)}
|
|
placeholder={UI_TEXT.stepResult.editPanel.feedbackPlaceholder}
|
|
rows={3}
|
|
className="w-full border border-gray-200 rounded-md p-3 text-sm focus:ring-1 focus:ring-[#EA4420] focus:border-[#EA4420] outline-none"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex justify-end pt-2">
|
|
<button
|
|
onClick={handleApplyChanges}
|
|
className="bg-[#EA4420] text-white px-6 py-3 rounded-md font-bold hover:bg-[#d63b1a] transition-colors flex items-center gap-2"
|
|
>
|
|
<RefreshCw size={18} />
|
|
{UI_TEXT.stepResult.editPanel.applyBtn}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* MAPA WYŚWIETLA SIĘ TYLKO DLA WYCIECZEK */}
|
|
{eventType === 'trip' && tripData && tripData.startPoint && (
|
|
<TripMap tripData={tripData} />
|
|
)}
|
|
|
|
<div className="bg-white rounded-md border border-gray-200 overflow-hidden">
|
|
<div className="bg-gray-50 px-8 py-5 border-b border-gray-200 flex justify-between items-center">
|
|
<div className="flex items-center space-x-3 text-gray-900">
|
|
<MessageSquare size={20} className="text-[#EA4420]" />
|
|
<span className="font-bold">{UI_TEXT.stepResult.captionTitle}</span>
|
|
</div>
|
|
<button
|
|
onClick={() => copyToClipboard(content.caption, 'caption')}
|
|
className="flex items-center space-x-2 text-sm font-semibold text-[#EA4420] hover:text-[#d63b1a] transition-colors bg-white border border-gray-200 px-4 py-2 rounded-md hover:bg-gray-50"
|
|
>
|
|
{copiedSection === 'caption' ? <Check size={16} /> : <Copy size={16} />}
|
|
<span>{copiedSection === 'caption' ? UI_TEXT.stepResult.copied : UI_TEXT.stepResult.copy}</span>
|
|
</button>
|
|
</div>
|
|
<div className="p-8 text-gray-700 whitespace-pre-wrap font-sans text-base leading-relaxed">
|
|
{content.caption}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div className="flex items-center space-x-3 text-gray-900 mb-8 px-1">
|
|
<ImageIcon size={28} className="text-[#EA4420]" />
|
|
<h3 className="text-2xl font-bold tracking-tight">{UI_TEXT.stepResult.slidesTitle}</h3>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{content.slides.map((slide, idx) => (
|
|
<div key={idx} className="bg-white rounded-md border border-gray-200 flex flex-col h-full hover:border-[#EA4420]/30 transition-colors group">
|
|
<div className="px-6 py-4 border-b border-gray-100 bg-gray-50/50 flex justify-between items-center">
|
|
<span className="text-xs font-bold text-gray-400 uppercase tracking-widest">
|
|
Element {idx + 1}
|
|
</span>
|
|
</div>
|
|
<div className="p-6 flex-1 flex flex-col space-y-6">
|
|
<div>
|
|
<div className="flex justify-between items-start mb-2">
|
|
<span className="text-xs text-[#EA4420] uppercase font-bold tracking-wider">Nagłówek / Typ</span>
|
|
<button
|
|
onClick={() => copySlideText(slide.overlay_text, idx)}
|
|
className="text-gray-300 hover:text-[#EA4420] transition-colors"
|
|
title={UI_TEXT.stepResult.copy}
|
|
>
|
|
{copiedSlideIndex === idx ? <Check size={14} className="text-green-500" /> : <Copy size={14} />}
|
|
</button>
|
|
</div>
|
|
<p className="font-bold text-gray-900 text-xl leading-tight">"{slide.overlay_text}"</p>
|
|
</div>
|
|
|
|
<div className="pt-4 border-t border-gray-100 mt-auto">
|
|
<div className="flex items-start space-x-3 text-gray-500">
|
|
<ImageIcon size={16} className="mt-1 flex-shrink-0 text-gray-400" />
|
|
<p className="text-sm italic leading-relaxed">{slide.image_prompt}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default StepResult;
|