Splits the analytics panel into Traffic / Engagement / System tabs so
episode plays, engagement metrics, and contact/study data are no longer
buried under the visitor log.
Fixes a bug where topCountries, topStates, topCities, device breakdown,
and referrers were computed from only the 100 most-recent visitor rows
instead of all rows in the selected range — causing older geographic data
(e.g. visits from India, Texas, Georgia) to silently disappear from the
aggregate totals.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces outbound-only tracking with a useLinkClickTracking hook that
captures every <a> click on the site. Stores as link_click events keyed
by destination URL, with an internal flag. Admin analytics page gains a
Link Clicks table sorted by count, showing type (internal/external).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EpisodeAudioPlayer restored to original horizontal layout (unchanged
for individual episode detail pages chosen in /admin)
- LatestEpisodePlayer on /episodes page gets its own markup + CSS
(.latest-episode-player__*) with the centered layout A — large gold
play button, skip-back 15s / skip-forward 30s, full-width scrubber
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EpisodeAudioPlayer full variant: centered title, large gold play button,
skip-back 15s / skip-forward 30s flanking controls, full-width scrubber
- Compact variant (study sections) unchanged
- Episode detail page fetches direct Anchor MP3 URL from /api/episodes by
title match; falls back to resolvedEmbedUrl while loading
- Plays stream from Anchor CDN so stats still count
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes CI lint errors — both functions were replaced by shareImageTo
in the previous commit but not deleted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaced the separate text-link X/FB buttons and the duplicate image buttons
with a single X and Facebook button each that share the generated quote card.
Keeps button row to 5: Upvote, X Share, Facebook Share, Copy link, Save image.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On mobile: Web Share API opens native share sheet with the PNG attached,
letting users pick X, Facebook, Instagram, etc. directly.
On desktop: downloads the image and opens the platform compose window.
"Save image" download button kept alongside the two new share buttons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Contact form: notifyOnAnswer checkbox (shown for Bible Questions only)
- Contact route: stores notifyOnAnswer flag on question record
- Email: buildQuestionAnsweredEmailTemplate for branded notification
- Questions route: sends notification email on approve when notifyOnAnswer + email + answer are set
- Q&A section: Canvas API "Save as image" button generates 1080x1080 PNG quote card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add qa-cards--compact class to container when active. Compact mode now
tightens padding, reduces font sizes, hides related questions, and
reduces card gaps — making the density difference obvious.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Testimonials: new Testimonial type, CMS field, admin UI under About,
and TestimonialsSection rendered on homepage and About page (hidden when empty)
- Q&A: "Submit a Question →" CTA at bottom of Q&A page linking to /contact
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Podcast highlights now display newest-first (reversed array)
- Q&A page uses full site layout (header, nav, footer) instead of card/modal wrapper
- Remove redundant "Back to Site" link from QASection now that header is present
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Docker deployments have no .env file — settings live in container env
vars — so the export now writes env-snapshot.env (dotenv format) into
the data dir from a whitelist of portable keys (admin password, Resend
keys, webhook URLs, etc.) before archiving. Machine-specific vars
(PORT, NODE_ENV, SITEFORGE_DATA_DIR, ALLOW_INSECURE_COOKIES, build
metadata) are excluded. Values are not applied automatically on
restore; the file documents what to set on the new server.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
iPadOS greys out .tar.gz files when the input has an accept filter, so
the archive could not be selected. The server validates the gzip
signature and archive contents before restoring, so no client-side
filter is needed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New admin endpoints: GET /api/admin-backup/export streams the entire
data directory as tar.gz (after flushing queued writes); POST
/api/admin-backup/import validates the archive, snapshots current
data, replaces the folder, and reloads all in-memory state.
- Admin UI: "Full Data Backup" section with download and restore-from-
file controls in the Analytics panel.
- Fix admin TOTP secret path to honor SITEFORGE_DATA_DIR (moved base
path resolution to server/paths.js to avoid a circular import), so
2FA for admin and study users survives backup/restore.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each visit is now a card showing name/IP, page path, location, device,
visit count, new/returning tag, and timestamp at a glance. Logged-in users
show their display name prominently. Click to expand still shows full page
history and raw details. Adds a purple "Logged in" tag for study users.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tracks scroll depth (25/50/75/90%), time on page, UTM parameters, outbound
link clicks, search queries, audio pause/completion/listen time, and 404s.
Logged-in study users are now tied to their visitor record and surfaced in
the admin recent visits table. Scroll position resets on every route change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- POST /api/analytics/play records a play event (title + date) on first
play per player mount, skipping admin sessions
- Plays persisted to data/episode-plays.json with total + byDay buckets
- Admin stats response includes episodePlays sorted by total plays
- AnalyticsPanel shows horizontal bar chart + summary cards for all
episodes dynamically — new episodes appear automatically as played
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add /api/episode-audio route returning MP3 URLs from Anchor RSS feed
- Replace Spotify URL text inputs in admin with RSS episode dropdowns
- New EpisodeAudioPlayer component with podcast art, show name, progress
bar, and Spotify icon link — full and compact sizes
- Backward-compatible: legacy Spotify embed URLs still render as iframes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 — Quick wins:
- Image lazy-loading on series/resource cards
- Newsletter signup added to Episodes page (before highlights)
- Per-route meta tags via usePageMeta hook (title, og:title, og:description)
- Breadcrumbs on study index, section, and notes pages
- SVG completion checkmark badges on study section list
- Analytics time-range filter (7d / 30d / 90d) in admin panel
Phase 2 — Medium features:
- Related episodes on archived series detail pages
- Resource library two-tier filter (type + tag chips)
- Global search (Fuse.js) moved below sticky header as full-width bar
- Q&A anonymous upvoting with localStorage dedup + admin pin/unpin
- Study enrollment funnel tracking (firstVisitAt, firstCompletionAt) with funnel chart in analytics
Phase 3 — Larger features:
- Study section comments (auto-approve for enrolled users, admin moderation panel)
- Study completion certificate (canvas render, PNG download, shareable public URL)
- Episode script full-text search (mammoth docx extraction, server-side search, admin upload UI)
- Reflection questions renamed from Discussion Questions; quiz answers can be shared to section discussion
- Public certificate route at /certificate/:token with og meta tags
- Comment moderation panel added to admin under Manage > Study Comments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>