Add logo image and update branding across header, footer, and emails

- Replace text logo with logo-spanglish.png in Header and Footer
- Update email templates to use logo image instead of styled text
- Add new HeroSection and EventHighlight components
- Update homepage layout and event detail pages
- Adjust Tailwind config and global styles
This commit is contained in:
root
2026-01-30 22:45:43 +00:00
parent 47ba754f05
commit d3c69f2936
18 changed files with 461 additions and 351 deletions

View File

@@ -2,6 +2,7 @@
import { useState, useEffect } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { useLanguage } from '@/context/LanguageContext';
import { eventsApi, Event } from '@/lib/api';
import Card from '@/components/ui/Card';
@@ -22,6 +23,12 @@ interface EventDetailClientProps {
export default function EventDetailClient({ eventId, initialEvent }: EventDetailClientProps) {
const { t, locale } = useLanguage();
const [event, setEvent] = useState<Event>(initialEvent);
const [mounted, setMounted] = useState(false);
// Ensure consistent hydration by only rendering dynamic content after mount
useEffect(() => {
setMounted(true);
}, []);
// Refresh event data on client for real-time availability
useEffect(() => {
@@ -48,7 +55,8 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
const isSoldOut = event.availableSeats === 0;
const isCancelled = event.status === 'cancelled';
const isPastEvent = new Date(event.startDatetime) < new Date();
// Only calculate isPastEvent after mount to avoid hydration mismatch
const isPastEvent = mounted ? new Date(event.startDatetime) < new Date() : false;
const canBook = !isSoldOut && !isCancelled && !isPastEvent && event.status === 'published';
return (
@@ -66,14 +74,20 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
{/* Event Details */}
<div className="lg:col-span-2">
<Card className="overflow-hidden">
{/* Banner */}
{/* Banner - LCP element, loaded with high priority */}
{/* Using unoptimized for backend-served images via /uploads/ rewrite */}
{event.bannerUrl ? (
<img
src={event.bannerUrl}
alt={`${event.title} - Spanglish language exchange event in Asunción`}
className="h-64 w-full object-cover"
loading="eager"
/>
<div className="relative h-64 w-full">
<Image
src={event.bannerUrl}
alt={`${event.title} - Spanglish language exchange event in Asunción`}
fill
className="object-cover"
sizes="(max-width: 1024px) 100vw, 66vw"
priority
unoptimized
/>
</div>
) : (
<div className="h-64 bg-gradient-to-br from-primary-yellow/40 to-secondary-blue/30 flex items-center justify-center">
<CalendarIcon className="w-24 h-24 text-primary-dark/30" />
@@ -82,7 +96,7 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
<div className="p-8">
<div className="flex items-start justify-between gap-4">
<h1 className="text-3xl font-bold text-primary-dark">
<h1 className="text-3xl font-bold text-primary-dark" suppressHydrationWarning>
{locale === 'es' && event.titleEs ? event.titleEs : event.title}
</h1>
{isCancelled && (
@@ -98,7 +112,7 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
<CalendarIcon className="w-6 h-6 text-primary-yellow flex-shrink-0" />
<div>
<p className="font-medium">{t('events.details.date')}</p>
<p className="text-gray-600">{formatDate(event.startDatetime)}</p>
<p className="text-gray-600" suppressHydrationWarning>{formatDate(event.startDatetime)}</p>
</div>
</div>
@@ -106,7 +120,7 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
<span className="w-6 h-6 flex items-center justify-center text-primary-yellow text-xl"></span>
<div>
<p className="font-medium">{t('events.details.time')}</p>
<p className="text-gray-600">{formatTime(event.startDatetime)}</p>
<p className="text-gray-600" suppressHydrationWarning>{formatTime(event.startDatetime)}</p>
</div>
</div>
@@ -141,7 +155,7 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
<div className="mt-8 pt-8 border-t border-secondary-light-gray">
<h2 className="font-semibold text-lg mb-4">About this event</h2>
<p className="text-gray-700 whitespace-pre-line">
<p className="text-gray-700 whitespace-pre-line" suppressHydrationWarning>
{locale === 'es' && event.descriptionEs
? event.descriptionEs
: event.description}
@@ -149,7 +163,7 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
</div>
{/* Social Sharing */}
<div className="mt-8 pt-8 border-t border-secondary-light-gray">
<div className="mt-8 pt-8 border-t border-secondary-light-gray" suppressHydrationWarning>
<ShareButtons
title={locale === 'es' && event.titleEs ? event.titleEs : event.title}
description={`${locale === 'es' ? 'Únete a' : 'Join'} ${locale === 'es' && event.titleEs ? event.titleEs : event.title} - ${formatDate(event.startDatetime)}`}