Commit Graph
100 Commits
Author SHA1 Message Date
nmemmertandClaude Sonnet 5 a6e8243705 Add season field to episode scripts and improve global search; v2.0.50
Publish Container / build (push) Successful in 16m53s
Tag scripts with a season number (with an RSS-feed backfill for
existing uploads), surface it in search results, and fix word-boundary
matching plus a title/script-match dedup bug in the search merge logic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 08:45:40 -04:00
nmemmertandClaude Sonnet 4.6 b236e69f22 Switch Gitea workflow back to ubuntu-latest runner; v2.0.49
Publish Container / build (push) Successful in 13m50s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-22 10:24:36 -04:00
nmemmertandClaude Sonnet 4.6 8d5a983a94 Remove QEMU, build linux/arm64 natively on ARM runner; v2.0.48
Publish Container / build (push) Failing after 22s
Runner is on the ARM server so linux/arm64 builds natively — no QEMU
or cross-compilation needed. Drop setup-qemu-action and amd64 platform.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-22 10:23:17 -04:00
nmemmertandClaude Sonnet 4.6 22b82af3de Add Gitea Actions workflow with fast-builder runner; v2.0.46
Publish Container / build (push) Failing after 5m25s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-22 09:27:48 -04:00
nmemmertandClaude Sonnet 4.6 90c72c3265 Revert Gitea Actions workflow; v2.0.45
Publish Container / docker (push) Has been cancelled
Reverts the .gitea/workflows/docker-publish.yml addition from v2.0.44.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-22 08:42:10 -04:00
nmemmertandClaude Sonnet 4.6 861b32d54c Add Gitea Actions workflow for container builds; v2.0.44
Publish Container / docker (push) Has been cancelled
Mirrors the existing .github/workflows/docker-publish.yml so Gitea
Actions picks it up from the standard .gitea/workflows/ location.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-22 08:39:17 -04:00
nmemmertandClaude Sonnet 4.6 3c62499ad5 Fix 500 on all requests: recordVisitor is sync, not a Promise; v2.0.43
Publish Container / docker (push) Successful in 15m4s
server.js was calling recordVisitor(req, res).catch() but recordVisitor
returns undefined (it's synchronous). .catch() on undefined throws
TypeError on every request, causing Express to return 500 for every page.
Swap to try/catch since the function is synchronous.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-22 08:14:57 -04:00
nmemmertandClaude Sonnet 4.6 6621c45be8 Guard startup scheduler calls against unhandled promise rejections; v2.0.42
Publish Container / docker (push) Successful in 14m48s
Replace bare `void fn()` with `fn().catch(err => console.error(...))` for all
four startup-time scheduler invocations so an unexpected rejection cannot
crash the Node.js process (which exits on unhandled rejections in v15+).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-22 08:10:58 -04:00
nmemmertandClaude Sonnet 4.6 8ba300eddc Fix host upload: handle multer errors as JSON, pre-create upload-tmp dir; v2.0.41
Publish Container / docker (push) Successful in 15m21s
If multer failed to write the temp file (e.g. data-volume permission issue),
the error was propagating unhandled through Express, returning a non-JSON 500
that the client couldn't parse — showing only "Upload failed." with no details.

Changes:
- Import mkdir and pre-create UPLOAD_TEMP_DIR on startup (non-blocking)
- Wrap upload.single('file') in a callback so multer errors return JSON
  responses with the actual message instead of a bare HTML 500

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-22 07:49:00 -04:00
nmemmertandClaude Sonnet 4.6 600c450eff Fix episode highlight save: preserve structured show-notes fields; v2.0.40
Publish Container / docker (push) Successful in 15m47s
sanitizeFeaturedLinks was silently dropping showNotesSummary, showNotesThemes,
showNotesScripture, showNotesQuotes, and showNotesResources on every save — only
the legacy showNotes string was kept. Added sanitization for all five new fields
and updated the keep-filter to include them.

Also removed set -e from entrypoint.sh and made seed/merge steps non-fatal so a
permissions error on the mounted data volume no longer crash-loops the container.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-21 07:51:37 -04:00
nmemmertandClaude Sonnet 4.6 c79589b311 Add local vs R2 destination picker for admin asset uploads; v2.0.39
Publish Container / docker (push) Successful in 14m12s
Upload dropdown lets you choose R2 folder (uploads/images/guides/finished)
or local server disk. Asset table shows location column for each file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 12:20:21 -04:00
nmemmertandClaude Sonnet 4.6 4e6449f3a7 List all R2 objects bucket-wide so new folders are picked up automatically; v2.0.38
Publish Container / docker (push) Successful in 13m50s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 12:06:10 -04:00
nmemmertandClaude Sonnet 4.6 f525e7e784 Add Cloudflare R2 storage for admin asset uploads; v2.0.37
New uploads route to R2 with folder selection (uploads/images/guides/finished).
Existing local files continue to be served via express.static for backward compat.
Adds R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET,
R2_PUBLIC_URL env vars. listUploadedAssets merges R2 objects + local files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 12:04:55 -04:00
nmemmertandClaude Sonnet 4.6 f254e3ec19 Add scheduled series auto-launch for series card, Start Here, and study guide; v2.0.36
Publish Container / docker (push) Successful in 14m20s
One scheduled date/time in the Current Series admin panel archives the active series to
Finished Books and atomically swaps in new content for the series card, Start Here steps,
and Companion Study Guide. Server checks every hour via setInterval; also runs on restart
so the transition fires even if the server was down at the scheduled moment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 11:45:02 -04:00
nmemmertandClaude Sonnet 4.6 06f89d6436 Fix Scripture Index page width; v2.0.35
Publish Container / docker (push) Successful in 14m16s
.scripture-index-main had no max-width — page stretched full viewport width.
Set to 900px with auto margins to match the site standard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 11:13:54 -04:00
nmemmertandClaude Sonnet 4.6 a4c37d0276 Render QR code on finished series page; v2.0.34
Publish Container / docker (push) Successful in 13m37s
The downloadQrUrl field was saved but never displayed — add the QR image
below the action buttons, wrapped in a link to the download URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 11:12:20 -04:00
nmemmertandClaude Sonnet 4.6 e64c784c7c Standardize page content widths to 900px; v2.0.33
Publish Container / docker (push) Successful in 14m16s
- Add missing .page-inner CSS (finished series page was stretching full width)
- Fix .site-search-bar-inner: 860px → 900px
- Remove redundant max-width from .section-share-inner (inherits 900px from .section-inner)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 10:34:21 -04:00
nmemmertandClaude Sonnet 4.6 7ac23635b2 Add QR code field to finished books admin; v2.0.32
Publish Container / docker (push) Successful in 14m33s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 10:20:35 -04:00
nmemmertandClaude Sonnet 4.6 e86594b096 Add download option and CD disc artwork to finished books; v2.0.31
Publish Container / docker (push) Successful in 14m49s
- Add downloadUrl field to FinishedBook type
- Admin: paste any external URL (Drive, Dropbox, etc.) per finished book
- Public series page: replace square hero image with circular CD disc artwork
  (conic-gradient sheen, center hole, drop shadow, slight tilt)
- Stream Episodes + Download Album action buttons in the hero
- Grid cards show a gold download badge when a download URL is set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 10:16:42 -04:00
nmemmertandClaude Sonnet 4.6 65623ce0eb Wire up deployment metadata: build commit, number, and server start time; v2.0.30
Publish Container / docker (push) Successful in 14m11s
- Dockerfile: expose BUILD_COMMIT and BUILD_NUMBER env vars from build args
  (was COMMIT_SHA only; server was reading BUILD_COMMIT so it always fell through to null)
- CI: pass github.run_number as BUILD_NUMBER build arg
- state.js: record serverStartedAt at module load so deployedAt always shows
  when the current container instance came up, even without an env var
- admin-content.js: fall back COMMIT_SHA → BUILD_COMMIT for older images,
  use state.serverStartedAt when DEPLOYED_AT env var not set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-17 16:52:44 -04:00
nmemmertandClaude Sonnet 4.6 4002f1ee93 Analytics overhaul: privacy fixes, All Time range, readability improvements; v2.0.29
Publish Container / docker (push) Successful in 14m17s
- Remove raw IP storage; store only SHA-256 hash prefix in visitor records
- Non-blocking geo lookup: recordVisitor now synchronous, geo resolved in background
- UTM capture race condition fixed via sessionStorage pre-consent capture
- Add consent + bot checks to play endpoint (was missing)
- Add rate limiter to pageview endpoint (60 req/min per IP)
- Add 'All time' range option alongside 7d/30d/90d with trendData aggregation
- AnalyticsPanel: tabbed layout (Traffic/Engagement/System), dynamic chart titles,
  range buttons with active state, visitor log uses ipHash instead of raw IP,
  trendData drives main chart with weekly bucketing for all-time view
- Re-add prop-types dependency (required by react-simple-maps bundler)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-17 16:27:53 -04:00
nmemmertandClaude Sonnet 4.6 da5c1c5078 Remove prop-types from node_modules via npm prune; v2.0.28
Publish Container / docker (push) Failing after 10m38s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-17 16:11:47 -04:00
nmemmertandClaude Sonnet 4.6 e158e1e53a Codebase audit: remove dead code, fix broken patterns, consolidate duplicates; v2.0.27
Publish Container / docker (push) Failing after 10m40s
- Remove unused prop-types dep, deprecated useOutboundLinkTracking export
- Fix missing linkClicks key in analyticsEvents initial state (B-01)
- Export ADMIN_SESSION_COOKIE from auth.js; remove local copy in admin-auth.js (D-02)
- Remove cookieFlags() wrapper in auth.js and study-helpers.js; use cookieSecureFlag() directly (U-04)
- Remove ADMIN_SESSION_TTL_MS duplicate from config.js (D-01)
- Remove DEFAULT_SEO/DEFAULT_LEGAL/DEFAULT_REDIRECT_RULES/DEFAULT_PODCAST_FEATURED_LINKS/DEFAULT_PUBLISH_STATE
  duplicates from config.js; re-export from helpers.js (D-04, fixes sitemapPaths divergence)
- Fix email.js hardcoded fallback strings to use DEFAULT_RESEND_TO/DEFAULT_RESEND_REPLY_TO (M-02)
- Remove duplicate normalizeIp, isPrivateOrLocalIp, normalizeRedirectPath, sanitizeUrl,
  sanitizeRedirectRules bodies from study-helpers.js; re-export from helpers.js (D-03)
- Fix pruneStatsByDays to import VISITOR_RETENTION_DAYS_DEFAULT from config.js (B-02)
- Fix createTitusDownloadToken to use DOWNLOAD_TOKEN_TTL_MS constant (B-03)
- Remove normalizeMessageType duplicate from study-helpers.js (U-03/D-08)
- Remove dead wantsWelcome variable in contact.js; use shouldSendWelcome (B-04)
- Replace dynamic imports in contact.js with static imports (B-07)
- Remove dead wantsWelcome=true in study-auth.js (B-05)
- Merge duplicate import blocks in analytics.js and public.js (D-05, D-06)
- Replace dynamic readFile re-import in nuggets-host-upload.js (B-06)
- Replace hardcoded SITE_URL fallbacks with getCanonicalBaseUrl() (M-06)
- Replace raw resend.emails.send in study-account.js with sendResendEmailWithRetry (M-01)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-17 16:10:31 -04:00
nmemmertandClaude Sonnet 4.6 1ddb8ee7be Add Nuggets to global search and RSS-sourced scripture refs to index; v2.0.26
Publish Container / docker (push) Successful in 15m20s
- Raise RSS description truncation from 220 → 800 chars so scripture
  references in episode descriptions are preserved
- Global search now indexes Nuggets podcast episodes from
  /api/nuggets/episodes/all (title, subtitle, description via Fuse)
- Scripture Index (/api/public/scripture-index) now extracts verse
  references from both main podcast and Nuggets RSS descriptions
  using a regex built from the shared BIBLE_BOOKS list; duplicate refs
  across sources are merged by the existing client-side dedup logic
- Export fetchNuggetsEpisodes from nuggets.js for reuse

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-17 10:27:12 -04:00
nmemmertandClaude Sonnet 4.6 590885f6b1 Add contact form inbox to admin portal; v2.0.25
- New Inbox view in the Audience nav group shows all contact form submissions
- Filter by Unread / All / Archived with a search bar for name, email, or message
- Click any message to open a detail pane with full text, metadata, and a Reply via Email link
- Archive / Unarchive button to clear messages without deleting them
- Unread badge on the Inbox nav item; dashboard card now links directly to Inbox
- Added PATCH /api/admin-contact-submissions/:id to persist archived state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-17 10:20:49 -04:00
nmemmertandClaude Sonnet 4.6 8ff01f8e48 Raise host bio character limit from 600 to 4000; v2.0.24
Publish Container / docker (push) Successful in 13m18s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-16 09:49:25 -04:00
nmemmertandClaude Sonnet 4.6 b999a2fe86 Support multi-paragraph host bios on detail page; v2.0.23
Publish Container / docker (push) Successful in 13m57s
Split bio on blank lines to render each paragraph as its own <p> tag.
Also grows the admin textarea from 3 to 8 rows so long bios are easier
to write and review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-16 08:20:35 -04:00
nmemmertandClaude Sonnet 4.6 7ff5c453e8 Redesign Nuggets page: host pills in a row above episodes; v2.0.22
Publish Container / docker (push) Successful in 14m8s
Replaces the two-column hosts/episodes sidebar layout with a single full-width
column: host pills (photo + name, pill-shaped, clickable) in a horizontal row
at the top, episodes list filling the full width below.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-16 07:37:25 -04:00
nmemmertandClaude Sonnet 4.6 b35605f411 Add clickable host bios linking to host detail pages on Nuggets; v2.0.21
Publish Container / docker (push) Has been cancelled
Each host card on /nuggets now links to /nuggets/hosts/:hostId showing
the host's full photo, bio, and listen links. Archived hosts return a
graceful "not found" page with a back link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-16 07:32:18 -04:00
nmemmertandClaude Sonnet 4.6 4607532440 Decode HTML entities in RSS episode descriptions; v2.0.20
Publish Container / docker (push) Successful in 15m27s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-09 19:16:25 -04:00
nmemmertandClaude Sonnet 4.6 ac65db796a Show episode description in Nuggets episode list; v2.0.19
Publish Container / docker (push) Successful in 13m47s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-09 08:48:22 -04:00
nmemmertandClaude Sonnet 4.6 86de45dbc3 Make recording guidelines editable from admin panel; v2.0.18
Publish Container / docker (push) Successful in 14m23s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-09 08:38:02 -04:00
nmemmertandClaude Sonnet 4.6 ef1d9fd8fc Add full portal expansion: 2FA, change password, history, announcements, recording guidelines, Seafile link, draft saving, file validation; v2.0.17
Publish Container / docker (push) Successful in 14m36s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-09 08:08:42 -04:00
nmemmertandClaude Sonnet 4.6 afff859d17 Fix upload notification email dark text on dark background; v2.0.16
Publish Container / docker (push) Successful in 13m16s
Body text was #2c2417 inside the dark branded email wrapper. Updated
to light brand colors: #c8c0ac body, #f0ead8 emphasis, #c9a84c filename.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 16:01:22 -04:00
nmemmertandClaude Sonnet 4.6 51543396e3 Fix Seafile upload response parsed as JSON; v2.0.15
Publish Container / docker (push) Successful in 14m18s
Seafile seafhttp returns a plain file ID string, not JSON. Replaced
res.json() with res.text() — the return value was unused anyway.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 15:42:02 -04:00
nmemmertandClaude Sonnet 4.6 26a51f9089 Fix Seafile API path from /api/v2.1/ to /api2/; v2.0.14
Publish Container / docker (push) Successful in 13m54s
This Seafile instance exposes /api2/ not /api/v2.1/. Updated both the
upload-link call in the host upload route and the dir listing in the
Seafile watcher. Confirmed /api2/ works via node fetch test on the server.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 15:25:48 -04:00
nmemmertandClaude Sonnet 4.6 806c3bd59c Fix host welcome email using dark text on dark background; v2.0.13
Publish Container / docker (push) Successful in 13m29s
Body text was #2c2417 (near-black) rendered inside the dark branded
email wrapper (#0f0f0c). Updated to light colors matching the template:
#c8c0ac for body, #f0ead8 for emphasis, #1a1a15/#2a2518 for table cells.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 14:58:37 -04:00
nmemmertandClaude Sonnet 4.6 3febad351d Fix Seafile upload-link using wrong query param ?path= instead of ?p=; v2.0.12
Publish Container / docker (push) Successful in 14m0s
Seafile API uses ?p= for the directory path parameter. Using ?path= caused
the request to never match an API route, returning the web UI 404 HTML page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 14:45:51 -04:00
nmemmertandClaude Sonnet 4.6 ce12ae1d0d Fix reset-password email sending undefined as email field; v2.0.11
Publish Container / docker (push) Has been cancelled
Was passing data.hosts[idx].username (doesn't exist on stored hosts)
instead of data.hosts[idx].email for the welcome/reset email username param.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 14:41:41 -04:00
nmemmertandClaude Sonnet 4.6 4940e53a98 Fix host portal using wrong CSS variables; v2.0.10
Publish Container / docker (push) Has been cancelled
Replaced --color-bg/surface/border/input-bg fallbacks (which don't exist
in the site's CSS) with the actual --brand-* tokens so the portal
renders in the site's dark theme instead of light fallback colors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 14:40:47 -04:00
nmemmertandClaude Sonnet 4.6 f5c5281684 Fix build error: replace stale h.username ref with h.email; v2.0.9
Publish Container / docker (push) Successful in 13m30s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 14:17:55 -04:00
nmemmertandClaude Sonnet 4.6 684181c34c Use email as host login credential; remove separate username field; v2.0.8
Publish Container / docker (push) Failing after 10m44s
- Login form now asks for email + password (no username)
- Admin creates hosts with name + email + password; email is always
  required and welcome email always sends on creation
- Duplicate email check replaces duplicate username check
- Admin table shows Email (login) column only; username field removed
  throughout data model, routes, and UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 14:05:02 -04:00
nmemmertandClaude Sonnet 4.6 a12a471c41 Add host welcome/reset emails, email field, and /host URL; v2.0.7
- Host accounts now have an optional email field
- Creating a host with an email sends a welcome email with the portal
  URL, username, and password via Resend
- Resetting a password also emails the new credentials if the host
  has an email on file; admin sees a confirmation either way
- Admin panel shows email column and confirmation messages
- Portal URL changed from /host-upload to /host

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 14:01:51 -04:00
nmemmertandClaude Sonnet 4.6 1e396644a9 Add upload email notifications with Seafile direct-upload watcher; v2.0.6
- Portal uploads send one email immediately (host, episode, passage,
  description, filenames) via Resend to NUGGETS_NOTIFY_EMAIL
- Seafile watcher polls /Episode Upload every 15 min for direct uploads
  and sends a notification for any new file not already seen via portal
- Files uploaded through the portal are marked seen so the watcher
  never sends a duplicate notification
- Credentials kept out of git: SEAFILE_TOKEN, SEAFILE_NUGGETS_REPO_ID,
  NUGGETS_NOTIFY_EMAIL now sourced from docker .env file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 13:54:46 -04:00
nmemmertandClaude Sonnet 4.6 6a03813914 Redesign Nuggets upload portal: single episode submission form; v2.0.5
- Replace two-tab file picker with a combined form: episode name, passage,
  description, and audio file in one submit
- Server now generates an Episode Info text file (name, host, passage,
  description, timestamp) and uploads it to /Episode Info/ automatically,
  while the audio goes to /Episode Upload/
- Drop zone changes color when a valid file is selected (green border)
- Success shows submitted episode name and filename in the session log

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 13:46:20 -04:00
nmemmertandClaude Sonnet 4.6 afdc00114a Add Nuggets host upload portal with Seafile integration; v2.0.4
- New /host-upload page: per-host login (username + password) with
  session cookie, drag-and-drop file upload to Seafile via server proxy
- Two upload targets: Episode Upload (audio) and Episode Info (docs)
- Server route: /api/nuggets-upload/* for host auth + file proxy,
  /api/admin/nuggets-upload/hosts for admin CRUD (create/disable/reset pw/delete)
- Admin panel: Nuggets → Upload Portal view to manage host accounts
- Seafile config via SEAFILE_URL / SEAFILE_TOKEN / SEAFILE_NUGGETS_REPO_ID env vars
- Directories default to /Episode Upload and /Episode Info (configurable)
- Uses multer for multipart uploads; 500 MB per-file limit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 13:38:57 -04:00
nmemmertandClaude Sonnet 4.6 29d2ace1b2 Fix cramped host card layout on Nuggets page; v2.0.3
Publish Container / docker (push) Successful in 12m59s
- Restructure host card: image+name/role in a row header, bio spans full card width below
- Widen hosts column from 300px max to 360px max
- Add nuggets-host-card-header and nuggets-host-card-meta CSS classes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 10:47:21 -04:00
nmemmertandClaude Sonnet 4.6 334b455d25 Bump to v2.0.2
Publish Container / docker (push) Successful in 13m56s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 09:14:52 -04:00
nmemmertandClaude Sonnet 4.6 20fbfb4772 Fix Docker image missing shared/ directory; v2.0.2
Publish Container / docker (push) Successful in 13m10s
shared/bible-books.js was added in the consolidation commit but the
Dockerfile had no COPY line for it, causing a boot crash in production.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 09:14:36 -04:00
nmemmertandClaude Sonnet 4.6 aac7afbdf1 Update runtime data and sync lock file to v2.0.1
Publish Container / docker (push) Successful in 13m45s
- study-users: add new user, migrate existing users to reminder-frequency schema fields
- study-reminders: bump updatedAt timestamp
- package-lock: sync version to 2.0.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 08:22:12 -04:00
nmemmertandClaude Sonnet 4.6 4378fad66f Fix 5 bugs: analytics range keys, stale visitor rows, NuggetsPage init state, episodes timer leak, unsanitized nuggets object; v2.0.1
Publish Container / docker (push) Successful in 14m54s
- analytics: union byDayReal+byDayBot keys so bot-only days aren't dropped from range totals
- analytics: exclude timestamp-less visitor rows from range filters (was unconditionally including them)
- App.tsx: init `configured` to false so a network failure doesn't show a misleading "no episodes" state; add r.ok guard before r.json()
- episodes.js: wrap fetch in try/catch so clearTimeout is called on network failure, matching the nuggets.js pattern
- helpers.js: add sanitizeNuggets + wire it into sanitizeSiteContent so corrupted hosts/urls can't crash NuggetsPage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 08:21:02 -04:00
nmemmertandClaude Sonnet 5 31e79ad542 Add Verse By Verse: Nuggets sub-show with editable hosts and RSS feed
Publish Container / docker (push) Successful in 14m16s
New /nuggets page for the companion show, linked from the Episodes nav
dropdown. Hosts (name, role, bio, photo) show in a left column with the
episode list on the right — latest episode featured, rest below.
Episodes are pulled from an admin-configurable RSS feed URL (empty by
default, shows "coming soon" until set) rather than hardcoded like the
main show's feed, since the show doesn't have one yet.

Everything is editable in /admin under a new Nuggets section: Show
Settings (title, tagline, description, podcast art, RSS/platform URLs)
and Hosts (add freely; removing a host archives them instead of
deleting — kept on record and restorable, never lost).

Extracted the RSS item-parsing logic shared with the main show's feed
(server/routes/episodes.js) into server/rss-helpers.js so both feeds
use the same parser instead of duplicating it.

Also fixes the local Vite dev server not proxying /uploads/* to the
backend — it was silently serving the SPA shell (200, text/html)
instead of the actual file, so any admin-uploaded image (podcast art,
host photos, etc.) appeared broken in local dev only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Yvx4rPVy2HWe7ubuk3X3J
2026-09-07 14:27:52 -04:00
nmemmertandClaude Sonnet 5 335a9cbe31 Consolidate the three duplicate Bible-book lookup tables into one shared registry
Publish Container / docker (push) Successful in 14m9s
src/App.tsx (Scripture Index book map) and server/routes/bible-passage.js
(BSB/bolls.life passage fetch) each hand-maintained their own independent
66-book list with a different key format (3-letter USFM id vs bolls.life's
numeric id), so a future alias/rename fix in one would silently not apply to
the other.

Add shared/bible-books.js as the single canonical list (name + aliases +
USFM id, in canonical order); the server derives its numeric bolls.life ids
from the array position instead of hand-maintaining a second table. Requires
allowJs + including shared/ in tsconfig.app.json so the client can import the
plain-JS module alongside its TypeScript sources.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Yvx4rPVy2HWe7ubuk3X3J
2026-09-07 09:49:52 -04:00
nmemmertandClaude Sonnet 5 c158fb6317 Fix contact form crash, analytics range filter, Scripture Index parsing, and unsafe-delete UI bugs
Publish Container / docker (push) Successful in 13m12s
- server/routes/contact.js: import fireWebhooks (was undefined, throwing on every submission)
- server/routes/analytics.js: filter admin-stats by row.at, the field recordVisitor actually sets, instead of the never-populated row.visitedAt
- src/App.tsx: parseSIRef now supports chapter-spanning verse ranges (e.g. "5:15-6:2"); SI_BOOK_MAP indexes both spaced and unspaced book names so multi-word Bible Book values from the admin field resolve correctly
- src/App.css / src/App.tsx: fix Episodes/Studies nav dropdown menus being unclickable — a positioning gap between the trigger and menu created a hover dead zone; menu is now flush with spacing moved into padding so hover carries through
- src/components/StudySectionComments.tsx: check response status before removing a comment locally, so a rejected delete no longer appears to succeed
- src/components/QASection.tsx: don't bail out of search scoring for 1-2 character queries, so short substrings like "OT" or "AI" can still match
- src/AdminPage.tsx: check response status in study-user, QR code, and episode-script delete handlers before updating local state

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Yvx4rPVy2HWe7ubuk3X3J
2026-09-07 09:39:37 -04:00
nmemmertandClaude Sonnet 5 6ed29ea527 Rebuild Scripture Index as a true cross-content reference index
Publish Container / docker (push) Successful in 15m2s
Previously it just grouped study lessons under each study (essentially
another list of lessons) and ignored podcast episodes entirely. Now it
parses every reference from studies and episode show notes, merges
entries that point at the same verse(s) into one row with links to all
sources, and groups rows by book in canonical Bible order. References
that don't parse (e.g. whole-chapter refs) fall into an "Other
References" section instead of being dropped.

Also fixes the reference passed to the index for studies without a
bibleBook set — it was falling back to the study's marketing title
instead of its slug, producing unparseable references.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Yvx4rPVy2HWe7ubuk3X3J
2026-09-07 09:20:12 -04:00
nmemmertandClaude Sonnet 4.6 274adcc093 Merge feature/show-notes-form into main — v2.0.0
Publish Container / docker (push) Successful in 13m47s
- Structured show notes form for Episode Highlights (v1.2.3)
- Study account engagement: streaks, re-engagement reminders (v1.2.4)
- Redesigned study lesson UI: progress bar, sidebar nav, inline answers (v1.2.5)
- Social media community feed: likes, threading, relative timestamps (v1.2.6)
- Community shortcut in lesson sidebar (v1.2.7)
- v2.0.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 16:44:46 -04:00
nmemmertandClaude Sonnet 4.6 e35e5d55bb Bump to v2.0.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 16:41:18 -04:00
nmemmertandClaude Sonnet 4.6 53e200174f Add community shortcut to lesson sidebar; v1.2.7
Adds a "Discuss this lesson" link in the classroom sidebar that takes
the user directly to the community page pre-filtered to the current
lesson's sectionId, so they can jump into discussion without navigating
back to the study index first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 16:38:38 -04:00
nmemmertandClaude Sonnet 4.6 49e3a80c2c Redesign community section with social media feed UX; v1.2.6
- Twitter/X-style post cards: avatar thread line, author + relative timestamp
- Heart like button with optimistic toggle and like count per post
- Comment button with reply count; inline threaded replies always visible
- Compose card with user avatar, borderless textarea, pill Post button
- Horizontal scrollable lesson filter chips (All + per-lesson)
- New POST /api/study-community/posts/:postId/like endpoint (toggle)
- GET community posts now returns likedByMe + likeCount per post
- sanitizeStudyCommunityPosts preserves likedBy array on disk
- Relative timestamps (just now, Xm ago, Xh ago, Xd ago, then date)
- UserAvatar helper renders img or initials fallback consistently

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 16:34:28 -04:00
nmemmertandClaude Sonnet 4.6 874dd0c89b Redesign study lesson UI: progress bar, sidebar nav, inline answers; v1.2.5
- Progress bar: thin gold bar under lesson label showing completed/total lessons
- Sidebar lesson navigator: all lessons listed with ✓/current/○ states, replaces static Study Track + Lesson Navigation blocks
- Notes panel moves to sidebar: clicking a block's Note button opens NoteCard in sidebar instead of inline, preserving reading position
- Inline reflection answers: textareas under each question auto-save to quiz API; replaces separate quiz page link
- Note buttons: "Note" / "✎ Note" text label instead of bare pencil icon for better discoverability
- Greek Word Study block hidden when lesson has no greek notes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 16:01:15 -04:00
nmemmertandClaude Sonnet 4.6 038ef2b039 Add study account engagement features; v1.2.4
- Pause reminders: remindersPausedUntil field + scheduler skip + account UI with 7/14/30-day presets
- Email digest mode: reminderFrequency field (immediate/weekly) + weekly digest email + scheduler + account UI toggle
- Admin user list improvements: streak, reengagement, frequency, pause, per-study lesson progress
- Admin manual re-engagement: POST endpoint + tier selector button in admin panel
- Study bookmarks: bookmarkedSectionIds in progress file + POST/DELETE endpoints + toggle button in lesson UI
- Study completion certificate email: auto-sent on first certificate issuance
- Content fields for certificate and weekly digest email templates in admin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 15:16:47 -04:00
nmemmertandClaude Sonnet 4.6 5bc8245ed2 Add structured show notes form to Episode Highlights; v1.2.3
Replace single showNotes textarea with five guided fields: Episode Summary,
Key Themes, Scripture References, Key Quotes, and Resources & Links. Legacy
showNotes content is preserved as a backward-compatible fallback — it
auto-populates the Summary field in the admin editor and renders on the public
episode detail page until overwritten with structured data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 14:17:47 -04:00
nmemmertandClaude Sonnet 4.6 525ae85f6a Fix scripture verse popup clipped by overflow:hidden parents; v1.2.2
Publish Container / docker (push) Successful in 13m53s
Switch from position:absolute (relative to trigger) to position:fixed
with getBoundingClientRect() so the popup escapes any overflow:hidden
ancestor. Also closes on scroll and window resize.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 11:21:04 -04:00
nmemmertandClaude Sonnet 4.6 36dfa0c1d7 Redesign Scripture Index with BSB verse text and row layout; v1.2.1
Publish Container / docker (push) Successful in 13m38s
- Replace cramped auto-fill grid with full-width row layout (reference | verse text | arrow)
- Fetch BSB passage text from bible.helloao.org, grouped by chapter to minimize API calls
- Display 2-line italic excerpt of each passage beneath the lesson title
- Hover state reveals arrow and highlights row in gold
- Mobile-responsive collapse to single column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 11:04:22 -04:00
nmemmertandClaude Sonnet 4.6 83d631608b Add Studies dropdown with Scripture Index link; split nav triggers into hover+click
Publish Container / docker (push) Successful in 14m4s
- Studies nav item is now a dropdown (same pattern as Episodes): hover to expand,
  click the label to navigate directly to /study
- Dropdown contains: All Studies (/study) and Scripture Index (/scripture)
- Episodes dropdown follows the same split pattern: label navigates, caret toggles
- Dropdown menus open on CSS :hover (desktop) and stay toggle-able on mobile
- v1.2.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 10:34:17 -04:00
nmemmertandClaude Sonnet 4.6 63d4f9e316 v1.2.0: Broadcast emails, webhook admin UI, dynamic sitemap, scripture index
Publish Container / docker (push) Successful in 14m11s
- Admin > Audience > Broadcast Email: compose and send one-off HTML emails
  to all subscribers via Resend batch API; {{name}} personalization; confirm step
- Admin > Configure > Webhooks: CRUD UI for managing POST event webhooks;
  backend GET/POST/DELETE /api/admin-webhooks routes wired to existing state.webhooks
- Sitemap: dynamically appends released study lesson URLs (/study/:slug/:id)
  and episode highlight URLs (/episodes/:id) to /sitemap.xml
- Scripture Index: public /scripture page listing every released lesson grouped
  by study with passage reference links; served by /api/public/scripture-index

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 09:54:43 -04:00
nmemmertandClaude Sonnet 4.6 0bf2d92b1f Apply master-detail pattern to Ep. Highlights and Download Library; v1.1.52
Publish Container / docker (push) Successful in 13m55s
Episode Highlights and the Downloads Library section now use the same compact
list → click to edit pattern as Studies. Both sections show a scannable row
list (title, key meta, edit/delete buttons) and open a focused editor for a
single item. Testimonials and Finished Books remain as collapsible cards since
they have few fields and grow slowly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 09:31:43 -04:00
nmemmertandClaude Sonnet 4.6 14665d06b5 Replace study lesson expanding cards with master-detail drill-down; v1.1.51
Publish Container / docker (push) Successful in 13m15s
Studies section now uses a three-level navigation: study list → study detail
with compact sortable lesson rows → individual lesson editor. Eliminates the
40+ expanding AdminCollapsibleCard items for lessons, replacing them with a
compact table (drag handle, reference, title, live/draft status, edit/preview/
delete per row) and a focused lesson editor opened by clicking Edit. Prev/Next
navigation between lessons added. BsbFetchButton extracted as a standalone
component for the inline lesson editor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 09:23:56 -04:00
nmemmertandClaude Sonnet 4.6 e593031d0d Revamp /admin with drill-down sidebar nav and focused sub-views; v1.1.50
Publish Container / docker (push) Successful in 13m50s
- Replace flat sidebar with accordion nav: parent items (Homepage, About,
  Contact, Podcast Hub) expand to reveal focused sub-items
- Split big sections into granular sub-views so each form shows only 3-8
  fields (Homepage: Hero/Share/PRISM/Where-to-Next, About: Show Copy/Bio/
  Testimonials, Contact: Profile/Copy & Scripture, Podcast: Current Series/
  Highlights/Finished Books/Scripts/RSS)
- New topbar: brand + breadcrumb (Homepage › Hero), clean gold Save Draft
  button, ghost Preview/Publish buttons — no more jump-to select
- New sidebar: compact 196px width, filter input at top, small-caps group
  labels, chevron-animated parent items, indented sub-items with active
  highlight, Back-to-site/Log-out/version at the bottom
- Replace PodcastTab state with adminView sub-views (podcast:current-series
  etc.) eliminating the secondary tab bar inside the panel
- Extend AdminView type with all new colon-separated sub-view values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 09:09:52 -04:00
nmemmertandClaude Sonnet 4.6 a072076818 Rename Downloads→Resources throughout, fix audit bugs, add /subscribe to footer; v1.1.49
Publish Container / docker (push) Successful in 14m2s
- Rename all user-facing "Downloads" text to "Resources" (nav, page eyebrows, buttons, admin labels, page title, content defaults)
- Add usePageMeta to ContactPage, EpisodeDetailPage, DownloadDetailPage, StartHerePage for correct social share previews
- Wire contactEyebrow and aboutShowEyebrow fields to the UI (were editable in admin but never rendered)
- Wrap StartHerePage in full site chrome (header, search, footer); fix Step 2 link from /#qa to /questions
- Fix admin "View All Contacts" link (was 404); now navigates to Contact admin panel
- Remove broken "Previous Studies" jump nav entry (no matching anchor)
- Remove /studys typo route
- Make dashboard "Top Resource Downloads" stat dynamic instead of hardcoded to titus-study key
- Add Newsletter link (/subscribe) to footer legal nav
- Fix server.js startup log ("Portfolio app" → "Siteforge")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 08:37:23 -04:00
nmemmertandClaude Sonnet 4.6 0d2e68ae11 Fix Ko-fi support section to match dark site theme; v1.1.48
Publish Container / docker (push) Successful in 13m37s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-02 12:03:16 -04:00
nmemmertandClaude Sonnet 4.6 07b2dc843f Add Ko-fi support section with QR code to About page; v1.1.47
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-02 11:58:34 -04:00
nmemmertandClaude Sonnet 4.6 d2829e610e Add Ko-fi integration: footer link, floating widget, and admin field; v1.1.46
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-02 11:57:09 -04:00
nmemmertandClaude Sonnet 4.6 0d830d6e79 Remove dead admin routes and broken UI elements; v1.1.45
Publish Container / docker (push) Successful in 14m54s
- Deleted ~24 unused API routes from contact.js (email inbox/CRM feature
  never wired to frontend): reply, compose, draft, broadcast, webhooks,
  audit-log, contact CRUD, merge, import, and health endpoints
- Removed dead imports from contact.js (config, data, email modules)
- Removed broken /email Link from admin dashboard Inbox card
- Cleaned up AdminView type: removed unreachable 'current-series' and
  'episode-highlights' top-level values (only valid as podcast sub-tabs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 12:48:24 -04:00
nmemmertandClaude Sonnet 4.6 fc63a34ef2 Shrink download detail cover image to 180px wide; v1.1.44
Publish Container / docker (push) Successful in 14m28s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-27 13:40:48 -04:00
nmemmertandClaude Sonnet 4.6 943674e7f3 Fix server sanitizer to persist appleBooksUrl/Label on customLinks; v1.1.43
Publish Container / docker (push) Successful in 15m7s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-27 11:56:53 -04:00
nmemmertandClaude Sonnet 4.6 1db0a919c2 Add optional Apple Books button to downloads and book cards; v1.1.42
Publish Container / docker (push) Successful in 15m16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-27 11:34:55 -04:00
nmemmertandClaude Sonnet 4.6 1097cb425c Fix docker-compose to use Gitea registry image; v1.1.41
Publish Container / docker (push) Successful in 15m13s
docker-compose.yml was still pointing to the old GHCR image
(ghcr.io/nmemmert/siteforge:latest) after CI was switched to push
to git.necloud.us/nate/siteforge:latest, so servers were pulling
a stale build that predated the download/header changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 13:53:37 -04:00
nmemmertandClaude Sonnet 4.6 75c82dc8fc Fix CI: use REGISTRY_USER and REGISTRY_TOKEN secrets for Gitea login
Publish Container / docker (push) Successful in 16m15s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 13:22:32 -04:00
nmemmertandClaude Sonnet 4.6 53405dad9b Fix CI: use email address as Gitea registry username
Publish Container / docker (push) Failing after 11m6s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 12:59:18 -04:00
nmemmertandClaude Sonnet 4.6 2d1c28b467 Fix CI: use Gitea username 'nate' for container registry login
Publish Container / docker (push) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 12:44:59 -04:00
nmemmertandClaude Sonnet 4.6 a4a1fdd891 Fix CI: hardcode Gitea username for container registry login
Publish Container / docker (push) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 12:26:59 -04:00
nmemmertandClaude Sonnet 4.6 6d4bcb2ae2 Fix CI: use github.actor context for Gitea registry login
Publish Container / docker (push) Failing after 11m20s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 12:24:36 -04:00
nmemmertandClaude Sonnet 4.6 293d9bee4e Switch CI to Gitea container registry; drop GHCR
Publish Container / docker (push) Failing after 10m54s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 12:11:09 -04:00
nmemmertandClaude Sonnet 4.6 0512d40fc6 Redesign resources page with PageBanner, featured download, and consistent page headers; v1.1.40
Publish Container / docker (push) Has been cancelled
- Replace hero study guide form with PageBanner + featured download card in library
- Add PageBanner component used consistently across all interior pages (Downloads, Episodes, About, Contact, Q&A, Finished Books)
- Remove duplicate headings/eyebrows that conflicted with banner titles on About, Contact, Q&A, Finished Books pages
- Compact download card sizing (padding, border-radius, image size)
- Add Books & Resources tabbed section with fallback to TruthForLife widget
- Fix server sanitizer to pass through 'resources' placement items without URL
- Add featured-badge and featured card styling for primary guide

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 11:03:43 -04:00
nmemmertandClaude Sonnet 4.6 0fdf7f0f50 v1.1.39
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 08:49:32 -04:00
nmemmertandClaude Sonnet 4.6 005da43738 Default book button label to 'Learn More' when using url instead of amazonUrl; v1.1.38
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 08:47:25 -04:00
nmemmertandClaude Sonnet 4.6 24d7fd7cec Fix book card button to render when url is set but amazonUrl is not; v1.1.38
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 08:42:09 -04:00
nmemmertandClaude Sonnet 4.6 889baf23ca Fix server sanitizer to allow books/recommended placements and linkUrl/linkLabel fields; v1.1.38
- Add 'books' and 'recommended' to the placement whitelist in sanitizeCustomLinks
- Allow items with no url when placement is books/recommended (they use amazonUrl)
- Preserve linkUrl and linkLabel through sanitizeCustomBlocks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 08:23:59 -04:00
nmemmertandClaude Sonnet 4.6 eb1d3ddd9c Add Books + editable Recommended Resources sections; improve content block links; v1.1.38
- CustomBlock: add linkUrl/linkLabel fields for a styled button below the body
- Body text auto-linkifies raw URLs into gold anchor tags
- New 'books' placement: Reading List grid on Downloads page (cover, desc, Amazon btn)
- New 'recommended' placement: editable Recommended Resources section on Downloads
  page; falls back to Truth For Life widget when empty
- Admin: Books and Recommended Resources panels added to Content nav

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-26 08:16:36 -04:00
nmemmertandClaude Sonnet 4.6 a35e04320a Remove production checklist and email/calendar/contacts app; v1.1.37
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-20 09:30:19 -04:00
nmemmertandClaude Sonnet 4.6 2ba9c9f7f4 Add Fetch from BSB button to admin lesson editor; v1.1.36
Adds a one-click "Fetch from BSB" button in the lesson passage text field
that calls the BSB API and overwrites the field with verse-numbered text.
Also fixes reference parsing to tolerate leading book abbreviations like
t:1-2 or col:1-2 by stripping letters before the chapter:verse pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-10 10:23:48 -04:00
nmemmertandClaude Sonnet 4.6 9e602e29ec Fetch BSB passage text from Bolls.life API in study lesson view; v1.1.35
Adds /api/bible-passage server route that proxies the Berean Standard Bible
from Bolls.life per-verse, with in-memory caching. Study lesson scripture
block now displays live BSB text instead of static passageText. Adds
bibleBook field to StudyProgram so admins can set the book name explicitly
when the study slug doesn't match (e.g. a study titled "ephesians-part-2").

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-10 10:12:25 -04:00
nmemmertandClaude Sonnet 4.6 6acc3f1d04 Analytics: 3-tab layout + fix geographic data loss; v1.1.34
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>
2026-08-10 09:22:47 -04:00
nmemmertandClaude Sonnet 4.6 bf3154378b Track all link clicks (internal + external) in analytics; v1.1.33
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>
2026-08-10 09:11:53 -04:00
nmemmertandClaude Sonnet 4.6 abb4c87c41 Update runtime data: analytics events, episode plays, study user state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-10 09:03:00 -04:00
nmemmertandClaude Sonnet 4.6 441b907ae1 Fancy 404 page with book icon, action buttons, and broken-link report form; v1.1.32
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-10 08:56:10 -04:00
nmemmertandClaude Sonnet 4.6 76239ee83b Fix redirects broken by service worker navigate fallback; v1.1.31
The SW's navigateFallback was using a denylist, so any path not explicitly
excluded (e.g. /spotify, /apple, /amazon, custom admin redirects) got served
the cached index.html instead of reaching the server's redirect handler.

Switch to navigateFallbackAllowlist: the SW only intercepts known SPA routes;
all other paths hit the network so server-side 301/302 redirects work correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-10 08:49:15 -04:00
nmemmert c56e468115 Revert "Add student birthday tracking with celebration banners and admin notification; v1.1.31"
This reverts commit c0a79b9ed0.
2026-08-10 08:43:53 -04:00
nmemmertandClaude Sonnet 4.6 c0a79b9ed0 Add student birthday tracking with celebration banners and admin notification; v1.1.31
- Admin can set birthday (month/day) per student in Study Users panel
- Summer birthdays (Jun–Aug) prompt admin to set an alternate school-year date
- 🎂 badge on student card header during birthday week
- Dismissible happy birthday banner shown to student on their study hub
- Admin receives email notification on the celebration day, once per year

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-10 08:42:52 -04:00