The admin API creates subscribers but doesn't trigger opt-in emails.
Use /api/public/subscription instead — no auth needed, triggers
confirmation email automatically. Removes API token from env vars.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire subscribe forms to Listmonk API (token auth) with double opt-in,
falling back to email notifications when Listmonk is not configured.
SMTP via Mailcow (orders@katheryntrenshaw.com).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Contact form POSTs to /api/contact → emails Katheryn with reply-to sender
- Subscribe page and footer form POST to /api/subscribe → welcome email
to subscriber + notification to Katheryn
- Loading states, error handling, and disabled buttons while sending
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updated SMTP_HOST from mx.jeffemmett.com to mail.rmail.online
using shared service credentials for orders@katheryntrenshaw.com.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Merge two-bar header into single nav bar: remove social icons top bar,
move search + cart icons next to hamburger menu
- Add /subscribe page with email signup form
- Fix blog images by restoring Squarespace CDN in next.config remotePatterns
- Compress footer into 4-column link layout, remove logo block
- Update Cynthia Trenshaw Poetry link to cynthiatrenshaw.com
- Add local images for homepage, about, IYOS, and artwork pages
- Add /wisdom-words page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All store items are priced in GBP (filtered by price_gbp > 0).
Hardcode GBP currency throughout the checkout and order creation
flow to prevent currency mismatches between the PayPal SDK, order
items, and purchase unit amounts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The env_file is only available at runtime, not during docker build.
Build args need explicit values in docker-compose.yml for NEXT_PUBLIC
vars to be inlined into the client bundle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NEXT_PUBLIC_* vars must be available at build time to be inlined
into the client JS bundle. Added NEXT_PUBLIC_PAYPAL_CLIENT_ID as
a Docker build arg so the PayPal checkout button works in the browser.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `search: ''` setting in localPatterns was blocking image URLs
that include query parameters (width, quality, format). Removing
this restriction allows the /api/assets proxy URLs to pass through
Next.js image optimization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Setting a GBP price in Directus is the intentional signal that an
artwork is for sale. USD-only items (264 with images) are imported
historical catalog data not meant for the store. The store now shows
the 19 artworks that have name + image + GBP price set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Artworks now automatically appear in the store when their required
fields are populated in Directus (name, image, and at least one price
in GBP or USD). Changing status to 'sold' moves them to the sold
section; 'draft' or 'archived' removes them entirely. No manual
"add to store" step needed — field completeness drives visibility.
Also ensures related works on detail pages only show store-ready items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The store was fetching all 2,324 artworks and filtering client-side,
resulting in 656 items shown (including imported catalog entries with
only USD prices and no images). Now filters at the Directus API level
for artworks with price_gbp set, showing only the 19 actual store items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Configure images.localPatterns to allow /api/assets/** with query strings
- Add /app/.next/cache as tmpfs for image optimization cache (read-only container)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Next.js image optimizer blocks internal Docker URLs due to SSRF
protection (private IP resolution). Instead, proxy assets through
/api/assets/[id] which fetches from internal Directus URL server-side.
This bypasses both Cloudflare Access and SSRF protection since the
<Image> src is a same-origin path, not an external URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NEXT_PUBLIC_ vars need to be set both at build time (client bundle)
AND at runtime (server-side rendering in standalone mode).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Directus CMS is behind Cloudflare Access, which blocks the Next.js
image optimizer from fetching assets. Route image requests through the
internal Docker network (http://katheryn-cms:8055) instead.
- Add NEXT_PUBLIC_DIRECTUS_ASSET_URL/TOKEN env vars for client components
- Use DIRECTUS_INTERNAL_URL for server-side Directus API calls
- Convert store detail page from client to server component (data
fetching now happens server-side, not in browser)
- Add internal Docker hostname to Next.js remotePatterns
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add @paypal/react-paypal-js, nodemailer, @types/nodemailer to package.json
- Add Order and OrderItem TypeScript interfaces to directus.ts
- These were part of the checkout implementation but missed in the initial commit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrated from /root/.katheryn_credentials to the new organized
/opt/secrets/ directory structure. Symlinks ensure backward compat.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use env_file referencing /root/.katheryn_credentials for all secrets
- Remove inline secrets from environment block (PayPal, SMTP, store token)
- Add directus_katheryn-internal network for internal CMS access
- Add container security: read_only, cap_drop ALL, no-new-privileges
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create shipping.ts with flat-rate tiers: UK £10, Europe £25, International £40
- Integrate shipping cost into PayPal order breakdown (item_total + shipping)
- Add server-side shipping calculation in order creation API (prevents tampering)
- Update checkout page to show real-time shipping cost based on country selection
- Add subtotal/shipping/total breakdown to order confirmation page
- Add order confirmation emails via SMTP (customer + Katheryn notification)
- Include shipping breakdown in email templates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add removeLeadingImage() to strip the first image from content when it matches
the featured image, preventing duplication on blog post pages
- Handles figure wraps, linked images, and standalone img tags
- Featured image now appears only once (in the header), not repeated in content
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add convertCaptionShortcodes() to transform [caption]...[/caption] into
<figure class="wp-caption"> with <figcaption>
- Handle both plain images and linked images (wrapped in <a> tags)
- Add CSS styling for figure captions in prose content
- All 17 posts with captions now display properly
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add decodeExcerptEntities() to handle —, …, smart quotes, etc.
- Add removeShortcodes() to strip [caption] and other WordPress shortcodes
- Regenerate blog-posts.json and pages.json with cleaned excerpts
Fixes issue where [caption] tags and raw HTML entities appeared in blog listings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Embeds the Breaking The Silence film (https://youtu.be/hpzvEQs9vEk)
in a dedicated video section on the page.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Passionate Presence Centre page with Living Awake series
- Add Consulting page with approach and testimonials
- Add 1-to-1 Sessions page with session types and areas of focus
- Update navigation to link to new pages
- Add images for all three pages
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Homepage, events, and store pages now use force-dynamic
- This ensures fresh data is fetched at runtime
- Fixes build-time Directus fetch failures
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Import getAssetUrl function for Directus asset URLs
- Update homepage events section to display thumbnail images
- Images link to events page and show hover effect
- Fallback gracefully when no image is available
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add auth token to asset URLs for proper image access
- Map Directus artwork fields (name->title, price_gbp/usd->price, notes->description)
- Add currency field (GBP/USD) based on available price
- Fetch upcoming events dynamically on homepage
- Prefer artworks with images for featured display
- Add proper error handling for artwork lookups
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Keep social links, logo, and copyright in footer.
Newsletter signup is already available in /subscribe page.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add external link to cynthia-staging.jeffemmett.com
- Support external links in both desktop and mobile nav
- Opens in new tab with proper rel attributes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add full-screen portrait hero with testimonial quote overlay
- Add social icons on hero section
- Add scroll indicator animation
- Add YouTube video background for In Your Own Skin section
- Add book section with Amazon embed
- Add Amazon images domain to Next.js config
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add full biographical content from Squarespace export
- Include artist statement and teacher bio sections
- Add media banner and major projects showcase
- Use Squarespace CDN images for portraits
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Parse Squarespace WordPress XML export and extract all blog posts
- Create blog listing page with posts grouped by year
- Create individual blog post pages with full content
- Add social sharing, prev/next navigation, related posts
- Configure Next.js for Squarespace CDN images
- Add Tailwind typography plugin for prose styling
- Include 357 artwork thumbnails from Airtable
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>