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>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-08-27 11:34:55 -04:00
1 parent 1097cb425c
commit 1db0a919c2
5 files changed
+56 -2

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
{
"users": {},
"updatedAt": "2026-08-10T14:22:31.192Z"
"updatedAt": "2026-08-26T15:41:55.120Z"
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "siteforge",
"private": true,
"version": "1.1.41",
"version": "1.1.42",
"type": "module",
"scripts": {
"dev": "vite",
+32
View File
@@ -3558,6 +3558,14 @@ async function handleAssetUpload(event: ChangeEvent<HTMLInputElement>) {
<label htmlFor="resources-study-guide-amazon-label">Amazon Button Label</label>
<input id="resources-study-guide-amazon-label" type="text" value={form.studyGuideAmazonButtonLabel} placeholder="Get it on Amazon" onChange={e => handleChange('studyGuideAmazonButtonLabel', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor="resources-study-guide-apple-books-url">Apple Books URL (optional)</label>
<input id="resources-study-guide-apple-books-url" type="url" value={form.studyGuideAppleBooksUrl} placeholder="https://books.apple.com/..." onChange={e => handleChange('studyGuideAppleBooksUrl', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor="resources-study-guide-apple-books-label">Apple Books Button Label</label>
<input id="resources-study-guide-apple-books-label" type="text" value={form.studyGuideAppleBooksLabel} placeholder="Get it on Apple Books" onChange={e => handleChange('studyGuideAppleBooksLabel', e.target.value)} />
</div>
</div>
</div>
@@ -3594,6 +3602,14 @@ async function handleAssetUpload(event: ChangeEvent<HTMLInputElement>) {
<label htmlFor={`resource-amazon-label-${link.id}`}>Amazon Button Label</label>
<input id={`resource-amazon-label-${link.id}`} type="text" value={link.amazonLabel ?? ''} placeholder="Get it on Amazon" onChange={e => updateLink(link.id, 'amazonLabel', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`resource-apple-books-url-${link.id}`}>Apple Books URL (optional)</label>
<input id={`resource-apple-books-url-${link.id}`} type="url" value={link.appleBooksUrl ?? ''} placeholder="https://books.apple.com/..." onChange={e => updateLink(link.id, 'appleBooksUrl', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`resource-apple-books-label-${link.id}`}>Apple Books Button Label</label>
<input id={`resource-apple-books-label-${link.id}`} type="text" value={link.appleBooksLabel ?? ''} placeholder="Get it on Apple Books" onChange={e => updateLink(link.id, 'appleBooksLabel', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`resource-image-${link.id}`}>Image URL</label>
<input id={`resource-image-${link.id}`} type="text" value={link.imageUrl ?? ''} placeholder="/uploads/example.png" onChange={e => updateLink(link.id, 'imageUrl', e.target.value)} />
@@ -3799,6 +3815,14 @@ async function handleAssetUpload(event: ChangeEvent<HTMLInputElement>) {
<label htmlFor={`resources-link-amazon-label-${link.id}`}>Amazon Button Label</label>
<input id={`resources-link-amazon-label-${link.id}`} type="text" value={link.amazonLabel ?? ''} placeholder="Get it on Amazon" onChange={e => updateLink(link.id, 'amazonLabel', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`resources-link-apple-books-url-${link.id}`}>Apple Books URL (optional)</label>
<input id={`resources-link-apple-books-url-${link.id}`} type="url" value={link.appleBooksUrl ?? ''} placeholder="https://books.apple.com/..." onChange={e => updateLink(link.id, 'appleBooksUrl', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`resources-link-apple-books-label-${link.id}`}>Apple Books Button Label</label>
<input id={`resources-link-apple-books-label-${link.id}`} type="text" value={link.appleBooksLabel ?? ''} placeholder="Get it on Apple Books" onChange={e => updateLink(link.id, 'appleBooksLabel', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`resources-link-tags-${link.id}`}>Tags</label>
<input id={`resources-link-tags-${link.id}`} type="text" value={(link.tags ?? []).join(', ')} placeholder="worksheet, study, pdf" onChange={e => updateLink(link.id, 'tags', e.target.value.split(',').map(tag => tag.trim()).filter(Boolean))} />
@@ -3904,6 +3928,14 @@ async function handleAssetUpload(event: ChangeEvent<HTMLInputElement>) {
<label htmlFor={`book-amazon-label-${book.id}`}>Amazon Button Label</label>
<input id={`book-amazon-label-${book.id}`} type="text" value={book.amazonLabel ?? ''} placeholder="View on Amazon" onChange={e => updateLink(book.id, 'amazonLabel', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`book-apple-books-url-${book.id}`}>Apple Books URL (optional)</label>
<input id={`book-apple-books-url-${book.id}`} type="url" value={book.appleBooksUrl ?? ''} placeholder="https://books.apple.com/..." onChange={e => updateLink(book.id, 'appleBooksUrl', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`book-apple-books-label-${book.id}`}>Apple Books Button Label</label>
<input id={`book-apple-books-label-${book.id}`} type="text" value={book.appleBooksLabel ?? ''} placeholder="Get it on Apple Books" onChange={e => updateLink(book.id, 'appleBooksLabel', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`book-url-${book.id}`}>Link URL <span className="admin-field-optional">(used if no Amazon URL is set)</span></label>
<input id={`book-url-${book.id}`} type="url" value={book.url ?? ''} placeholder="https://..." onChange={e => updateLink(book.id, 'url', e.target.value)} />
+16
View File
@@ -267,6 +267,8 @@ interface DownloadPageResource {
resourceId: string
amazonUrl?: string
amazonLabel?: string
appleBooksUrl?: string
appleBooksLabel?: string
isPrimaryGuide?: boolean
}
@@ -284,6 +286,8 @@ function resolveDownloadPageResource(content: SiteContent, pageId: string | unde
resourceId: 'primary-guide',
amazonUrl: content.studyGuideUrl,
amazonLabel: content.studyGuideAmazonButtonLabel,
appleBooksUrl: content.studyGuideAppleBooksUrl,
appleBooksLabel: content.studyGuideAppleBooksLabel,
isPrimaryGuide: true,
}
}
@@ -303,6 +307,8 @@ function resolveDownloadPageResource(content: SiteContent, pageId: string | unde
resourceId: buildCustomResourceDownloadId(resource.id),
amazonUrl: resource.amazonUrl,
amazonLabel: resource.amazonLabel,
appleBooksUrl: resource.appleBooksUrl,
appleBooksLabel: resource.appleBooksLabel,
}
}
@@ -854,6 +860,11 @@ function DownloadDetailPage({ content }: { content: SiteContent }) {
{resource.amazonLabel || 'Get it on Amazon'}
</a>
)}
{resource.appleBooksUrl && (
<a href={resource.appleBooksUrl} target="_blank" rel="noreferrer" className="btn-secondary">
{resource.appleBooksLabel || 'Get it on Apple Books'}
</a>
)}
<Link to="/resources" className="btn-secondary">Back to Downloads</Link>
</div>
@@ -1804,6 +1815,11 @@ function BooksAndResourcesSection({ content }: { content: SiteContent }) {
{book.amazonUrl ? (book.amazonLabel || 'View on Amazon') : (book.amazonLabel || 'Learn More')}
</a>
)}
{book.appleBooksUrl && (
<a href={book.appleBooksUrl} target="_blank" rel="noopener noreferrer" className="btn-secondary book-card-btn">
{book.appleBooksLabel || 'Get it on Apple Books'}
</a>
)}
</div>
</div>
))}
+6
View File
@@ -31,6 +31,8 @@ export interface CustomLink {
description?: string
amazonUrl?: string
amazonLabel?: string
appleBooksUrl?: string
appleBooksLabel?: string
tags?: string[]
}
@@ -158,6 +160,8 @@ export interface SiteContent {
studyGuideDownloadUrl: string
studyGuideUrl: string
studyGuideAmazonButtonLabel: string
studyGuideAppleBooksUrl: string
studyGuideAppleBooksLabel: string
// ── Colossians Study ──
studies: StudyProgram[]
colossiansStudySections: ColossiansStudySection[]
@@ -337,6 +341,8 @@ export const DEFAULTS: SiteContent = {
studyGuideDownloadUrl: '',
studyGuideUrl: 'https://a.co/d/01sG2tOJ',
studyGuideAmazonButtonLabel: 'Get it on Amazon',
studyGuideAppleBooksUrl: '',
studyGuideAppleBooksLabel: 'Get it on Apple Books',
studies: [
{
id: 'study-colossians',