330 lines
11 KiB
Vue
330 lines
11 KiB
Vue
<template>
|
|
<div class="min-h-screen bg-gray-50">
|
|
<!-- Header -->
|
|
<header class="bg-white shadow-sm">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
|
<!-- Mobile Layout -->
|
|
<div class="md:hidden">
|
|
<div class="flex items-center justify-between mb-3">
|
|
<img src="/logos/logo.png" alt="New Life Christian Church" class="h-16 w-auto" />
|
|
<ClientOnly fallback-tag="div">
|
|
<template v-if="isAuthenticated">
|
|
<div class="flex items-center gap-2">
|
|
<NuxtLink
|
|
to="/profile"
|
|
class="px-3 py-2 text-sm font-medium text-blue-700 bg-blue-50 rounded-md hover:bg-blue-100 whitespace-nowrap"
|
|
>
|
|
Edit Profile
|
|
</NuxtLink>
|
|
<button
|
|
@click="handleLogout"
|
|
class="px-3 py-2 text-sm font-medium text-red-700 bg-red-50 rounded-md hover:bg-red-100 whitespace-nowrap"
|
|
>
|
|
Log Out
|
|
</button>
|
|
</div>
|
|
</template>
|
|
<NuxtLink
|
|
v-else
|
|
to="/login"
|
|
class="px-4 py-2 text-sm font-medium text-green-700 bg-green-50 rounded-md hover:bg-green-100"
|
|
>
|
|
Log In
|
|
</NuxtLink>
|
|
</ClientOnly>
|
|
</div>
|
|
<ClientOnly fallback-tag="div">
|
|
<div v-if="isAuthenticated && isAdmin" class="flex items-center justify-center gap-2">
|
|
<NuxtLink
|
|
to="/admin"
|
|
class="px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 font-medium text-xs whitespace-nowrap"
|
|
>
|
|
Manage Sermons
|
|
</NuxtLink>
|
|
<NuxtLink
|
|
to="/users"
|
|
class="px-3 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 font-medium text-xs whitespace-nowrap"
|
|
>
|
|
Manage Users
|
|
</NuxtLink>
|
|
</div>
|
|
</ClientOnly>
|
|
</div>
|
|
|
|
<!-- Desktop Layout -->
|
|
<div class="hidden md:flex items-center justify-between">
|
|
<div class="flex items-center space-x-4">
|
|
<img src="/logos/logo.png" alt="New Life Christian Church" class="h-16 w-auto" />
|
|
</div>
|
|
<div class="flex items-center gap-4">
|
|
<ClientOnly fallback-tag="div">
|
|
<template v-if="isAuthenticated">
|
|
<NuxtLink
|
|
v-if="isAdmin"
|
|
to="/admin"
|
|
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 font-medium text-sm whitespace-nowrap"
|
|
>
|
|
Manage Sermons
|
|
</NuxtLink>
|
|
<NuxtLink
|
|
v-if="isAdmin"
|
|
to="/users"
|
|
class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 font-medium text-sm whitespace-nowrap"
|
|
>
|
|
Manage Users
|
|
</NuxtLink>
|
|
<NuxtLink
|
|
to="/profile"
|
|
class="px-4 py-2 text-sm font-medium text-blue-700 bg-blue-50 rounded-md hover:bg-blue-100"
|
|
>
|
|
Edit Profile
|
|
</NuxtLink>
|
|
<button
|
|
@click="handleLogout"
|
|
class="px-4 py-2 text-sm font-medium text-red-700 bg-red-50 rounded-md hover:bg-red-100"
|
|
>
|
|
Log Out
|
|
</button>
|
|
</template>
|
|
<NuxtLink
|
|
v-else
|
|
to="/login"
|
|
class="px-4 py-2 text-sm font-medium text-green-700 bg-green-50 rounded-md hover:bg-green-100"
|
|
>
|
|
Log In
|
|
</NuxtLink>
|
|
<template #fallback>
|
|
<div class="h-10"></div>
|
|
</template>
|
|
</ClientOnly>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-center mt-6">
|
|
<ClientOnly>
|
|
<h2 v-if="isAuthenticated && firstName" class="text-xl text-gray-700 mb-2">
|
|
{{ greeting }}
|
|
</h2>
|
|
</ClientOnly>
|
|
<h1 class="text-3xl font-bold text-gray-900">
|
|
Welcome to New Life!
|
|
</h1>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Main Content -->
|
|
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 space-y-12">
|
|
<ClientOnly>
|
|
<!-- Today's Sermon -->
|
|
<div v-if="todaysSermon">
|
|
<h2 class="text-2xl font-semibold text-gray-900 mb-6">Today's Sermon</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<SermonCard :sermon="todaysSermon" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upcoming Sermons -->
|
|
<div v-if="upcomingSermons.length > 0">
|
|
<h2 class="text-2xl font-semibold text-gray-900 mb-6">Upcoming Sermons</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<SermonCard v-for="sermon in upcomingSermons" :key="sermon.id" :sermon="sermon" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Empty State (if no current or upcoming sermons) -->
|
|
<div v-if="!hasCurrentSermons" class="text-center py-12">
|
|
<p class="text-gray-500 text-lg">No upcoming sermons available yet.</p>
|
|
</div>
|
|
|
|
<template #fallback>
|
|
<div class="text-center py-12">
|
|
<p class="text-gray-500 text-lg">Loading sermons...</p>
|
|
</div>
|
|
</template>
|
|
</ClientOnly>
|
|
|
|
<!-- Previous Sermons Dropdown -->
|
|
<div v-if="previousSermons.length > 0" class="max-w-2xl mx-auto">
|
|
<h2 class="text-2xl font-semibold text-gray-900 mb-4 text-center">Previous Sermons</h2>
|
|
<p class="text-center text-gray-700 mb-4">Use the dropdown below to view a previous sermon</p>
|
|
<div class="flex flex-col sm:flex-row gap-3">
|
|
<select
|
|
v-model="selectedSermonSlug"
|
|
class="flex-1 px-4 py-3 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-base"
|
|
>
|
|
<option value="">-- Select a previous sermon --</option>
|
|
<option v-for="sermon in previousSermons" :key="sermon.id" :value="sermon.slug">
|
|
{{ sermon.title }} ({{ formatDate(sermon.date) }})
|
|
</option>
|
|
</select>
|
|
<button
|
|
@click="navigateToSermon"
|
|
:disabled="!selectedSermonSlug"
|
|
class="px-6 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed font-medium sm:whitespace-nowrap"
|
|
>
|
|
View
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<Footer />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
// Check authentication status
|
|
const { data: authData } = await useFetch('/api/auth/verify')
|
|
const isAuthenticated = computed(() => authData.value?.authenticated || false)
|
|
const isAdmin = computed(() => authData.value?.isAdmin || false)
|
|
const firstName = computed(() => authData.value?.firstName || '')
|
|
|
|
// Time-based greeting
|
|
const greeting = computed(() => {
|
|
if (!firstName.value) return ''
|
|
|
|
const hour = new Date().getHours()
|
|
let timeGreeting = ''
|
|
|
|
if (hour >= 5 && hour < 12) {
|
|
timeGreeting = 'Good morning'
|
|
} else if (hour >= 12 && hour < 18) {
|
|
timeGreeting = 'Good afternoon'
|
|
} else if (hour >= 18 && hour < 21) {
|
|
timeGreeting = 'Good evening'
|
|
} else {
|
|
timeGreeting = 'Greetings'
|
|
}
|
|
|
|
return `${timeGreeting}, ${firstName.value}!`
|
|
})
|
|
|
|
// Fetch non-archived sermons for the most recent sermon
|
|
const { data: activeSermons } = await useFetch('/api/sermons?includeArchived=false')
|
|
|
|
// Fetch archived sermons for the previous sermons dropdown
|
|
const { data: archivedSermons } = await useFetch('/api/sermons?includeArchived=true')
|
|
|
|
const selectedSermonSlug = ref('')
|
|
|
|
// Get today's sermon (if one exists for today's date)
|
|
const todaysSermon = computed(() => {
|
|
if (!activeSermons.value || activeSermons.value.length === 0) return null
|
|
|
|
const today = new Date()
|
|
today.setHours(0, 0, 0, 0)
|
|
|
|
for (const s of activeSermons.value) {
|
|
// Check primary date
|
|
const sermonDate = new Date(s.date + 'T00:00:00')
|
|
sermonDate.setHours(0, 0, 0, 0)
|
|
if (sermonDate.getTime() === today.getTime()) {
|
|
return { ...s, displayDate: s.date }
|
|
}
|
|
|
|
// Check additional dates if they exist
|
|
if (s.dates) {
|
|
try {
|
|
const additionalDates = JSON.parse(s.dates)
|
|
const matchingDate = additionalDates.find((dateStr: string) => {
|
|
const additionalDate = new Date(dateStr + 'T00:00:00')
|
|
additionalDate.setHours(0, 0, 0, 0)
|
|
return additionalDate.getTime() === today.getTime()
|
|
})
|
|
|
|
if (matchingDate) {
|
|
return { ...s, displayDate: matchingDate }
|
|
}
|
|
} catch {
|
|
// Continue to next sermon if parsing fails
|
|
}
|
|
}
|
|
}
|
|
|
|
return null
|
|
})
|
|
|
|
// Get upcoming sermons (future dates)
|
|
const upcomingSermons = computed(() => {
|
|
if (!activeSermons.value) return []
|
|
|
|
const today = new Date()
|
|
today.setHours(0, 0, 0, 0)
|
|
|
|
return activeSermons.value
|
|
.map((s: any) => {
|
|
// Check primary date
|
|
const sermonDate = new Date(s.date + 'T00:00:00')
|
|
sermonDate.setHours(0, 0, 0, 0)
|
|
|
|
// If primary date is in the future, use it
|
|
if (sermonDate.getTime() > today.getTime()) {
|
|
return { ...s, displayDate: s.date }
|
|
}
|
|
|
|
// Check additional dates if they exist
|
|
if (s.dates) {
|
|
try {
|
|
const additionalDates = JSON.parse(s.dates)
|
|
// Find the first future date from additional dates
|
|
const futureDate = additionalDates.find((dateStr: string) => {
|
|
const additionalDate = new Date(dateStr + 'T00:00:00')
|
|
additionalDate.setHours(0, 0, 0, 0)
|
|
return additionalDate.getTime() > today.getTime()
|
|
})
|
|
|
|
if (futureDate) {
|
|
return { ...s, displayDate: futureDate }
|
|
}
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
return null
|
|
})
|
|
.filter((s: any) => s !== null)
|
|
})
|
|
|
|
// Get archived sermons only for the previous sermons dropdown
|
|
const previousSermons = computed(() => {
|
|
if (!archivedSermons.value) return []
|
|
return archivedSermons.value.filter((s: any) => s.archived === 1)
|
|
})
|
|
|
|
// Check if there are any current or upcoming sermons
|
|
const hasCurrentSermons = computed(() => {
|
|
return todaysSermon.value !== null || upcomingSermons.value.length > 0
|
|
})
|
|
|
|
function formatDate(dateString: string) {
|
|
// Add T00:00:00 to ensure the date is interpreted as local time, not UTC
|
|
const date = new Date(dateString + 'T00:00:00')
|
|
return date.toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit'
|
|
})
|
|
}
|
|
|
|
function navigateToSermon() {
|
|
if (selectedSermonSlug.value) {
|
|
navigateTo(`/${selectedSermonSlug.value}`)
|
|
}
|
|
}
|
|
|
|
async function handleLogout() {
|
|
await $fetch('/api/auth/logout', { method: 'POST' })
|
|
window.location.reload()
|
|
}
|
|
|
|
// Watch for authentication changes (e.g., if user is deleted)
|
|
watch(() => authData.value?.authenticated, (newAuth, oldAuth) => {
|
|
// If user was authenticated but now isn't (and it's not initial load)
|
|
if (oldAuth === true && newAuth === false) {
|
|
window.location.reload()
|
|
}
|
|
})
|
|
</script>
|