Add clickable host bios linking to host detail pages on Nuggets; v2.0.21
Publish Container / docker (push) Has been cancelled
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>
This commit is contained in:
1 parent
4607532440
commit
b35605f411
4 files changed
+86
-5
No files matched your search
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "siteforge",
|
"name": "siteforge",
|
||||||
"version": "2.0.3",
|
"version": "2.0.20",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "siteforge",
|
"name": "siteforge",
|
||||||
"version": "2.0.3",
|
"version": "2.0.20",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "siteforge",
|
"name": "siteforge",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.0.20",
|
"version": "2.0.21",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
+18
@@ -9890,6 +9890,24 @@
|
|||||||
.nuggets-host-card-name { font-family: var(--brand-font-heading); font-size: 1.05rem; color: #e8d9b5; margin: 0; }
|
.nuggets-host-card-name { font-family: var(--brand-font-heading); font-size: 1.05rem; color: #e8d9b5; margin: 0; }
|
||||||
.nuggets-host-card-role { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--brand-gold); opacity: 0.85; margin: 0.2rem 0 0; }
|
.nuggets-host-card-role { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--brand-gold); opacity: 0.85; margin: 0.2rem 0 0; }
|
||||||
.nuggets-host-card-bio { font-size: 0.875rem; color: var(--brand-muted); line-height: 1.6; margin: 0.875rem 0 0; }
|
.nuggets-host-card-bio { font-size: 0.875rem; color: var(--brand-muted); line-height: 1.6; margin: 0.875rem 0 0; }
|
||||||
|
.nuggets-host-card--link { text-decoration: none; cursor: pointer; transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s; }
|
||||||
|
.nuggets-host-card--link:hover { border-color: rgba(201, 168, 76, 0.45); box-shadow: 0 4px 18px rgba(0,0,0,0.28); transform: translateY(-2px); }
|
||||||
|
.nuggets-host-card--link .nuggets-host-card-name { color: #e8d9b5; }
|
||||||
|
.nuggets-host-card-cta { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--brand-gold); margin: 0.9rem 0 0; opacity: 0.75; transition: opacity 0.15s; }
|
||||||
|
.nuggets-host-card--link:hover .nuggets-host-card-cta { opacity: 1; }
|
||||||
|
|
||||||
|
.nuggets-host-detail-layout { display: grid; grid-template-columns: 240px 1fr; gap: 3.5rem; align-items: start; padding-top: 0.5rem; }
|
||||||
|
.nuggets-host-detail-photo-col { display: flex; justify-content: center; }
|
||||||
|
.nuggets-host-detail-photo { width: 200px; height: 200px; object-fit: cover; border-radius: 50%; border: 3px solid rgba(201, 168, 76, 0.35); box-shadow: 0 6px 24px rgba(0,0,0,0.35); }
|
||||||
|
.nuggets-host-detail-bio { font-size: 1.05rem; color: var(--brand-muted); line-height: 1.7; margin: 0 0 1.5rem; }
|
||||||
|
.nuggets-host-detail-listen { margin-top: 1.5rem; }
|
||||||
|
.nuggets-host-detail-back { font-size: 0.875rem; color: var(--brand-gold); text-decoration: none; opacity: 0.8; transition: opacity 0.15s; }
|
||||||
|
.nuggets-host-detail-back:hover { opacity: 1; }
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.nuggets-host-detail-layout { grid-template-columns: 1fr; gap: 2rem; }
|
||||||
|
.nuggets-host-detail-photo { width: 140px; height: 140px; }
|
||||||
|
}
|
||||||
|
|
||||||
.nuggets-episodes-note { color: var(--brand-muted); margin-top: 1.5rem; }
|
.nuggets-episodes-note { color: var(--brand-muted); margin-top: 1.5rem; }
|
||||||
.nuggets-latest-episode { background: rgba(26, 26, 21, 0.9); border: 1px solid rgba(201, 168, 76, 0.25); border-radius: 12px; padding: 1.5rem 1.75rem; margin-top: 1.5rem; }
|
.nuggets-latest-episode { background: rgba(26, 26, 21, 0.9); border: 1px solid rgba(201, 168, 76, 0.25); border-radius: 12px; padding: 1.5rem 1.75rem; margin-top: 1.5rem; }
|
||||||
|
|||||||
+65
-2
@@ -1933,7 +1933,7 @@ function NuggetsPage({ content }: { content: SiteContent }) {
|
|||||||
<p className="eyebrow">Meet the Hosts</p>
|
<p className="eyebrow">Meet the Hosts</p>
|
||||||
<div className="nuggets-hosts-list">
|
<div className="nuggets-hosts-list">
|
||||||
{activeHosts.map(host => (
|
{activeHosts.map(host => (
|
||||||
<div key={host.id} className="nuggets-host-card">
|
<Link key={host.id} to={`/nuggets/hosts/${host.id}`} className="nuggets-host-card nuggets-host-card--link">
|
||||||
<div className="nuggets-host-card-header">
|
<div className="nuggets-host-card-header">
|
||||||
{host.imageUrl && <img src={host.imageUrl} alt={host.name} className="nuggets-host-card-img" loading="lazy" />}
|
{host.imageUrl && <img src={host.imageUrl} alt={host.name} className="nuggets-host-card-img" loading="lazy" />}
|
||||||
<div className="nuggets-host-card-meta">
|
<div className="nuggets-host-card-meta">
|
||||||
@@ -1942,7 +1942,8 @@ function NuggetsPage({ content }: { content: SiteContent }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{host.bio && <p className="nuggets-host-card-bio">{host.bio}</p>}
|
{host.bio && <p className="nuggets-host-card-bio">{host.bio}</p>}
|
||||||
</div>
|
<p className="nuggets-host-card-cta">View Profile →</p>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -1992,6 +1993,67 @@ function NuggetsPage({ content }: { content: SiteContent }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NuggetsHostDetailPage({ content }: { content: SiteContent }) {
|
||||||
|
const { hostId } = useParams<{ hostId: string }>()
|
||||||
|
const nuggets = content.nuggets ?? DEFAULTS.nuggets
|
||||||
|
const siteTitle = content.seo?.title || DEFAULTS.seo.title
|
||||||
|
const host = (nuggets.hosts ?? []).find(h => h.id === hostId && !h.archived)
|
||||||
|
|
||||||
|
usePageMeta(
|
||||||
|
host ? `${host.name} | ${nuggets.title} | ${siteTitle}` : `Host | ${nuggets.title} | ${siteTitle}`,
|
||||||
|
host?.bio || `Meet ${host?.name}, a host on ${nuggets.title}.`,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!host) {
|
||||||
|
return (
|
||||||
|
<div className="site">
|
||||||
|
<SiteHeader content={content} />
|
||||||
|
<SiteSearchBar content={content} />
|
||||||
|
<PageBanner eyebrow="Nuggets" title="Host Not Found" desc="This host profile doesn't exist or has been removed." />
|
||||||
|
<section className="section-nuggets-body" style={{ textAlign: 'center', paddingBottom: '4rem' }}>
|
||||||
|
<Link to="/nuggets" className="btn-secondary">← Back to Nuggets</Link>
|
||||||
|
</section>
|
||||||
|
<SiteFooter content={content} />
|
||||||
|
<AnalyticsConsentBanner content={content} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="site">
|
||||||
|
<SiteHeader content={content} />
|
||||||
|
<SiteSearchBar content={content} />
|
||||||
|
<PageBanner eyebrow={nuggets.title} title={host.name} desc={host.role} />
|
||||||
|
<section className="section-nuggets-body" aria-label={`${host.name} profile`}>
|
||||||
|
<div className="section-inner nuggets-host-detail-layout">
|
||||||
|
<div className="nuggets-host-detail-photo-col">
|
||||||
|
{host.imageUrl && (
|
||||||
|
<img src={host.imageUrl} alt={host.name} className="nuggets-host-detail-photo" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="nuggets-host-detail-body">
|
||||||
|
{host.bio && <p className="nuggets-host-detail-bio">{host.bio}</p>}
|
||||||
|
{(nuggets.spotifyUrl || nuggets.appleUrl) && (
|
||||||
|
<div className="nuggets-host-detail-listen">
|
||||||
|
<p className="eyebrow" style={{ marginBottom: '0.75rem' }}>Listen to {nuggets.title}</p>
|
||||||
|
<div className="nuggets-platform-links">
|
||||||
|
{nuggets.spotifyUrl && <a href={nuggets.spotifyUrl} target="_blank" rel="noopener noreferrer" className="btn-primary">Listen on Spotify</a>}
|
||||||
|
{nuggets.appleUrl && <a href={nuggets.appleUrl} target="_blank" rel="noopener noreferrer" className="btn-secondary">Listen on Apple Podcasts</a>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div style={{ marginTop: '2rem' }}>
|
||||||
|
<Link to="/nuggets" className="nuggets-host-detail-back">← Back to {nuggets.title}</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<SiteFooter content={content} />
|
||||||
|
<AnalyticsConsentBanner content={content} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function BooksAndResourcesSection({ content }: { content: SiteContent }) {
|
function BooksAndResourcesSection({ content }: { content: SiteContent }) {
|
||||||
const books = (content.customLinks ?? []).filter(link => link.placement === 'books')
|
const books = (content.customLinks ?? []).filter(link => link.placement === 'books')
|
||||||
const recommended = (content.customLinks ?? []).filter(link => link.placement === 'recommended')
|
const recommended = (content.customLinks ?? []).filter(link => link.placement === 'recommended')
|
||||||
@@ -3131,6 +3193,7 @@ export default function App() {
|
|||||||
<Route path="/episodes" element={<EpisodesPage content={content} />} />
|
<Route path="/episodes" element={<EpisodesPage content={content} />} />
|
||||||
<Route path="/episodes/:id" element={<EpisodeDetailPage content={content} />} />
|
<Route path="/episodes/:id" element={<EpisodeDetailPage content={content} />} />
|
||||||
<Route path="/nuggets" element={<NuggetsPage content={content} />} />
|
<Route path="/nuggets" element={<NuggetsPage content={content} />} />
|
||||||
|
<Route path="/nuggets/hosts/:hostId" element={<NuggetsHostDetailPage content={content} />} />
|
||||||
<Route path="/resources" element={<ResourcesPage content={content} />} />
|
<Route path="/resources" element={<ResourcesPage content={content} />} />
|
||||||
<Route path="/downloads/:id" element={<DownloadDetailPage content={content} />} />
|
<Route path="/downloads/:id" element={<DownloadDetailPage content={content} />} />
|
||||||
<Route path="/about" element={<AboutPage content={content} />} />
|
<Route path="/about" element={<AboutPage content={content} />} />
|
||||||
|
|||||||
Reference in new issue
Block a user