Commit Graph
162 Commits
Author SHA1 Message Date
nmemmertandClaude Sonnet 5.5 1eb0dc1296 Admin: split long pages into tabs instead of scrolling; v2.9.2
Publish Container / build (push) Successful in 16m23s
- Shared tab bar; chosen tab is remembered per page
- Current Series: Main Series / Second Series / Launch: Main / Launch: Second
- Resources, Footer & Global, SEO, Legal, Start Here, Custom Links: former "Jump to" scroll links are now tabs
- Dashboard: Overview / Recent Activity / Study Users
- Studies editor: Lessons / Study Settings

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 10:49:41 -04:00
nmemmertandClaude Sonnet 5.5 652b003bd9 Episode highlights per series: tag by lane, group on Episodes page, move to Finished Book when series ends; v2.9.1
Publish Container / build (push) Successful in 15m21s
- Highlights get a Series field (Lane A / Lane B / General); legacy highlights count as Lane A
- Episodes page groups highlights under each series' title
- Ending a series (scheduled or manual) moves only that series' highlights onto its Finished Book page; other lane and General stay
- Archived highlights keep working: detail pages, sitemap, scripture index and search include them

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 10:46:33 -04:00
nmemmertandClaude Sonnet 5.5 ddcaec0ddf Two concurrent series lanes, Complete studies, self-paced finished books; v2.9.0
Publish Container / build (push) Successful in 17m50s
- Study status 'complete' (manual or auto when all lessons released); stays enrollable and self-paced (no sequential lock, Resume button, Finished Studies hub section)
- Studies get lane (A/B) and expected start date; planned studies show "Starts <date>"; hub shows next lesson date instead of hardcoded pace
- Second concurrent series (homepage card, admin panel) with its own scheduled launch; transitions are per lane and flip the ending study to Complete and the incoming study to Active
- Finished books link to their study: "Study This Book" on playlist page, audio-only link from hub (playlist stays public, no account)
- Admin: bulk release scheduler (every N days), same-day collision warning, linked-study selects
- Weekly digest no longer dumps a finished study's backlog on late enrollees
- CI: build and tag the container as :beta on pushes to the beta branch

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 10:14:02 -04:00
nmemmertandClaude Sonnet 5.5 2d8e4be5ea Study hub: show available vs coming-soon study counts instead of total tracks; v2.8.5
Publish Container / build (push) Successful in 14m52s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 15:51:48 -04:00
nmemmertandClaude Sonnet 5.5 cd40ecc207 Add Active/Planned status badges, dim planned studies, sort active first in track lists; v2.8.4
Publish Container / build (push) Successful in 16m49s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 14:57:53 -04:00
nmemmertandClaude Sonnet 5.5 db1737baf3 Hide Enroll for planned studies (Coming soon) and reject planned enrollments server-side; v2.8.3
Publish Container / build (push) Has been cancelled
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 14:56:17 -04:00
nmemmertandClaude Sonnet 5.5 b1695b2b59 Add release date and episode # columns to admin lesson list with inline date editing; use local time for release dates; v2.8.2
Publish Container / build (push) Successful in 18m28s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 14:46:25 -04:00
nmemmertandClaude Sonnet 5.5 0ff0b32b97 Fix scheduled series launch: read schedule from draft, apply to draft+published, check every minute, local-time picker; v2.8.1
Publish Container / build (push) Successful in 16m22s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 10:12:05 -04:00
nmemmertandClaude Sonnet 5.5 134a2c403e Expand admin dashboard with trends, health, study and email insights; v2.8.0
Publish Container / build (push) Successful in 15m53s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 10:19:25 -04:00
nmemmertandClaude Sonnet 5.5 dad0e5691e Retry Resend calls on 429 and throttle Newsletter Archive lookups; v2.7.1
Publish Container / build (push) Successful in 15m57s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 09:14:49 -04:00
nmemmertandClaude Sonnet 5.5 66cdb9c4c9 Send app broadcasts as Resend Broadcasts; add selective import from Resend; v2.7.0
Publish Container / build (push) Successful in 15m22s
- Broadcast Email creates and sends a Resend Broadcast to the Resend segment (unsubscribe handling, reports, New Episode topic, scheduling, test send)
- Subscribers: import chosen Resend-only contacts into the app list

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 08:54:23 -04:00
nmemmertandClaude Sonnet 5.5 af967d0049 Add Compare with Resend panel to Subscribers; stop resubscribing opted-out contacts; v2.6.1
Publish Container / build (push) Successful in 16m27s
- Subscribers: compare in-app list with Resend audience/segment, with one-click fixes
- syncContactToResend no longer resets unsubscribed on existing contacts

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 08:29:56 -04:00
nmemmertandClaude Sonnet 5.5 e74bd1df5d Upgrade Asset Manager and Inbox; load Resend templates in Broadcast; v2.5.0
Publish Container / build (push) Successful in 16m39s
- Asset Manager: search/filter/sort, list+grid views, rename, move between R2 folders, tag chips, bulk actions, used-in counts; fix R2 delete lookup
- Inbox: readable wrapped rows with subject/preview, real read state, type filters, bulk actions, mojibake repair
- Broadcast: pick a Resend template to fill subject and body

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 07:45:50 -04:00
nmemmertandClaude Sonnet 5.5 4931bfab4d Add podcast image picker to the Email Templates welcome card; v2.3.2
Publish Container / build (push) Failing after 27s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 07:57:33 -04:00
nmemmertandClaude Sonnet 5 abfbb1981b Fix newsletter issue pages rendering left-aligned on white; v2.3.1
Publish Container / build (push) Successful in 17m23s
The Resend email HTML uses align="left" on its outer wrapper table and a
white background, both fine inside a narrow inbox pane but broken in a
full-width browser tab: the content stuck to the left edge with a large
white field to its right. Override with CSS that centers the content and
matches the site's dark background.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 12:50:47 -04:00
nmemmertandClaude Sonnet 5 fe4c09f8a3 Add a newsletter archive so past issues can be shared and found; v2.3.0
Publish Container / build (push) Successful in 20m51s
Web-hosted copies of episode newsletters, published from Resend broadcasts
tagged with the "New Episode" topic. Gives Facebook (and anyone else) a
proper link to share instead of raw HTML email, and makes issues findable
via site search and Google.

- Admin "Newsletter Archive" tab lists sent Resend broadcasts, flags ones
  tagged "New Episode", and publishes them with one click (pulls the HTML
  straight from Resend's API)
- Public /newsletter archive index and /newsletter/:slug issue pages,
  server-rendered with per-issue Open Graph tags for social previews
- Unsubscribe merge-tag stripped from the web copy; archive + subscribe
  links appended
- Issues added to sitemap.xml and the on-site search index

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 12:17:13 -04:00
nmemmertandClaude Sonnet 5 d1a68754a1 Fix analytics data-loss bugs and clean up the admin dashboard; v2.2.4
Publish Container / build (push) Successful in 16m3s
- Self-heal consent desync: if the server's consent cookie and the
  client's localStorage flag fall out of sync, the client now notices
  and stops silently sending events into a void.
- Stop double-counting hard page loads in hit/visitor stats.
- Flush pending disk writes on SIGTERM/SIGINT so redeploys don't drop
  the last few events.
- Log rate-limiter drops once per window (diagnoses proxy misconfig).
- Widen the bot-detection allowlist to avoid flagging real browsers
  (Chrome-iOS, Samsung Browser, etc.) as bots.
- Stop merging distinct visitors who share an IP when a real session
  cookie is already present — only fall back to IP correlation when no
  cookie was sent at all.
- Include analyticsEvents/episodePlays in backup snapshots and restore
  them, so a restore no longer wipes engagement analytics.
- Regroup admin dashboard KPI cards by time scope (range vs all-time)
  and remove a duplicated Top Countries/States block.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 14:06:02 -04:00
nmemmertandClaude Sonnet 5 8496e227b3 Stop showing email-derived names in student discussions; v2.2.1
Publish Container / build (push) Successful in 14m56s
Community posts/replies fell back to the local part of the student's email
(e.g. "student.test" from student.test@versebyverse.local) whenever they
hadn't set a display name. The per-lesson discussion comments were worse —
they fell back to the FULL email address, and that endpoint has no auth
requirement, so it was publicly readable.

Adds getCommunityDisplayName() in study-helpers.js: uses the student's
display name when set, otherwise assigns a stable "Student XXXX" handle
derived from their account id — never their email. Both the community board
and lesson comments now resolve names live from the author's current
account at read time (falling back to the stored snapshot only if the
account was deleted), so this also fixes any already-posted content
without a data migration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 11:15:54 -04:00
nmemmertandClaude Sonnet 5 3d3c7175a1 Add a study-picker discussion hub and require sign-in to view it; v2.2.0
Publish Container / build (push) Successful in 16m31s
The "Discussion" nav link previously jumped straight into the featured
study's community board. Adds /study/discussion as a proper landing page
that lists every study track so students pick which one to discuss, then
links into /study/:slug/community from there. Both pages now require an
authenticated session to view at all (previously the per-study community
page rendered its shell to logged-out visitors, only gating the post feed
itself).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 10:19:20 -04:00
nmemmertandClaude Sonnet 5 c92bb2189a Fix 15 pre-existing ESLint issues in studyPages.tsx; v2.1.1
Publish Container / build (push) Successful in 16m13s
Fixes two real bugs: useEffect and usePageMeta were called after an early
"study not found" return, violating React's rules of hooks (could corrupt
hook state if a study slug resolves inconsistently across renders). Also
removes dead code (unused NOTE_ANCHORS array, an unused state pair, a stale
eslint-disable comment), fixes three return-in-finally statements that could
mask exceptions, adds the missing study?.slug effect dependency, and cleans
up an unnecessary regex escape and an empty catch block.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 09:26:03 -04:00
nmemmertandClaude Sonnet 5 841e754e16 Add student account self-service security features and discoverability fixes; v2.1.0
Adds forgot/reset password, signup email verification, sign-out-of-all-devices,
note-usage and certificate display in account settings, and password-changed
security emails. Fixes study sessions being lost on server restart by
persisting them to disk, and fixes sanitizeStudyUsers silently dropping the
new pending-token fields on load. Also surfaces the lesson discussion board
in the main site nav, trims the sign-in page for returning users, and renames
colossiansStudy.tsx/colossiansStudyData.ts to studyPages.tsx/legacyColossiansData.ts
now that the study system covers more than Colossians.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 09:21:54 -04:00
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 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