Songs & dates

This commit is contained in:
2025-10-02 08:59:05 -04:00
parent dfe3517ac6
commit 27fcedfcd5
6 changed files with 207 additions and 13 deletions

View File

@@ -78,6 +78,23 @@
</div> </div>
</section> </section>
<!-- Worship Songs Section -->
<section v-if="worshipSongs.length > 0" class="mb-8">
<h2 class="text-2xl font-semibold text-gray-900 mb-4">Worship Songs</h2>
<div class="bg-yellow-50 rounded-lg p-6">
<ul class="space-y-3">
<li
v-for="(song, index) in worshipSongs"
:key="index"
:class="['text-gray-800', fontSizeClasses]"
>
<span class="font-semibold">{{ song.name }}</span>
<span v-if="song.artist" class="text-gray-600"> - {{ song.artist }}</span>
</li>
</ul>
</div>
</section>
<!-- Back Button --> <!-- Back Button -->
<div class="border-t pt-6"> <div class="border-t pt-6">
<NuxtLink <NuxtLink
@@ -132,6 +149,15 @@ const bibleReferences = computed(() => {
} }
}) })
const worshipSongs = computed(() => {
if (!sermon.value?.worship_songs) return []
try {
return JSON.parse(sermon.value.worship_songs)
} catch {
return []
}
})
// Font size classes // Font size classes
const fontSizeClasses = computed(() => { const fontSizeClasses = computed(() => {
switch (fontSize.value) { switch (fontSize.value) {

View File

@@ -111,7 +111,7 @@
<div class="space-y-6"> <div class="space-y-6">
<div> <div>
<label for="date" class="block text-sm font-medium text-gray-700 mb-2"> <label for="date" class="block text-sm font-medium text-gray-700 mb-2">
Sermon Date Primary Sermon Date
</label> </label>
<input <input
id="date" id="date"
@@ -120,6 +120,37 @@
required required
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
/> />
<p class="text-xs text-gray-500 mt-1">This date will be used for the sermon URL and sorting</p>
</div>
<!-- Additional Dates Section -->
<div class="border-t pt-6">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Additional Dates</h3>
<p class="text-sm text-gray-600 mb-4">Add more dates if this sermon will be presented multiple times (e.g., Saturday and Sunday services)</p>
<div class="space-y-3">
<div v-for="(date, index) in sermonDates" :key="index" class="flex gap-3">
<input
v-model="sermonDates[index]"
type="date"
class="flex-1 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
/>
<button
v-if="sermonDates.length > 1"
type="button"
@click="removeDate(index)"
class="px-3 py-2 bg-red-100 text-red-700 rounded-md hover:bg-red-200"
>
</button>
</div>
<button
type="button"
@click="addDate"
class="px-4 py-2 bg-blue-100 text-blue-700 rounded-md hover:bg-blue-200 font-medium"
>
+ Add Date
</button>
</div>
</div> </div>
<div> <div>
@@ -231,6 +262,57 @@
></textarea> ></textarea>
</div> </div>
<!-- Worship Songs Section -->
<div class="border-t pt-6">
<h2 class="text-xl font-semibold text-gray-900 mb-4">Worship Songs</h2>
<p class="text-sm text-gray-600 mb-4">Add songs that were sung during this service (optional)</p>
<div class="space-y-4">
<div v-for="(song, index) in worshipSongs" :key="index" class="border border-gray-200 rounded-lg p-4 space-y-3">
<div class="flex flex-col md:flex-row gap-3">
<div class="flex-1">
<label :for="`song-name-${index}`" class="block text-sm font-medium text-gray-700 mb-1">
Song Name
</label>
<input
:id="`song-name-${index}`"
v-model="song.name"
type="text"
placeholder="e.g., Amazing Grace"
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div class="flex-1">
<label :for="`song-artist-${index}`" class="block text-sm font-medium text-gray-700 mb-1">
Artist/Composer
</label>
<input
:id="`song-artist-${index}`"
v-model="song.artist"
type="text"
placeholder="e.g., John Newton"
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<button
v-if="worshipSongs.length > 1"
type="button"
@click="removeSong(index)"
class="self-end px-3 py-2 bg-red-100 text-red-700 rounded-md hover:bg-red-200"
>
</button>
</div>
</div>
<button
type="button"
@click="addSong"
class="px-4 py-2 bg-blue-100 text-blue-700 rounded-md hover:bg-blue-200 font-medium"
>
+ Add Song
</button>
</div>
</div>
<!-- Error/Success Messages --> <!-- Error/Success Messages -->
<div v-if="error" class="text-red-600 text-sm"> <div v-if="error" class="text-red-600 text-sm">
{{ error }} {{ error }}
@@ -280,6 +362,8 @@ const archiveSuccess = ref('')
// Create sermon form state // Create sermon form state
const editingSermonId = ref<number | null>(null) const editingSermonId = ref<number | null>(null)
const bibleReferences = ref([{ version: 'ESV', reference: '', text: '' }]) const bibleReferences = ref([{ version: 'ESV', reference: '', text: '' }])
const sermonDates = ref([''])
const worshipSongs = ref([{ name: '', artist: '' }])
const formData = ref({ const formData = ref({
date: '', date: '',
title: '', title: '',
@@ -305,6 +389,22 @@ function removeReference(index: number) {
bibleReferences.value.splice(index, 1) bibleReferences.value.splice(index, 1)
} }
function addDate() {
sermonDates.value.push('')
}
function removeDate(index: number) {
sermonDates.value.splice(index, 1)
}
function addSong() {
worshipSongs.value.push({ name: '', artist: '' })
}
function removeSong(index: number) {
worshipSongs.value.splice(index, 1)
}
function formatDateToSlug(date: string) { function formatDateToSlug(date: string) {
// Convert YYYY-MM-DD to MMDDYYYY // Convert YYYY-MM-DD to MMDDYYYY
const [year, month, day] = date.split('-') const [year, month, day] = date.split('-')
@@ -324,13 +424,23 @@ async function handleSubmit() {
) )
const bible_references = JSON.stringify(validReferences) const bible_references = JSON.stringify(validReferences)
// Filter valid dates and serialize as JSON
const validDates = sermonDates.value.filter(d => d)
const dates = validDates.length > 0 ? JSON.stringify(validDates) : null
// Filter valid songs and serialize as JSON
const validSongs = worshipSongs.value.filter(s => s.name)
const worship_songs = validSongs.length > 0 ? JSON.stringify(validSongs) : null
const body = { const body = {
slug, slug,
title: formData.value.title, title: formData.value.title,
date: formData.value.date, date: formData.value.date,
bible_references, bible_references,
personal_appliance: formData.value.personal_appliance, personal_appliance: formData.value.personal_appliance,
pastors_challenge: formData.value.pastors_challenge pastors_challenge: formData.value.pastors_challenge,
dates,
worship_songs
} }
if (editingSermonId.value) { if (editingSermonId.value) {
@@ -382,6 +492,8 @@ function cancelEdit() {
pastors_challenge: '' pastors_challenge: ''
} }
bibleReferences.value = [{ version: 'ESV', reference: '', text: '' }] bibleReferences.value = [{ version: 'ESV', reference: '', text: '' }]
sermonDates.value = ['']
worshipSongs.value = [{ name: '', artist: '' }]
} }
function handleEdit() { function handleEdit() {
@@ -408,6 +520,20 @@ function handleEdit() {
// Fallback for old format // Fallback for old format
bibleReferences.value = [{ version: 'ESV', reference: sermon.bible_references, text: '' }] bibleReferences.value = [{ version: 'ESV', reference: sermon.bible_references, text: '' }]
} }
// Parse and load additional dates
try {
sermonDates.value = sermon.dates ? JSON.parse(sermon.dates) : ['']
} catch {
sermonDates.value = ['']
}
// Parse and load worship songs
try {
worshipSongs.value = sermon.worship_songs ? JSON.parse(sermon.worship_songs) : [{ name: '', artist: '' }]
} catch {
worshipSongs.value = [{ name: '', artist: '' }]
}
// Scroll to form (just below the management section) // Scroll to form (just below the management section)
const formElement = document.querySelector('form') const formElement = document.querySelector('form')

View File

@@ -123,9 +123,26 @@ const todaysSermon = computed(() => {
today.setHours(0, 0, 0, 0) today.setHours(0, 0, 0, 0)
return activeSermons.value.find((s: any) => { return activeSermons.value.find((s: any) => {
// Check primary date
const sermonDate = new Date(s.date + 'T00:00:00') const sermonDate = new Date(s.date + 'T00:00:00')
sermonDate.setHours(0, 0, 0, 0) sermonDate.setHours(0, 0, 0, 0)
return sermonDate.getTime() === today.getTime() if (sermonDate.getTime() === today.getTime()) return true
// Check additional dates if they exist
if (s.dates) {
try {
const additionalDates = JSON.parse(s.dates)
return additionalDates.some((dateStr: string) => {
const additionalDate = new Date(dateStr + 'T00:00:00')
additionalDate.setHours(0, 0, 0, 0)
return additionalDate.getTime() === today.getTime()
})
} catch {
return false
}
}
return false
}) || null }) || null
}) })
@@ -137,9 +154,26 @@ const upcomingSermons = computed(() => {
today.setHours(0, 0, 0, 0) today.setHours(0, 0, 0, 0)
return activeSermons.value.filter((s: any) => { return activeSermons.value.filter((s: any) => {
// Check primary date
const sermonDate = new Date(s.date + 'T00:00:00') const sermonDate = new Date(s.date + 'T00:00:00')
sermonDate.setHours(0, 0, 0, 0) sermonDate.setHours(0, 0, 0, 0)
return sermonDate.getTime() > today.getTime() if (sermonDate.getTime() > today.getTime()) return true
// Check additional dates if they exist
if (s.dates) {
try {
const additionalDates = JSON.parse(s.dates)
return additionalDates.some((dateStr: string) => {
const additionalDate = new Date(dateStr + 'T00:00:00')
additionalDate.setHours(0, 0, 0, 0)
return additionalDate.getTime() > today.getTime()
})
} catch {
return false
}
}
return false
}) })
}) })

View File

@@ -11,7 +11,7 @@ export default defineEventHandler(async (event) => {
} }
const body = await readBody(event) const body = await readBody(event)
const { slug, title, date, bible_references, personal_appliance, pastors_challenge } = body const { slug, title, date, dates, bible_references, personal_appliance, pastors_challenge, worship_songs } = body
if (!slug || !title || !date || !bible_references || !personal_appliance || !pastors_challenge) { if (!slug || !title || !date || !bible_references || !personal_appliance || !pastors_challenge) {
throw createError({ throw createError({
@@ -25,9 +25,11 @@ export default defineEventHandler(async (event) => {
slug, slug,
title, title,
date, date,
dates,
bible_references, bible_references,
personal_appliance, personal_appliance,
pastors_challenge pastors_challenge,
worship_songs
}) })
return { return {

View File

@@ -20,7 +20,7 @@ export default defineEventHandler(async (event) => {
} }
const body = await readBody(event) const body = await readBody(event)
const { slug, title, date, bible_references, personal_appliance, pastors_challenge } = body const { slug, title, date, dates, bible_references, personal_appliance, pastors_challenge, worship_songs } = body
if (!slug || !title || !date || !bible_references || !personal_appliance || !pastors_challenge) { if (!slug || !title || !date || !bible_references || !personal_appliance || !pastors_challenge) {
throw createError({ throw createError({
@@ -33,10 +33,10 @@ export default defineEventHandler(async (event) => {
const db = getDatabase() const db = getDatabase()
const result = db.prepare(` const result = db.prepare(`
UPDATE sermons UPDATE sermons
SET slug = ?, title = ?, date = ?, bible_references = ?, SET slug = ?, title = ?, date = ?, dates = ?, bible_references = ?,
personal_appliance = ?, pastors_challenge = ? personal_appliance = ?, pastors_challenge = ?, worship_songs = ?
WHERE id = ? WHERE id = ?
`).run(slug, title, date, bible_references, personal_appliance, pastors_challenge, parseInt(id)) `).run(slug, title, date, dates || null, bible_references, personal_appliance, pastors_challenge, worship_songs || null, parseInt(id))
if (result.changes === 0) { if (result.changes === 0) {
throw createError({ throw createError({

View File

@@ -8,9 +8,11 @@ export interface Sermon {
slug: string slug: string
title: string title: string
date: string date: string
dates?: string
bible_references: string bible_references: string
personal_appliance: string personal_appliance: string
pastors_challenge: string pastors_challenge: string
worship_songs?: string
created_at?: string created_at?: string
} }
@@ -32,9 +34,11 @@ export function getDatabase() {
slug TEXT UNIQUE NOT NULL, slug TEXT UNIQUE NOT NULL,
title TEXT NOT NULL, title TEXT NOT NULL,
date TEXT NOT NULL, date TEXT NOT NULL,
dates TEXT,
bible_references TEXT NOT NULL, bible_references TEXT NOT NULL,
personal_appliance TEXT NOT NULL, personal_appliance TEXT NOT NULL,
pastors_challenge TEXT NOT NULL, pastors_challenge TEXT NOT NULL,
worship_songs TEXT,
archived INTEGER DEFAULT 0, archived INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) )
@@ -87,16 +91,18 @@ export function getSermonBySlug(slug: string) {
export function createSermon(sermon: Sermon) { export function createSermon(sermon: Sermon) {
const db = getDatabase() const db = getDatabase()
const stmt = db.prepare(` const stmt = db.prepare(`
INSERT INTO sermons (slug, title, date, bible_references, personal_appliance, pastors_challenge) INSERT INTO sermons (slug, title, date, dates, bible_references, personal_appliance, pastors_challenge, worship_songs)
VALUES (?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`) `)
return stmt.run( return stmt.run(
sermon.slug, sermon.slug,
sermon.title, sermon.title,
sermon.date, sermon.date,
sermon.dates || null,
sermon.bible_references, sermon.bible_references,
sermon.personal_appliance, sermon.personal_appliance,
sermon.pastors_challenge sermon.pastors_challenge,
sermon.worship_songs || null
) )
} }