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
+25 -2
View File
@@ -978,7 +978,30 @@
"welcomeEmailSignoff": "Grace and peace,\nNate",
"headerFollowLabel": "Follow on Spotify",
"cookieBannerText": "We use optional analytics cookies to measure visits and location trends for site improvement.",
"customLinks": [],
"customLinks": [
{
"id": "mpvh1okr",
"label": "trbc.org",
"url": "https://trbc.org",
"imageUrl": "/uploads/titus-series-1780334718378.png",
"description": "We know that attending a new church can be overwhelming. Whether you are visiting for the first time or looking for ways to get more connected to Thomas Road, we want to help you take your next step with Jesus. We invite you to stop by our Connect wall (located in the main lobby) or fill out our connect form below—we'd love to meet you, answer your questions, and even offer you a welcome gift.",
"amazonUrl": "",
"amazonLabel": "",
"tags": [],
"placement": "externalSites"
},
{
"id": "mpvh7bqh",
"label": "necloud.us",
"url": "https://neclou.ud",
"imageUrl": "/uploads/titus_study_cover-1777916521873.png",
"description": "test test We know that attending a new church can be overwhelming. Whether you are visiting for the first time or looking for ways to get more connected to Thomas Road, we want to help you take your next step with Jesus. We invite you to stop by our Connect wall (located in the main lobby) or fill out our connect form below—we'd love to meet you, answer your questions, and even offer you a welcome gift. test test",
"amazonUrl": "",
"amazonLabel": "",
"tags": [],
"placement": "externalSites"
}
],
"customBlocks": [],
"archivedSeries": [],
"redirects": [
@@ -1046,5 +1069,5 @@
]
}
},
"updatedAt": "2026-06-01T16:33:33.885Z"
"updatedAt": "2026-06-01T17:25:41.122Z"
}
+131 -55
View File
@@ -54,7 +54,15 @@
"slug": "colossians",
"title": "Colossians: Rooted in Christ",
"description": "Walk through Colossians in guided lessons with commentary, Greek notes, and discussion prompts.",
"homepageEyebrow": "",
"showOnHomepage": false,
"showNewTag": false,
"newTagLabel": "NEW",
"status": "active",
"difficulty": "beginner",
"estimatedHours": 8,
"completionBadge": "",
"numberOfChapters": 1,
"sections": [
{
"id": "1-1-2",
@@ -65,10 +73,6 @@
"passageText": "v. 1 Paul, an apostle of Christ Jesus by the will of God, and Timothy our brother,\n\nv. 2 To the saints in Colossae, the faithful brothers in Christ: Grace and peace to you from God our Father.",
"summary": "Paul opens with apostolic authority and warm fellowship, greeting the saints at Colossae with the twin gifts that frame every believer's life: grace and peace.",
"commentary": "Paul opens with his credentials: an apostle of Christ Jesus. But the manner of the claim matters - he immediately attributes this not to his own merit but to the will of God. This is the same humility that runs beneath the letter's entire argument: everything comes from God, not from human achievement. Timothy is included not as a co-author but as a fellow laborer, identified simply as 'our brother' - the relational warmth is immediate. The recipients are addressed as 'saints' - the Greek hagios means holy ones, set apart by God - and as 'faithful brothers in Christ.' These are not terms of flattery but of theological definition: these are people who belong to God. Paul's greeting, 'grace and peace,' is more than convention. Grace (charis) is God's unmerited favor; peace (eirene) is the wholeness and well-being that flows from it. Every believer's life is framed by these two realities.",
"studyQuestions": [
"Why does Paul begin with grace and peace?",
"What tone does this create for the rest of the study?"
],
"greekNotes": [
"Apostle (apostolos): one commissioned directly by Jesus Christ, invested with authority to speak on His behalf.",
"Will (thelema): desire or purpose; Paul was not self-appointed but called by Gods initiative.",
@@ -77,6 +81,10 @@
"Grace (charis): God's unmerited favor that initiates and sustains the believer's life.",
"Peace (eirene): wholeness and well-being that flows from reconciliation with God."
],
"studyQuestions": [
"Why does Paul begin with grace and peace?",
"What tone does this create for the rest of the study?"
],
"releasedAt": "2026-11-09T00:00:00Z"
},
{
@@ -88,11 +96,11 @@
"passageText": "",
"summary": "Paul thanks God for faith, hope, and love, and reports that the gospel is bearing fruit.",
"commentary": "The gospel is shown to be active and alive. Paul points to evidence of real spiritual growth, not merely religious activity.",
"greekNotes": [],
"studyQuestions": [
"What signs of gospel growth does Paul mention?",
"How do faith, hope, and love work together here?"
],
"greekNotes": [],
"releasedAt": "2026-11-23T00:00:00Z"
},
{
@@ -104,11 +112,11 @@
"passageText": "",
"summary": "Paul prays for spiritual wisdom, endurance, and a life worthy of the Lord.",
"commentary": "This prayer shows that knowledge and fruitfulness belong together. Paul wants the believers to know Gods will and walk in a way that reflects it.",
"greekNotes": [],
"studyQuestions": [
"What does Paul ask God to produce?",
"How does this prayer shape the goals of the study?"
],
"greekNotes": [],
"releasedAt": "2026-12-07T00:00:00Z"
},
{
@@ -120,11 +128,11 @@
"passageText": "",
"summary": "Christ is supreme over creation, the church, and reconciliation.",
"commentary": "This is the center of Colossians. Everything else in the letter depends on who Christ is and what He has done.",
"greekNotes": [],
"studyQuestions": [
"What stands out most about Christs supremacy?",
"Why is this passage central to the study?"
],
"greekNotes": [],
"releasedAt": "2026-12-21T00:00:00Z"
},
{
@@ -136,11 +144,11 @@
"passageText": "",
"summary": "Paul explains how Christs work changes alienation into steadfast faith.",
"commentary": "The gospel moves from doctrine to real life. Paul shows what reconciliation looks like for people who have been brought near to God.",
"greekNotes": [],
"studyQuestions": [
"How does Paul describe the believers former condition?",
"What does continuing in the faith look like?"
],
"greekNotes": [],
"releasedAt": "2027-01-04T00:00:00Z"
},
{
@@ -152,11 +160,11 @@
"passageText": "",
"summary": "Paul describes his labor to proclaim Christ and present believers mature in Him.",
"commentary": "Pauls ministry is costly and purposeful. He labors so the church will grow into maturity, not just receive information.",
"greekNotes": [],
"studyQuestions": [
"What is the goal of Pauls ministry?",
"Why is maturity such an important theme here?"
],
"greekNotes": [],
"releasedAt": "2027-01-18T00:00:00Z"
},
{
@@ -168,11 +176,11 @@
"passageText": "",
"summary": "Paul reveals his struggle for the believers and his desire for encouragement and unity.",
"commentary": "This passage shows the heart behind the letter. Paul is deeply invested in the spiritual stability of the church.",
"greekNotes": [],
"studyQuestions": [
"What does Paul want the believers to experience?",
"Why does he emphasize encouragement and unity?"
],
"greekNotes": [],
"releasedAt": "2027-02-01T00:00:00Z"
},
{
@@ -184,11 +192,11 @@
"passageText": "",
"summary": "Paul calls believers to continue in Christ and resist deceptive teaching.",
"commentary": "The Christian life begins in Christ and must continue in Christ. Paul warns that subtle deception can slowly pull believers off course.",
"greekNotes": [],
"studyQuestions": [
"What does it mean to continue in Christ?",
"What kinds of deception should believers watch for?"
],
"greekNotes": [],
"releasedAt": "2027-02-15T00:00:00Z"
},
{
@@ -200,11 +208,11 @@
"passageText": "",
"summary": "The fullness of deity dwells in Christ, and believers are made complete in Him.",
"commentary": "This section corrects the idea that anything must be added to Christ. He is fully sufficient for the believers life and growth.",
"greekNotes": [],
"studyQuestions": [
"Why is fullness in Christ such an important correction?",
"How does this guard against spiritual insecurity?"
],
"greekNotes": [],
"releasedAt": "2027-03-01T00:00:00Z"
},
{
@@ -216,11 +224,11 @@
"passageText": "",
"summary": "Paul explains the believers union with Christ in His death, resurrection, and victory.",
"commentary": "The work of Christ is presented as decisive and complete. This becomes the foundation for all the commands that follow.",
"greekNotes": [],
"studyQuestions": [
"What changes because of Christs work here?",
"How does victory over the powers encourage believers today?"
],
"greekNotes": [],
"releasedAt": "2027-03-15T00:00:00Z"
},
{
@@ -232,11 +240,11 @@
"passageText": "",
"summary": "Paul warns against letting others judge believers over shadows when Christ is the substance.",
"commentary": "Outward practices can never replace Christ. Paul reminds the church that the real thing has come in Jesus.",
"greekNotes": [],
"studyQuestions": [
"What do shadows and substance mean here?",
"Where do believers still feel pressure from external judgment?"
],
"greekNotes": [],
"releasedAt": "2027-03-29T00:00:00Z"
},
{
@@ -248,11 +256,11 @@
"passageText": "",
"summary": "Paul warns against prideful spirituality that disconnects believers from Christ the head.",
"commentary": "Anything that pulls attention away from Christ and His body is spiritually dangerous. True growth stays connected to the head.",
"greekNotes": [],
"studyQuestions": [
"How does Paul describe false spirituality?",
"Why is connection to Christ essential?"
],
"greekNotes": [],
"releasedAt": "2027-04-12T00:00:00Z"
},
{
@@ -264,11 +272,11 @@
"passageText": "",
"summary": "Human rules cannot produce true transformation.",
"commentary": "External restrictions may look wise, but they cannot change the heart. Paul points the church back to union with Christ.",
"greekNotes": [],
"studyQuestions": [
"Why do human regulations fail to transform the heart?",
"What does real spiritual change require?"
],
"greekNotes": [],
"releasedAt": "2027-04-26T00:00:00Z"
},
{
@@ -280,11 +288,11 @@
"passageText": "",
"summary": "Paul calls believers to set their hearts and minds on Christ.",
"commentary": "Identity in Christ leads to a new direction for the mind and heart. The believers life is hidden with Christ, so priorities shift upward.",
"greekNotes": [],
"studyQuestions": [
"What does it mean to seek the things above?",
"How does hidden life in Christ shape daily priorities?"
],
"greekNotes": [],
"releasedAt": "2027-05-10T00:00:00Z"
},
{
@@ -296,11 +304,11 @@
"passageText": "",
"summary": "Paul calls believers to put away the practices of the old life.",
"commentary": "Christian growth includes real moral change. Pauls language is strong because the old life cannot be carried into the new one.",
"greekNotes": [],
"studyQuestions": [
"Which old patterns does Paul name?",
"Why is decisive change necessary?"
],
"greekNotes": [],
"releasedAt": "2027-05-24T00:00:00Z"
},
{
@@ -312,11 +320,11 @@
"passageText": "",
"summary": "Believers are renewed in Christ and formed into one new people.",
"commentary": "Renewal in Christ changes both personal identity and community life. The new self is not merely private; it is shared by the body of Christ.",
"greekNotes": [],
"studyQuestions": [
"What does renewal in Christ produce?",
"How does this reshape how believers see one another?"
],
"greekNotes": [],
"releasedAt": "2027-06-07T00:00:00Z"
},
{
@@ -328,11 +336,11 @@
"passageText": "",
"summary": "Paul lists the virtues believers should wear as Christs character becomes visible in them.",
"commentary": "The image of clothing makes virtue practical and visible. Paul wants Christs character to define the way believers live together.",
"greekNotes": [],
"studyQuestions": [
"Which virtues are most needed in your context?",
"How does love bind these virtues together?"
],
"greekNotes": [],
"releasedAt": "2027-06-21T00:00:00Z"
},
{
@@ -344,11 +352,11 @@
"passageText": "",
"summary": "Paul calls the church to let Christs peace rule and His word dwell richly among them.",
"commentary": "Peace, gratitude, and the word of Christ shape a healthy Christian community. These verses connect worship, teaching, and daily life.",
"greekNotes": [],
"studyQuestions": [
"What does it look like for Christs peace to rule?",
"How can the word of Christ dwell richly in a church family?"
],
"greekNotes": [],
"releasedAt": "2027-07-05T00:00:00Z"
},
{
@@ -360,11 +368,11 @@
"passageText": "",
"summary": "Paul applies the new life in Christ to family relationships.",
"commentary": "The gospel reaches into ordinary relationships. Christ changes how homes are ordered and how people treat one another.",
"greekNotes": [],
"studyQuestions": [
"How do these instructions reflect Christs lordship?",
"Which relationship is most challenging to apply today?"
],
"greekNotes": [],
"releasedAt": "2027-07-19T00:00:00Z"
},
{
@@ -376,11 +384,11 @@
"passageText": "",
"summary": "Paul instructs believers to serve faithfully and wholeheartedly.",
"commentary": "Work becomes worship when it is done for Christ. Paul teaches integrity, diligence, and accountability before a heavenly Master.",
"greekNotes": [],
"studyQuestions": [
"How does serving the Lord reshape everyday work?",
"What does Paul say about fairness and accountability?"
],
"greekNotes": [],
"releasedAt": "2027-08-02T00:00:00Z"
},
{
@@ -392,11 +400,11 @@
"passageText": "",
"summary": "Paul reminds masters to act justly because they too answer to the Lord.",
"commentary": "The gospel confronts authority as well as submission. All authority is accountable to Christ.",
"greekNotes": [],
"studyQuestions": [
"Why does Paul remind masters of their heavenly Master?",
"What does fairness look like in this context?"
],
"greekNotes": [],
"releasedAt": "2027-08-16T00:00:00Z"
},
{
@@ -408,11 +416,11 @@
"passageText": "",
"summary": "Paul closes with a call to steadfast prayer and wise witness toward outsiders.",
"commentary": "Prayer and witness belong together. Paul shows that spiritual alertness and everyday speech are both part of faithful ministry.",
"greekNotes": [],
"studyQuestions": [
"How are prayer and witness connected here?",
"What does gracious speech look like in real life?"
],
"greekNotes": [],
"releasedAt": "2027-08-30T00:00:00Z"
},
{
@@ -424,11 +432,11 @@
"passageText": "",
"summary": "Paul sends trusted messengers to encourage the church and bring news from the ministry field.",
"commentary": "These greetings reveal the relational side of ministry. The letter is carried by real people who serve the church with loyalty and care.",
"greekNotes": [],
"studyQuestions": [
"Why do these messengers matter to the letter?",
"What does this tell us about trusted coworkers?"
],
"greekNotes": [],
"releasedAt": "2027-09-13T00:00:00Z"
},
{
@@ -440,11 +448,11 @@
"passageText": "",
"summary": "Paul names the coworkers who are with him and commends their ministry support.",
"commentary": "The closing greetings show the breadth of the gospel network. Ministry is shared, supported, and strengthened by faithful co-workers.",
"greekNotes": [],
"studyQuestions": [
"What stands out about Pauls coworkers?",
"How does shared ministry strengthen the church?"
],
"greekNotes": [],
"releasedAt": "2027-09-27T00:00:00Z"
},
{
@@ -456,11 +464,11 @@
"passageText": "",
"summary": "Paul offers final greetings, instructions, and a closing reminder to complete the ministry entrusted to them.",
"commentary": "The letter ends with community, responsibility, and grace. Pauls final words keep the church focused on the mission it has received.",
"greekNotes": [],
"studyQuestions": [
"What final responsibility does Paul place on the church?",
"How does the closing reinforce the letters themes?"
],
"greekNotes": [],
"releasedAt": "2027-10-11T00:00:00Z"
}
]
@@ -470,7 +478,15 @@
"slug": "titus",
"title": "Titus: Sound Doctrine",
"description": "A full online class version of Titus with lesson-by-lesson notes and discussion.",
"homepageEyebrow": "",
"showOnHomepage": false,
"showNewTag": false,
"newTagLabel": "NEW",
"status": "planned",
"difficulty": "beginner",
"estimatedHours": 8,
"completionBadge": "",
"numberOfChapters": 1,
"sections": []
}
],
@@ -495,7 +511,8 @@
"studyQuestions": [
"Why does Paul begin with grace and peace?",
"What tone does this create for the rest of the study?"
]
],
"releasedAt": "2026-11-09T00:00:00Z"
},
{
"id": "1-3-8",
@@ -510,7 +527,8 @@
"studyQuestions": [
"What signs of gospel growth does Paul mention?",
"How do faith, hope, and love work together here?"
]
],
"releasedAt": "2026-11-23T00:00:00Z"
},
{
"id": "1-9-14",
@@ -525,7 +543,8 @@
"studyQuestions": [
"What does Paul ask God to produce?",
"How does this prayer shape the goals of the study?"
]
],
"releasedAt": "2026-12-07T00:00:00Z"
},
{
"id": "1-15-20",
@@ -540,7 +559,8 @@
"studyQuestions": [
"What stands out most about Christs supremacy?",
"Why is this passage central to the study?"
]
],
"releasedAt": "2026-12-21T00:00:00Z"
},
{
"id": "1-21-23",
@@ -555,7 +575,8 @@
"studyQuestions": [
"How does Paul describe the believers former condition?",
"What does continuing in the faith look like?"
]
],
"releasedAt": "2027-01-04T00:00:00Z"
},
{
"id": "1-24-29",
@@ -570,7 +591,8 @@
"studyQuestions": [
"What is the goal of Pauls ministry?",
"Why is maturity such an important theme here?"
]
],
"releasedAt": "2027-01-18T00:00:00Z"
},
{
"id": "2-1-5",
@@ -585,7 +607,8 @@
"studyQuestions": [
"What does Paul want the believers to experience?",
"Why does he emphasize encouragement and unity?"
]
],
"releasedAt": "2027-02-01T00:00:00Z"
},
{
"id": "2-6-8",
@@ -600,7 +623,8 @@
"studyQuestions": [
"What does it mean to continue in Christ?",
"What kinds of deception should believers watch for?"
]
],
"releasedAt": "2027-02-15T00:00:00Z"
},
{
"id": "2-9-10",
@@ -615,7 +639,8 @@
"studyQuestions": [
"Why is fullness in Christ such an important correction?",
"How does this guard against spiritual insecurity?"
]
],
"releasedAt": "2027-03-01T00:00:00Z"
},
{
"id": "2-11-15",
@@ -630,7 +655,8 @@
"studyQuestions": [
"What changes because of Christs work here?",
"How does victory over the powers encourage believers today?"
]
],
"releasedAt": "2027-03-15T00:00:00Z"
},
{
"id": "2-16-17",
@@ -645,7 +671,8 @@
"studyQuestions": [
"What do shadows and substance mean here?",
"Where do believers still feel pressure from external judgment?"
]
],
"releasedAt": "2027-03-29T00:00:00Z"
},
{
"id": "2-18-19",
@@ -660,7 +687,8 @@
"studyQuestions": [
"How does Paul describe false spirituality?",
"Why is connection to Christ essential?"
]
],
"releasedAt": "2027-04-12T00:00:00Z"
},
{
"id": "2-20-23",
@@ -675,7 +703,8 @@
"studyQuestions": [
"Why do human regulations fail to transform the heart?",
"What does real spiritual change require?"
]
],
"releasedAt": "2027-04-26T00:00:00Z"
},
{
"id": "3-1-4",
@@ -690,7 +719,8 @@
"studyQuestions": [
"What does it mean to seek the things above?",
"How does hidden life in Christ shape daily priorities?"
]
],
"releasedAt": "2027-05-10T00:00:00Z"
},
{
"id": "3-5-9",
@@ -705,7 +735,8 @@
"studyQuestions": [
"Which old patterns does Paul name?",
"Why is decisive change necessary?"
]
],
"releasedAt": "2027-05-24T00:00:00Z"
},
{
"id": "3-10-11",
@@ -720,7 +751,8 @@
"studyQuestions": [
"What does renewal in Christ produce?",
"How does this reshape how believers see one another?"
]
],
"releasedAt": "2027-06-07T00:00:00Z"
},
{
"id": "3-12-14",
@@ -735,7 +767,8 @@
"studyQuestions": [
"Which virtues are most needed in your context?",
"How does love bind these virtues together?"
]
],
"releasedAt": "2027-06-21T00:00:00Z"
},
{
"id": "3-15-17",
@@ -750,7 +783,8 @@
"studyQuestions": [
"What does it look like for Christs peace to rule?",
"How can the word of Christ dwell richly in a church family?"
]
],
"releasedAt": "2027-07-05T00:00:00Z"
},
{
"id": "3-18-21",
@@ -765,7 +799,8 @@
"studyQuestions": [
"How do these instructions reflect Christs lordship?",
"Which relationship is most challenging to apply today?"
]
],
"releasedAt": "2027-07-19T00:00:00Z"
},
{
"id": "3-22-25",
@@ -780,7 +815,8 @@
"studyQuestions": [
"How does serving the Lord reshape everyday work?",
"What does Paul say about fairness and accountability?"
]
],
"releasedAt": "2027-08-02T00:00:00Z"
},
{
"id": "4-1",
@@ -795,7 +831,8 @@
"studyQuestions": [
"Why does Paul remind masters of their heavenly Master?",
"What does fairness look like in this context?"
]
],
"releasedAt": "2027-08-16T00:00:00Z"
},
{
"id": "4-2-6",
@@ -810,7 +847,8 @@
"studyQuestions": [
"How are prayer and witness connected here?",
"What does gracious speech look like in real life?"
]
],
"releasedAt": "2027-08-30T00:00:00Z"
},
{
"id": "4-7-9",
@@ -825,7 +863,8 @@
"studyQuestions": [
"Why do these messengers matter to the letter?",
"What does this tell us about trusted coworkers?"
]
],
"releasedAt": "2027-09-13T00:00:00Z"
},
{
"id": "4-10-14",
@@ -840,7 +879,8 @@
"studyQuestions": [
"What stands out about Pauls coworkers?",
"How does shared ministry strengthen the church?"
]
],
"releasedAt": "2027-09-27T00:00:00Z"
},
{
"id": "4-15-18",
@@ -855,11 +895,24 @@
"studyQuestions": [
"What final responsibility does Paul place on the church?",
"How does the closing reinforce the letters themes?"
]
],
"releasedAt": "2027-10-11T00:00:00Z"
}
],
"shareHeading": "Help one more person hear the Word this week.",
"shareP": "Scan the QR code or text the show link to a friend who needs encouragement today.",
"prismVideoUrl": "/images/PRISM.mp4",
"prismEyebrow": "Featured Video",
"prismHeading": "PRISM",
"prismIntro": "What if every time you opened your Bible, you had a clear, repeatable method to actually dig in?",
"prismPatternIntro": "In this bonus episode, I walk you through P.R.I.S.M. — a five-step daily Bible study pattern designed to help you slow down, go deep, and let the Word do its work.",
"prismStep1": "P — Pray before you read",
"prismStep2": "R — Read slowly and observe",
"prismStep3": "I — Interpret with context",
"prismStep4": "S — Study and apply specifically",
"prismStep5": "M — Memorize one verse at a time",
"prismOutro": "Whether you're brand new to daily Bible reading or you've been at it for years, P.R.I.S.M. gives you a consistent framework for every passage, every day.",
"prismClosing": "The goal isn't to get through the text - it's to let the text get through to you.",
"whereToNextEyebrow": "Where to Next",
"whereToNextHeading": "Choose the page you need.",
"whereToNextCards": [
@@ -925,7 +978,30 @@
"welcomeEmailSignoff": "Grace and peace,\nNate",
"headerFollowLabel": "Follow on Spotify",
"cookieBannerText": "We use optional analytics cookies to measure visits and location trends for site improvement.",
"customLinks": [],
"customLinks": [
{
"id": "mpvh1okr",
"label": "trbc.org",
"url": "https://trbc.org",
"imageUrl": "/uploads/titus-series-1780334718378.png",
"description": "We know that attending a new church can be overwhelming. Whether you are visiting for the first time or looking for ways to get more connected to Thomas Road, we want to help you take your next step with Jesus. We invite you to stop by our Connect wall (located in the main lobby) or fill out our connect form below—we'd love to meet you, answer your questions, and even offer you a welcome gift.",
"amazonUrl": "",
"amazonLabel": "",
"tags": [],
"placement": "externalSites"
},
{
"id": "mpvh7bqh",
"label": "necloud.us",
"url": "https://neclou.ud",
"imageUrl": "/uploads/titus_study_cover-1777916521873.png",
"description": "test test We know that attending a new church can be overwhelming. Whether you are visiting for the first time or looking for ways to get more connected to Thomas Road, we want to help you take your next step with Jesus. We invite you to stop by our Connect wall (located in the main lobby) or fill out our connect form below—we'd love to meet you, answer your questions, and even offer you a welcome gift. test test",
"amazonUrl": "",
"amazonLabel": "",
"tags": [],
"placement": "externalSites"
}
],
"customBlocks": [],
"archivedSeries": [],
"redirects": [
@@ -993,5 +1069,5 @@
]
}
},
"updatedAt": "2026-05-12T16:06:01.888Z"
"updatedAt": "2026-06-01T17:25:41.133Z"
}
+1 -1
View File
@@ -98,5 +98,5 @@
"topic": "Faith & Life"
}
],
"updatedAt": "2026-05-12T16:06:01.888Z"
"updatedAt": "2026-06-01T17:25:41.133Z"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

+5 -1
View File
@@ -162,7 +162,11 @@ function sanitizeCustomLinks(value) {
.filter(item => item && typeof item === 'object')
.map(item => {
const placement =
item?.placement === 'platforms' || item?.placement === 'footer' || item?.placement === 'resources'
item?.placement === 'platforms'
|| item?.placement === 'footer'
|| item?.placement === 'resources'
|| item?.placement === 'otherSites'
|| item?.placement === 'externalSites'
? item.placement
: 'footer'
+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>
+32 -2
View File
@@ -494,7 +494,7 @@ function SiteFooter({ content }: { content: SiteContent }) {
const creatorUrl = content.platformCreatorUrl || 'https://creators.spotify.com/pod/profile/nmemmert/'
const facebookUrl = content.platformFacebookUrl || 'https://facebook.com/versebyversewithnate'
const extraFooterLinks = (content.customLinks ?? []).filter(
l => l.placement === 'footer' || l.placement === 'platforms',
l => l.placement === 'footer' || l.placement === 'platforms' || l.placement === 'otherSites',
)
return (
@@ -524,7 +524,7 @@ function SiteFooter({ content }: { content: SiteContent }) {
<>
<span aria-hidden="true">·</span>
<details className="footer-more-links">
<summary>More Platforms</summary>
<summary>More Links</summary>
<div className="footer-more-links-menu">
{extraFooterLinks.map(link => (
<a key={link.id} href={link.url} target="_blank" rel="noreferrer">{link.label}</a>
@@ -950,6 +950,34 @@ function CustomBlocksSection({ content, page }: { content: SiteContent; page: st
)
}
function ExternalSitesSection({ content }: { content: SiteContent }) {
const links = (content.customLinks ?? []).filter(link => link.placement === 'externalSites')
if (links.length === 0) return null
return (
<section className="section-resources" aria-label="External sites">
<div className="section-inner">
<div className="download-library-head">
<p className="eyebrow">Homepage Links</p>
<h2 className="section-heading">External sites and ministries worth checking out.</h2>
<p className="download-library-copy">These links are shown on the homepage, separate from the footer menu.</p>
</div>
<div className="resources-list">
{links.map(link => (
<a key={link.id} href={link.url} target="_blank" rel="noreferrer" className="resource-link">
<div className="resource-download-meta">
<span className="resource-link-label">{link.label}</span>
{link.description && <p className="resource-link-description">{link.description}</p>}
<span className="resource-link-action">Open external site </span>
</div>
</a>
))}
</div>
</div>
</section>
)
}
function LandingPage({ content }: { content: SiteContent }) {
const featuredStudy = getHomepageFeaturedStudy(content)
@@ -1068,6 +1096,8 @@ function LandingPage({ content }: { content: SiteContent }) {
</div>
</section>
<ExternalSitesSection content={content} />
<section className="section-home-jump" aria-label="Homepage navigation">
<div className="section-inner">
<div className="home-jump-head">
+1 -1
View File
@@ -25,7 +25,7 @@ export interface CustomLink {
id: string
label: string
url: string
placement: 'platforms' | 'footer' | 'resources'
placement: 'platforms' | 'footer' | 'resources' | 'otherSites' | 'externalSites'
imageUrl?: string
description?: string
amazonUrl?: string