Sprzątanie projektu - dodanie podglądu gpx - dodanie obsługi logo i avatara - dodanie editable config do prostej edycji tekstów na stronie

This commit is contained in:
Arek Bykowski
2026-02-15 18:43:34 +01:00
parent 78a34498d0
commit 981ce1d1b2
13 changed files with 459 additions and 386 deletions

View File

@@ -3,33 +3,18 @@ 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 StepResultProps {
interface ExtendedStepResultProps {
content: GeneratedContent;
onRegenerate: (slideCount: number, feedback: string) => void;
isRegenerating: boolean;
// We need to access the wizard state to check for trip data
// But standard props here only have content.
// Ideally, StepResult should receive `data` too, but for now I'll check if I can pass it from App.tsx or infer it.
// Wait, I can't access `data` unless I modify App.tsx to pass it to StepResult.
// Let's assume the parent updates the props.
// Actually, I'll modify the StepResultProps in this file, but I also need to modify App.tsx to pass 'data'.
// However, looking at App.tsx, StepResult is rendered inside App.tsx. I can pass `data` there easily.
// But wait, the previous code block for StepResult didn't show 'data' in props.
// I will add `tripData` to the props.
}
// Extending interface to include tripData optionally passed from parent
// Note: I will update App.tsx to pass this prop.
interface ExtendedStepResultProps extends StepResultProps {
tripData?: WizardState['tripData'];
tripData?: WizardState['tripData'];
}
const StepResult: React.FC<ExtendedStepResultProps> = ({ content, onRegenerate, isRegenerating, tripData }) => {
const [copiedSection, setCopiedSection] = useState<string | null>(null);
const [copiedSlideIndex, setCopiedSlideIndex] = useState<number | null>(null);
// Edit Mode State
const [isEditing, setIsEditing] = useState(false);
const [slideCount, setSlideCount] = useState(content.slides.length || 12);
const [feedback, setFeedback] = useState("");
@@ -48,16 +33,15 @@ const StepResult: React.FC<ExtendedStepResultProps> = ({ content, onRegenerate,
const handleApplyChanges = () => {
onRegenerate(slideCount, feedback);
setIsEditing(false); // Close edit panel on submit, assumes success or loading state handles visual feedback
setIsEditing(false);
};
return (
<div className="space-y-12 animate-fade-in pb-20 relative">
{/* Top Header with Edit Button */}
<div className="text-center max-w-2xl mx-auto relative">
<h2 className="text-4xl font-bold tracking-tight text-gray-900 mb-3">Twój Vibe Gotowy! 🎉</h2>
<p className="text-gray-500 text-lg mb-6">Oto kompletna struktura Twojego posta. Skopiuj i publikuj.</p>
<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
@@ -65,25 +49,24 @@ const StepResult: React.FC<ExtendedStepResultProps> = ({ content, onRegenerate,
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>Edytuj / Popraw</span>
<span>{UI_TEXT.stepResult.editBtn}</span>
</button>
)}
</div>
{/* Edit Panel (Conditional) */}
{(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">Nanuszę poprawki...</p>
<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]" />
Wprowadź poprawki
{UI_TEXT.stepResult.editPanel.title}
</h3>
{!isRegenerating && (
<button onClick={() => setIsEditing(false)} className="text-gray-400 hover:text-gray-600">
@@ -93,10 +76,9 @@ const StepResult: React.FC<ExtendedStepResultProps> = ({ content, onRegenerate,
</div>
<div className="space-y-6">
{/* Slider */}
<div>
<label className="flex justify-between text-sm font-bold text-gray-700 mb-3">
<span>Liczba slajdów / Elementów</span>
<span>{UI_TEXT.stepResult.editPanel.slidesLabel}</span>
<span className="text-[#EA4420]">{slideCount}</span>
</label>
<input
@@ -113,50 +95,46 @@ const StepResult: React.FC<ExtendedStepResultProps> = ({ content, onRegenerate,
</div>
</div>
{/* Feedback Textarea */}
<div>
<label className="block text-sm font-bold text-gray-700 mb-2">Co chcesz zmienić w treści?</label>
<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="np. Zmień 'ból szczęki' na 'ból głowy'. Dodaj więcej emoji w slajdzie nr 3. Zrób bardziej agresywny wstęp."
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>
{/* Action Buttons */}
<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} />
Zastosuj poprawki
{UI_TEXT.stepResult.editPanel.applyBtn}
</button>
</div>
</div>
</div>
)}
{/* TRIP MAP (IF APPLICABLE) */}
{tripData && tripData.startPoint && (
<TripMap tripData={tripData} />
)}
{/* Caption Section */}
<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">Post Caption (Opis)</span>
<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' ? 'Skopiowano!' : 'Kopiuj'}</span>
<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">
@@ -164,11 +142,10 @@ const StepResult: React.FC<ExtendedStepResultProps> = ({ content, onRegenerate,
</div>
</div>
{/* Slides Grid */}
<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">Struktura Wizualna (Slajdy / Zdjęcia)</h3>
<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">
@@ -186,7 +163,7 @@ const StepResult: React.FC<ExtendedStepResultProps> = ({ content, onRegenerate,
<button
onClick={() => copySlideText(slide.overlay_text, idx)}
className="text-gray-300 hover:text-[#EA4420] transition-colors"
title="Kopiuj tekst"
title={UI_TEXT.stepResult.copy}
>
{copiedSlideIndex === idx ? <Check size={14} className="text-green-500" /> : <Copy size={14} />}
</button>