
- Implement dual theme system with separate Tailwind configs for light/dark modes - Add dynamic stylesheet switching based on system preference - Fix light mode text visibility by using darker colors on light backgrounds - Resolve sermon page bug where all content loaded on initial render - Add build scripts for theme compilation - Update recurring event type formatting consistency
264 lines
10 KiB
Plaintext
264 lines
10 KiB
Plaintext
---
|
|
import MainLayout from '../layouts/MainLayout.astro';
|
|
import { getChurchName, fetchSermonsJson, fetchLivestreamArchiveJson } from '../lib/bindings.js';
|
|
|
|
let churchName = 'Church';
|
|
let sermons = [];
|
|
let livestreams = [];
|
|
|
|
try {
|
|
churchName = getChurchName();
|
|
|
|
// Get regular sermons
|
|
const sermonsJson = fetchSermonsJson();
|
|
const parsedSermons = JSON.parse(sermonsJson);
|
|
sermons = Array.isArray(parsedSermons) ? parsedSermons : (parsedSermons.items || []);
|
|
|
|
// Get livestream archive
|
|
const livestreamsJson = fetchLivestreamArchiveJson();
|
|
const parsedLivestreams = JSON.parse(livestreamsJson);
|
|
livestreams = Array.isArray(parsedLivestreams) ? parsedLivestreams : (parsedLivestreams.items || []);
|
|
|
|
} catch (e) {
|
|
console.error('Failed to load sermons:', e);
|
|
}
|
|
|
|
// Combine all content for display
|
|
const allContent = [...sermons, ...livestreams];
|
|
---
|
|
|
|
<MainLayout title={`Sermons - ${churchName}`} description="Listen to our recent sermons and be inspired by God's Word">
|
|
|
|
<!-- Sermons Hero -->
|
|
<section class="py-16 bg-heavenly-gradient text-white">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
|
<h1 class="text-4xl lg:text-6xl font-bold mb-6">Sermons</h1>
|
|
<p class="text-xl text-blue-100 max-w-2xl mx-auto">
|
|
Be inspired and encouraged through messages from God's Word
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Sermons Grid -->
|
|
<section class="py-16 bg-white dark:bg-gray-900">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
|
|
<!-- Filter Tabs -->
|
|
<div class="mb-12">
|
|
<div class="flex flex-wrap justify-center gap-2">
|
|
<button class="filter-btn active" data-filter="sermons">
|
|
<i data-lucide="mic" class="w-4 h-4 mr-2"></i>
|
|
Sermons
|
|
</button>
|
|
<button class="filter-btn" data-filter="livestream">
|
|
<i data-lucide="video" class="w-4 h-4 mr-2"></i>
|
|
LiveStream Archive
|
|
</button>
|
|
<button class="filter-btn" data-filter="recent">
|
|
<i data-lucide="clock" class="w-4 h-4 mr-2"></i>
|
|
Recent Messages
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{allContent.length > 0 ? (
|
|
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8" id="sermons-grid">
|
|
{allContent.map(item => {
|
|
const isLivestream = livestreams.includes(item);
|
|
const category = isLivestream ? 'livestream' : 'sermon';
|
|
// Hide non-sermon items by default since "Sermons" tab is active
|
|
const hideByDefault = category !== 'sermon';
|
|
|
|
return (
|
|
<div
|
|
class={`sermon-card bg-gradient-to-br from-gray-50 to-blue-50 dark:from-gray-800 dark:to-gray-700 rounded-2xl p-6 hover:shadow-lg transition-all duration-300 hover:-translate-y-1 group${hideByDefault ? ' hidden' : ''}`}
|
|
data-animate
|
|
data-category={category}
|
|
data-speaker={item.speaker}
|
|
data-date={item.date}
|
|
>
|
|
<div class="w-16 h-16 bg-gradient-to-br from-primary-500 to-primary-600 rounded-2xl flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">
|
|
<i data-lucide="play" class="w-8 h-8 text-white"></i>
|
|
</div>
|
|
|
|
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3 line-clamp-2">{item.title}</h3>
|
|
|
|
<div class="space-y-2 mb-4">
|
|
{item.speaker && (
|
|
<div class="flex items-center space-x-2 text-gray-600 dark:text-gray-300">
|
|
<i data-lucide="user" class="w-4 h-4"></i>
|
|
<span class="text-sm">{item.speaker}</span>
|
|
</div>
|
|
)}
|
|
|
|
{item.date && (
|
|
<div class="flex items-center space-x-2 text-gray-600 dark:text-gray-300">
|
|
<i data-lucide="calendar" class="w-4 h-4"></i>
|
|
<span class="text-sm">{item.date}</span>
|
|
</div>
|
|
)}
|
|
|
|
{item.duration && (
|
|
<div class="flex items-center space-x-2 text-gray-600 dark:text-gray-300">
|
|
<i data-lucide="clock" class="w-4 h-4"></i>
|
|
<span class="text-sm">{item.duration}</span>
|
|
</div>
|
|
)}
|
|
|
|
{livestreams.includes(item) && (
|
|
<div class="flex items-center space-x-2 text-purple-600 dark:text-purple-400">
|
|
<i data-lucide="radio" class="w-4 h-4"></i>
|
|
<span class="text-sm font-medium">LiveStream Archive</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{item.description && (
|
|
<p class="text-gray-600 dark:text-gray-300 mb-4 leading-relaxed text-sm line-clamp-3">{item.description}</p>
|
|
)}
|
|
|
|
<div class="flex space-x-2">
|
|
{item.videoUrl && (
|
|
<a href={item.videoUrl} target="_blank" rel="noopener" class="flex-1 inline-flex items-center justify-center space-x-2 bg-primary-600 text-white px-4 py-2 rounded-xl font-medium hover:bg-primary-700 transition-colors text-sm">
|
|
<i data-lucide="play" class="w-4 h-4"></i>
|
|
<span>Watch</span>
|
|
</a>
|
|
)}
|
|
|
|
{item.audioUrl && (
|
|
<a href={item.audioUrl} target="_blank" rel="noopener" class="flex-1 inline-flex items-center justify-center space-x-2 bg-purple-600 text-white px-4 py-2 rounded-xl font-medium hover:bg-purple-700 transition-colors text-sm">
|
|
<i data-lucide="headphones" class="w-4 h-4"></i>
|
|
<span>Listen</span>
|
|
</a>
|
|
)}
|
|
|
|
{item.downloadUrl && (
|
|
<a href={item.downloadUrl} download class="inline-flex items-center justify-center p-2 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 rounded-xl hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors">
|
|
<i data-lucide="download" class="w-4 h-4"></i>
|
|
</a>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
) : (
|
|
<div class="text-center py-16">
|
|
<div class="w-24 h-24 bg-gray-100 dark:bg-gray-800 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
|
<i data-lucide="headphones" class="w-12 h-12 text-gray-400"></i>
|
|
</div>
|
|
<h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">No Sermons Available</h3>
|
|
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
|
Check back soon for new messages from God's Word
|
|
</p>
|
|
<a href="/live" class="inline-flex items-center space-x-2 bg-primary-600 text-white px-8 py-4 rounded-2xl font-semibold hover:bg-primary-700 transition-colors">
|
|
<i data-lucide="video" class="w-5 h-5"></i>
|
|
<span>Watch Live Services</span>
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Call to Action -->
|
|
<section class="py-16 bg-gray-50 dark:bg-gray-800">
|
|
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
|
<h2 class="text-3xl font-bold text-gray-900 dark:text-white mb-6">Stay Connected</h2>
|
|
<p class="text-xl text-gray-600 dark:text-gray-300 mb-8">
|
|
Don't miss our weekly messages. Join us for live services or subscribe to our updates.
|
|
</p>
|
|
|
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
|
<a href="/live" class="inline-flex items-center space-x-2 bg-primary-600 text-white px-8 py-4 rounded-2xl font-semibold hover:bg-primary-700 transition-colors">
|
|
<i data-lucide="video" class="w-5 h-5"></i>
|
|
<span>Watch Live</span>
|
|
</a>
|
|
<a href="/events" class="inline-flex items-center space-x-2 border-2 border-primary-600 text-primary-600 dark:text-primary-400 px-8 py-4 rounded-2xl font-semibold hover:bg-primary-600 hover:text-white transition-colors">
|
|
<i data-lucide="calendar" class="w-5 h-5"></i>
|
|
<span>View Events</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</MainLayout>
|
|
|
|
<style>
|
|
.line-clamp-2 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.line-clamp-3 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 3;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.filter-btn {
|
|
@apply px-6 py-3 rounded-xl font-medium transition-all duration-200 border-2;
|
|
@apply border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-300;
|
|
@apply hover:border-primary-500 hover:text-primary-600 dark:hover:text-primary-400;
|
|
}
|
|
|
|
.filter-btn.active {
|
|
@apply bg-primary-600 border-primary-600 text-white;
|
|
@apply hover:bg-primary-700 hover:border-primary-700;
|
|
}
|
|
|
|
.sermon-card.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.sermon-card {
|
|
animation: fadeIn 0.3s ease-in-out;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(20px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const filterButtons = document.querySelectorAll('.filter-btn');
|
|
const sermonCards = document.querySelectorAll('.sermon-card');
|
|
|
|
filterButtons.forEach(button => {
|
|
button.addEventListener('click', () => {
|
|
// Update active button
|
|
filterButtons.forEach(btn => btn.classList.remove('active'));
|
|
button.classList.add('active');
|
|
|
|
const filter = button.dataset.filter;
|
|
|
|
// Filter sermon cards
|
|
sermonCards.forEach(card => {
|
|
const category = card.dataset.category;
|
|
const date = new Date(card.dataset.date);
|
|
const isRecent = (Date.now() - date.getTime()) < (90 * 24 * 60 * 60 * 1000); // 90 days
|
|
|
|
let shouldShow = false;
|
|
|
|
if (filter === 'sermons') {
|
|
shouldShow = category === 'sermon';
|
|
} else if (filter === 'livestream') {
|
|
shouldShow = category === 'livestream';
|
|
} else if (filter === 'recent') {
|
|
shouldShow = isRecent;
|
|
}
|
|
|
|
if (shouldShow) {
|
|
card.classList.remove('hidden');
|
|
} else {
|
|
card.classList.add('hidden');
|
|
}
|
|
});
|
|
|
|
});
|
|
});
|
|
});
|
|
</script> |