diff --git a/entrypoint.sh b/entrypoint.sh index 22ac360..57a3d62 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,17 +1,26 @@ #!/bin/sh set -e -DATA_FILE=/app/data/admin-content.json -SEED_FILE=/app/data-seed/admin-content.json +DATA_DIR=/app/data +SEED_DIR=/app/data-seed # Ensure the data directory exists (in case the volume was not mounted) -mkdir -p /app/data +mkdir -p "$DATA_DIR" -# Only seed from image defaults when no live data file exists yet. +# Seed each default JSON file only when missing. # This runs on first boot or a fresh volume, but never overwrites existing data. -if [ ! -f "$DATA_FILE" ] && [ -f "$SEED_FILE" ]; then - echo "[siteforge] No admin-content.json found. Seeding from image defaults..." - cp "$SEED_FILE" "$DATA_FILE" +if [ -d "$SEED_DIR" ]; then + for seed_file in "$SEED_DIR"/*.json; do + [ -f "$seed_file" ] || continue + + base_name=$(basename "$seed_file") + target_file="$DATA_DIR/$base_name" + + if [ ! -f "$target_file" ]; then + echo "[siteforge] No $base_name found. Seeding from image defaults..." + cp "$seed_file" "$target_file" + fi + done fi exec node server.js diff --git a/src/App.css b/src/App.css index 59b92f3..bafea66 100644 --- a/src/App.css +++ b/src/App.css @@ -774,6 +774,20 @@ padding: 0; } +.subscribe-card { + max-width: 560px; +} + +.subscribe-form { + margin-top: 1rem; + text-align: left; +} + +.subscribe-form .btn-secondary { + width: 100%; + justify-content: center; +} + .thanks-countdown { color: #d4b87a !important; letter-spacing: 0.06em; diff --git a/src/App.tsx b/src/App.tsx index 776194e..5b9cdd2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1127,6 +1127,116 @@ function ThankYouPage() { ) } +function SubscribePage() { + const navigate = useNavigate() + const [name, setName] = useState('') + const [email, setEmail] = useState('') + const [honey, setHoney] = useState('') + const [status, setStatus] = useState<'idle' | 'submitting' | 'error'>('idle') + const [errorMsg, setErrorMsg] = useState('') + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + setStatus('submitting') + setErrorMsg('') + + try { + const res = await fetch('/api/contact', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name, + email, + messageType: 'general', + message: 'Newsletter signup from subscribe page', + subscribe: true, + _honey: honey, + }), + }) + + if (!res.ok) { + const data = await res.json().catch(() => ({})) + setErrorMsg((data as { message?: string }).message ?? 'Something went wrong. Please try again.') + setStatus('error') + return + } + + navigate('/subscribe/thanks') + } catch { + setErrorMsg('Could not connect. Please try again later.') + setStatus('error') + } + } + + return ( +
+
+

Verse by Verse with Nate

+

Subscribe for Updates

+

+ Get ministry updates and new episode announcements by email. +

+ +
+ setHoney(e.target.value)} + /> + + + + {status === 'error' &&

{errorMsg}

} + + + Back to Site +
+
+
+ ) +} + +function SubscribeThankYouPage() { + return ( +
+
+

Verse by Verse with Nate

+

You are subscribed

+

+ Thank you for subscribing. You will receive future ministry updates and episode announcements. +

+ Back to Site +
+
+ ) +} + function LegalPage({ title, body }: { title: string; body: string[] }) { return (
@@ -1252,6 +1362,8 @@ export default function App() { } /> } /> + } /> + } /> } />