Admin website

This commit is contained in:
2025-09-25 16:54:50 +02:00
parent 8dbf6caa6f
commit 24f15b6e28
36 changed files with 3451 additions and 561 deletions

View File

@@ -1,15 +1,34 @@
import React from 'react'
import React from 'react'
import { Link } from 'react-router-dom'
export function EventCard({ e }){
const apiBaseUrl = (import.meta.env.VITE_API_BASE_URL || 'http://localhost:4001').replace(/\/$/, '')
function resolveEventImage(value, fallback) {
if (typeof value === 'string') {
const trimmed = value.trim()
if (!trimmed) return fallback
if (/^(?:https?:)?\/\//i.test(trimmed) || trimmed.startsWith('data:')) {
return trimmed
}
const path = trimmed.startsWith('/') ? trimmed : `/${trimmed}`
if (apiBaseUrl) {
return `${apiBaseUrl}${path}`
}
return path
}
return fallback
}
export function EventCard({ e }) {
const dt = new Date(e.date)
const mon = dt.toLocaleString('en', { month:'short' })
const day = dt.getDate()
// Map specific event names to images
const hasValidDate = !Number.isNaN(dt)
const mon = hasValidDate ? dt.toLocaleString('en', { month: 'short' }) : ''
const day = hasValidDate ? dt.getDate() : ''
const details = [e.time, e.location].filter(Boolean).join(' | ')
const getEventImage = (title) => {
const lowerTitle = title.toLowerCase()
const lowerTitle = `${title || ''}`.toLowerCase()
if (lowerTitle.includes('community sabbath lunch')) {
return '/assets/potluck.png'
}
@@ -28,16 +47,18 @@ export function EventCard({ e }){
if (lowerTitle.includes('welcome') || lowerTitle.includes('committee')) {
return '/assets/welcome_commite.png'
}
// Default event image
return '/assets/potluck.png'
}
const defaultImage = getEventImage(e.title)
const imageSrc = resolveEventImage(e.image, defaultImage)
return (
<article className="card p-10 flex flex-col" style={{aspectRatio:'4/3'}}>
<article className="card p-10 flex flex-col" style={{ aspectRatio: '4/3' }}>
<div className="flex items-center gap-6 mb-8">
<div className="w-20 h-20 rounded-full overflow-hidden bg-primary text-white flex items-center justify-center">
<img
src={getEventImage(e.title)}
<img
src={imageSrc}
alt={`${e.title} event`}
className="w-full h-full object-cover"
loading="lazy"
@@ -45,28 +66,30 @@ export function EventCard({ e }){
</div>
<div>
<h3 className="font-heading text-h3">{e.title}</h3>
<div className="text-muted text-small mt-2">{e.time} {e.location}</div>
<div className="text-muted text-small mt-2">
{hasValidDate ? `${mon} ${day}` : e.date}
{details ? ` | ${details}` : ''}
</div>
</div>
</div>
<p className="mt-6 text-body mb-8">{e.description}</p>
<div className="mt-auto pt-8">
<Link
to={`/events/${e.slug}`}
<Link
to={`/events/${e.slug}`}
className="btn-outline"
aria-label={`Event details: ${e.title} on ${mon} ${day}`}
aria-label={`Event details: ${e.title}${hasValidDate ? ` on ${mon} ${day}` : ''}`}
>
Details {e.title}
Details - {e.title}
</Link>
</div>
</article>
)
}
export function MinistryCard({ m }){
// Map specific ministry names to images
export function MinistryCard({ m }) {
const getMinistryImage = (name) => {
const lowerName = name.toLowerCase()
if (lowerName === 'children\'s ministry') {
if (lowerName === "children's ministry") {
return '/assets/children_ministry_craft.png'
}
if (lowerName === 'youth ministry') {
@@ -75,16 +98,15 @@ export function MinistryCard({ m }){
if (lowerName === 'adult sabbath school') {
return '/assets/speeking.png'
}
if (lowerName === 'women\'s ministry') {
if (lowerName === "women's ministry") {
return '/assets/pray_heart.png'
}
if (lowerName === 'men\'s ministry') {
if (lowerName === "men's ministry") {
return '/assets/family_entry.png'
}
if (lowerName === 'community outreach') {
return '/assets/welcome_commite.png'
}
// Fallback for other ministries
if (lowerName.includes('children') || lowerName.includes('kids')) {
return '/assets/children_ministry_craft.png'
}
@@ -97,15 +119,14 @@ export function MinistryCard({ m }){
if (lowerName.includes('prayer') || lowerName.includes('pray')) {
return '/assets/pray_heart.png'
}
// Default ministry image
return '/assets/welcome_commite.png'
}
return (
<article className="card p-10 flex flex-col">
<div className="mb-6">
<img
src={getMinistryImage(m.name)}
<img
src={getMinistryImage(m.name)}
alt={`${m.name} ministry`}
className="w-full h-80 object-cover rounded-lg mb-4"
loading="lazy"
@@ -115,8 +136,8 @@ export function MinistryCard({ m }){
<div className="text-muted text-small mb-3">{m.meeting}</div>
<div className="text-small mb-8">Leader: {m.leader}</div>
<div className="mt-auto pt-8">
<Link
to={`/ministries/${m.slug}`}
<Link
to={`/ministries/${m.slug}`}
className="btn-outline"
aria-label={`Explore ${m.name} ministry`}
>
@@ -127,13 +148,13 @@ export function MinistryCard({ m }){
)
}
export function SermonCard({ s }){
export function SermonCard({ s }) {
return (
<article className="card p-10 flex flex-col" style={{aspectRatio:'4/3'}}>
<article className="card p-10 flex flex-col" style={{ aspectRatio: '4/3' }}>
<div className="flex items-center gap-6 mb-8">
<div className="w-24 h-24 bg-sand rounded-lg overflow-hidden flex items-center justify-center">
<img
src="/assets/speeking.png"
<img
src="/assets/speeking.png"
alt={`Sermon by ${s.speaker}`}
className="w-full h-full object-cover"
loading="lazy"
@@ -141,17 +162,17 @@ export function SermonCard({ s }){
</div>
<div>
<h3 className="font-heading text-h3">{s.title}</h3>
<div className="text-muted text-small mt-2">{s.speaker} {new Date(s.date).toLocaleDateString()}</div>
<div className="text-muted text-small mt-2">{s.speaker} | {new Date(s.date).toLocaleDateString()}</div>
</div>
</div>
<p className="mt-6 text-body mb-8">{s.summary}</p>
<div className="mt-auto pt-8">
<Link
to={`/sermons/${s.slug}`}
<Link
to={`/sermons/${s.slug}`}
className="btn-outline"
aria-label={`Watch or listen to ${s.title} by ${s.speaker}`}
>
Watch/Listen {s.title}
Watch/Listen - {s.title}
</Link>
</div>
</article>

View File

@@ -83,10 +83,11 @@ export default function Footer(){
<div className="text-small text-muted">
© {year} Annaville Seventh-day Adventist Church. All rights reserved.
</div>
<div className="text-small flex gap-6">
<div className="text-small flex flex-wrap items-center gap-4">
<Link className="text-muted hover:text-primary transition-colors" to="/privacy">Privacy Policy</Link>
<Link className="text-muted hover:text-primary transition-colors" to="/terms">Terms of Use</Link>
<Link className="text-muted hover:text-primary transition-colors" to="/accessibility">Accessibility</Link>
<Link to="/admin/events" className="btn-outline text-xs px-3 py-1">Admin Events</Link>
</div>
</div>
</div>

View File

@@ -7,6 +7,7 @@ const navItems = [
{ to:'/about', label:'ABOUT US' },
{ to:'/services', label:'SERVICES' },
{ to:'/resources', label:'RESOURCES' },
{ to:'/events', label:'EVENTS' },
{ to:'/prayer-requests', label:'PRAYER REQUESTS' },
{ to:'/calendar', label:'CALENDAR' },
{ to:'/beliefs', label:'OUR BELIEFS' },