diff --git a/.DS_Store b/.DS_Store index b9b5395..2e5e93b 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/App.tsx b/App.tsx index 69130e8..9e76762 100644 --- a/App.tsx +++ b/App.tsx @@ -11,11 +11,13 @@ import { ChevronLeft, ExternalLink, Sparkles, User, Lock, ArrowRight, AlertCircl import { generateStoryContent } from './services/geminiService'; import { getEnvVar } from './utils/envUtils'; +// --- CONFIG IMPORT --- +import { AUTHOR_CONFIG } from './_EDITABLE_CONFIG/author'; +import { UI_TEXT } from './_EDITABLE_CONFIG/ui_text'; + const STORAGE_KEY = 'gpx-storyteller-state-v6'; const AUTH_KEY = 'promptstory-auth-token'; -// --- PASSWORD CONFIGURATION --- -// STRICT MODE: No fallbacks. The environment variable must be set in Coolify/Vercel/Netlify. const APP_PASSWORD = getEnvVar('VITE_APP_PASSWORD'); const INITIAL_STATE: WizardState = { @@ -44,12 +46,9 @@ const INITIAL_STATE: WizardState = { } }; -// --- LOGIN SCREEN COMPONENT --- const LoginScreen: React.FC<{ onLogin: (success: boolean) => void }> = ({ onLogin }) => { const [input, setInput] = useState(''); const [error, setError] = useState(false); - - // Check if password is misconfigured (empty) const isConfigMissing = !APP_PASSWORD; const handleSubmit = (e: React.FormEvent) => { @@ -67,16 +66,10 @@ const LoginScreen: React.FC<{ onLogin: (success: boolean) => void }> = ({ onLogi
-

Błąd Konfiguracji

+

{UI_TEXT.login.configError}

- Aplikacja nie wykryła hasła w zmiennych środowiskowych. + Missing VITE_APP_PASSWORD.

-
-

Brakuje zmiennej: VITE_APP_PASSWORD

-

Jeśli używasz Coolify/Vercel:

-

1. Dodaj zmienną w panelu.

-

2. Przebuduj projekt (Re-deploy).

-
); @@ -85,83 +78,66 @@ const LoginScreen: React.FC<{ onLogin: (success: boolean) => void }> = ({ onLogi return (
-
-
-

Dostęp Chroniony

-

Wprowadź hasło, aby uzyskać dostęp.

+

{UI_TEXT.login.title}

+

{UI_TEXT.login.desc}

-
-
- { - setInput(e.target.value); - setError(false); - }} - placeholder="Hasło..." - className={`w-full p-4 border rounded-lg outline-none transition-all font-medium text-center tracking-widest ${ - error - ? 'border-red-300 bg-red-50 focus:border-red-500' - : 'border-gray-200 focus:border-[#EA4420] focus:ring-1 focus:ring-[#EA4420]' - }`} - autoFocus - /> -
- + { + setInput(e.target.value); + setError(false); + }} + placeholder="Hasło..." + className={`w-full p-4 border rounded-lg outline-none transition-all font-medium text-center tracking-widest ${ + error + ? 'border-red-300 bg-red-50 focus:border-red-500' + : 'border-gray-200 focus:border-[#EA4420] focus:ring-1 focus:ring-[#EA4420]' + }`} + autoFocus + /> {error && (
- Nieprawidłowe hasło + {UI_TEXT.login.error}
)} -
- -

- PromptStory v1.2 • Secure Production Build -

); }; const App: React.FC = () => { - // Auth State const [isAuthenticated, setIsAuthenticated] = useState(false); const [isAuthChecking, setIsAuthChecking] = useState(true); - - // App State const [data, setData] = useState(INITIAL_STATE); const [generatedContent, setGeneratedContent] = useState(null); const [isGenerating, setIsGenerating] = useState(false); const [isLoaded, setIsLoaded] = useState(false); const [errorMessage, setErrorMessage] = useState(null); - // Image loading states - const [logoError, setLogoError] = useState(false); - const [avatarError, setAvatarError] = useState(false); + // LOGO & AVATAR STATE + const [logoLoaded, setLogoLoaded] = useState(false); + const [avatarLoaded, setAvatarLoaded] = useState(false); - // 1. Check Auth on Mount useEffect(() => { const storedAuth = localStorage.getItem(AUTH_KEY); - if (storedAuth === 'true') { - setIsAuthenticated(true); - } + if (storedAuth === 'true') setIsAuthenticated(true); setIsAuthChecking(false); }, []); @@ -178,21 +154,18 @@ const App: React.FC = () => { resetApp(); }; - // 2. Load Data Persistence Logic useEffect(() => { const saved = localStorage.getItem(STORAGE_KEY); if (saved) { try { const parsed = JSON.parse(saved); - parsed.files = []; // Reset files - + parsed.files = []; if (!parsed.stats) parsed.stats = { distance: '', duration: '', elevation: '' }; if (!parsed.waypoints) parsed.waypoints = []; if (!parsed.tripData) parsed.tripData = { ...INITIAL_STATE.tripData }; if (!parsed.tone) parsed.tone = null; if (!parsed.goal) parsed.goal = null; if (!parsed.storyStyle) parsed.storyStyle = null; - setData(parsed); } catch (e) { console.error("Failed to load state", e); @@ -216,37 +189,28 @@ const App: React.FC = () => { }; const nextStep = () => { - if (data.step < Step.RESULT) { - updateData({ step: data.step + 1 }); - } + if (data.step < Step.RESULT) updateData({ step: data.step + 1 }); }; const prevStep = () => { - if (data.step > Step.CONTEXT) { - updateData({ step: data.step - 1 }); - } + if (data.step > Step.CONTEXT) updateData({ step: data.step - 1 }); }; const handleGenerate = async () => { setIsGenerating(true); setErrorMessage(null); - - // STRICT MODE: Use VITE_API_KEY const apiKey = getEnvVar('VITE_API_KEY'); - if (!apiKey) { - setErrorMessage("BŁĄD KRYTYCZNY: Brak klucza API (VITE_API_KEY). Skonfiguruj zmienne w panelu Coolify."); + setErrorMessage("BŁĄD: Brak VITE_API_KEY."); setIsGenerating(false); return; } - try { const content = await generateStoryContent(data, apiKey); setGeneratedContent(content); updateData({ step: Step.RESULT }); } catch (error: any) { - console.error("Generowanie nie powiodło się:", error); - setErrorMessage("Błąd generowania: " + (error.message || 'Sprawdź konsolę')); + setErrorMessage("Błąd: " + (error.message || 'Unknown')); } finally { setIsGenerating(false); } @@ -255,15 +219,12 @@ const App: React.FC = () => { const handleRegenerate = async (slideCount: number, feedback: string) => { setIsGenerating(true); setErrorMessage(null); - const apiKey = getEnvVar('VITE_API_KEY'); - try { const content = await generateStoryContent(data, apiKey || '', { slideCount, feedback }); setGeneratedContent(content); } catch (error: any) { - console.error("Regenerowanie nie powiodło się:", error); - setErrorMessage("Błąd regenerowania: " + (error.message || '')); + setErrorMessage("Błąd: " + (error.message || '')); } finally { setIsGenerating(false); } @@ -277,39 +238,34 @@ const App: React.FC = () => { }; if (isAuthChecking || !isLoaded) return null; - - if (!isAuthenticated) { - return ; - } - - const stepsLabels = ['Kontekst', 'Typ', 'Platforma', 'Vibe & Cel', 'Szczegóły']; + if (!isAuthenticated) return ; return (
- {!logoError ? ( - setLogoError(true)} - alt="PromptStory Logo" - className="w-10 h-10 object-contain" - /> - ) : ( -
- + alt="Logo" + onLoad={() => setLogoLoaded(true)} + className={`h-10 object-contain ${logoLoaded ? 'block' : 'hidden'}`} + /> + {!logoLoaded && ( +
+
+

{UI_TEXT.header.appTitle}

)} -

PromptStory

@@ -319,17 +275,14 @@ const App: React.FC = () => { {data.step !== Step.RESULT && (
- {stepsLabels.map((label, idx) => ( + {UI_TEXT.steps.labels.map((label, idx) => ( = idx ? 'text-[#EA4420]' : 'text-gray-300'}`}> 0{idx + 1}. {label} ))}
-
+
)} @@ -339,22 +292,8 @@ const App: React.FC = () => { {data.step === Step.EVENT_TYPE && } {data.step === Step.PLATFORM && } {data.step === Step.TONE_GOAL && } - {data.step === Step.DETAILS && ( - - )} - {data.step === Step.RESULT && generatedContent && ( - - )} + {data.step === Step.DETAILS && } + {data.step === Step.RESULT && generatedContent && } {errorMessage && (
@@ -372,26 +311,28 @@ const App: React.FC = () => { className="flex items-center space-x-2 text-gray-500 hover:text-gray-900 font-semibold px-6 py-3 rounded-md border border-gray-200 hover:bg-gray-50 transition-colors disabled:opacity-50" > - Wróć + {UI_TEXT.steps.nav.back}
)} + {/* FOOTER - Uses AUTHOR_CONFIG */}