Fix homepage external link placement and publish sanitization

This commit is contained in:
nmemmert
2026-06-01 13:31:26 -04:00
parent 1d536827e3
commit 33ac2fb5d0
8 changed files with 311 additions and 66 deletions
+115 -3
View File
@@ -1613,6 +1613,8 @@ export default function AdminPage({ content, onSave, onLogout }: Props) {
function getCustomLinkPlacementLabel(placement: CustomLink['placement']) {
if (placement === 'platforms') return 'Homepage listen/platform links'
if (placement === 'footer') return 'Footer navigation links'
if (placement === 'otherSites') return 'Other site links in footer dropdown'
if (placement === 'externalSites') return 'Homepage external site links'
return 'Downloads page (More Resources)'
}
@@ -1755,6 +1757,26 @@ export default function AdminPage({ content, onSave, onLogout }: Props) {
}))
}
function addOtherSiteLink() {
setForm(f => ({
...f,
customLinks: [
...(f.customLinks ?? []),
{ id: Date.now().toString(36), label: '', url: '', imageUrl: '', placement: 'otherSites' as const },
],
}))
}
function addExternalSiteLink() {
setForm(f => ({
...f,
customLinks: [
...(f.customLinks ?? []),
{ id: Date.now().toString(36), label: '', url: '', imageUrl: '', description: '', placement: 'externalSites' as const },
],
}))
}
function addResource() {
setForm(f => ({
...f,
@@ -3781,14 +3803,61 @@ export default function AdminPage({ content, onSave, onLogout }: Props) {
<p>Add links to show in platform buttons, footer navigation, or the More Resources download library.</p>
</div>
<div className="admin-panel-subhead">
<h3>Homepage External Links</h3>
<p>Links shown on the homepage, separate from the footer.</p>
</div>
{(form.customLinks ?? []).filter(link => link.placement === 'externalSites').length === 0 && (
<p className="admin-stats-note">No homepage external links yet.</p>
)}
{(form.customLinks ?? []).filter(link => link.placement === 'externalSites').map(link => (
<div key={link.id} className="admin-array-row">
<div className="admin-array-fields">
<div className="admin-field">
<label htmlFor={`external-site-link-label-${link.id}`}>Label</label>
<input id={`external-site-link-label-${link.id}`} type="text" value={link.label} placeholder="e.g. Bible study partner" onChange={e => updateLink(link.id, 'label', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`external-site-link-url-${link.id}`}>URL</label>
<input id={`external-site-link-url-${link.id}`} type="url" value={link.url} placeholder="https://..." onChange={e => updateLink(link.id, 'url', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`external-site-link-description-${link.id}`}>Description</label>
<textarea id={`external-site-link-description-${link.id}`} rows={3} value={link.description ?? ''} placeholder="Short description shown on the homepage" onChange={e => updateLink(link.id, 'description', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`external-site-link-image-${link.id}`}>Image URL</label>
<input id={`external-site-link-image-${link.id}`} type="text" value={link.imageUrl ?? ''} placeholder="/uploads/example.png" onChange={e => updateLink(link.id, 'imageUrl', e.target.value)} />
{renderImageAssetSelector(link.imageUrl, value => updateLink(link.id, 'imageUrl', value), `external-site-link-image-${link.id}-asset`)}
{renderImagePreview(link.imageUrl, `${link.label || 'External site link'} image preview`)}
</div>
<div className="admin-field">
<label htmlFor={`external-site-link-tags-${link.id}`}>Tags</label>
<input id={`external-site-link-tags-${link.id}`} type="text" value={(link.tags ?? []).join(', ')} placeholder="external, homepage, ministry" onChange={e => updateLink(link.id, 'tags', e.target.value.split(',').map(tag => tag.trim()).filter(Boolean))} />
</div>
<div className="admin-field">
<label>Quick Action</label>
<button type="button" className="btn-admin-apply" onClick={() => updateLink(link.id, 'placement', 'footer')}>Move to Footer Nav</button>
</div>
<div className="admin-field">
<label>Shows On</label>
<p className="admin-stats-note">{getCustomLinkPlacementLabel(link.placement)}</p>
<a href={getCustomLinkPreviewPath(link.placement)} target="_blank" rel="noopener noreferrer" className="btn-admin-secondary">Preview Destination Page</a>
</div>
</div>
<button type="button" className="btn-admin-remove" onClick={() => removeLink(link.id)}>Remove</button>
</div>
))}
<button type="button" className="btn-admin-add" onClick={addExternalSiteLink}>+ Add Homepage External Link</button>
<div className="admin-panel-subhead">
<h3>Platform + Footer Links</h3>
<p>Links that appear on the listen buttons row or footer nav.</p>
</div>
{(form.customLinks ?? []).filter(link => link.placement !== 'resources').length === 0 && (
{(form.customLinks ?? []).filter(link => link.placement !== 'resources' && link.placement !== 'otherSites' && link.placement !== 'externalSites').length === 0 && (
<p className="admin-stats-note">No custom links yet.</p>
)}
{(form.customLinks ?? []).filter(link => link.placement !== 'resources').map(link => (
{(form.customLinks ?? []).filter(link => link.placement !== 'resources' && link.placement !== 'otherSites' && link.placement !== 'externalSites').map(link => (
<div key={link.id} className="admin-array-row">
<div className="admin-array-fields">
<div className="admin-field">
@@ -3830,7 +3899,50 @@ export default function AdminPage({ content, onSave, onLogout }: Props) {
<button type="button" className="btn-admin-remove" onClick={() => removeLink(link.id)}>Remove</button>
</div>
))}
<button type="button" className="btn-admin-add" onClick={addLink}>+ Add Link</button>
<button type="button" className="btn-admin-add" onClick={addLink}>+ Add Platform/Footer Link</button>
<div className="admin-panel-subhead">
<h3>Other Site Links</h3>
<p>External links you want to surface in the footer dropdown.</p>
</div>
{(form.customLinks ?? []).filter(link => link.placement === 'otherSites').length === 0 && (
<p className="admin-stats-note">No other site links yet.</p>
)}
{(form.customLinks ?? []).filter(link => link.placement === 'otherSites').map(link => (
<div key={link.id} className="admin-array-row">
<div className="admin-array-fields">
<div className="admin-field">
<label htmlFor={`other-site-link-label-${link.id}`}>Label</label>
<input id={`other-site-link-label-${link.id}`} type="text" value={link.label} placeholder="e.g. Recommended ministry" onChange={e => updateLink(link.id, 'label', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`other-site-link-url-${link.id}`}>URL</label>
<input id={`other-site-link-url-${link.id}`} type="url" value={link.url} placeholder="https://..." onChange={e => updateLink(link.id, 'url', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`other-site-link-image-${link.id}`}>Image URL</label>
<input id={`other-site-link-image-${link.id}`} type="text" value={link.imageUrl ?? ''} placeholder="/uploads/example.png" onChange={e => updateLink(link.id, 'imageUrl', e.target.value)} />
{renderImageAssetSelector(link.imageUrl, value => updateLink(link.id, 'imageUrl', value), `other-site-link-image-${link.id}-asset`)}
{renderImagePreview(link.imageUrl, `${link.label || 'Other site link'} image preview`)}
</div>
<div className="admin-field">
<label htmlFor={`other-site-link-tags-${link.id}`}>Tags</label>
<input id={`other-site-link-tags-${link.id}`} type="text" value={(link.tags ?? []).join(', ')} placeholder="external, ministry, study" onChange={e => updateLink(link.id, 'tags', e.target.value.split(',').map(tag => tag.trim()).filter(Boolean))} />
</div>
<div className="admin-field">
<label>Quick Action</label>
<button type="button" className="btn-admin-apply" onClick={() => updateLink(link.id, 'placement', 'footer')}>Move to Footer Nav</button>
</div>
<div className="admin-field">
<label>Shows On</label>
<p className="admin-stats-note">{getCustomLinkPlacementLabel(link.placement)}</p>
<a href={getCustomLinkPreviewPath(link.placement)} target="_blank" rel="noopener noreferrer" className="btn-admin-secondary">Preview Destination Page</a>
</div>
</div>
<button type="button" className="btn-admin-remove" onClick={() => removeLink(link.id)}>Remove</button>
</div>
))}
<button type="button" className="btn-admin-add" onClick={addOtherSiteLink}>+ Add Other Site Link</button>
<div className="admin-panel-subhead">
<h3>More Resources Links</h3>