SEO: robots.txt, sitemap, Organization & Event schema; dashboard fmtTime fix; frontend updates

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Michilis
2026-02-12 07:55:43 +00:00
parent 95ee5a5dec
commit 18254c566e
31 changed files with 227 additions and 196 deletions

View File

@@ -3,89 +3,109 @@ import { MetadataRoute } from 'next';
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://spanglish.com.py';
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
interface Event {
interface SitemapEvent {
id: string;
status: string;
startDatetime: string;
updatedAt: string;
}
async function getPublishedEvents(): Promise<Event[]> {
/**
* Fetch all indexable events: published, completed, and cancelled.
* Sold-out / past events stay in the index (marked as expired, not removed).
* Only draft and archived events are excluded.
*/
async function getIndexableEvents(): Promise<SitemapEvent[]> {
try {
const response = await fetch(`${apiUrl}/api/events?status=published`, {
next: { tags: ['events-sitemap'] },
});
if (!response.ok) return [];
const data = await response.json();
return data.events || [];
const [publishedRes, completedRes] = await Promise.all([
fetch(`${apiUrl}/api/events?status=published`, {
next: { tags: ['events-sitemap'] },
}),
fetch(`${apiUrl}/api/events?status=completed`, {
next: { tags: ['events-sitemap'] },
}),
]);
const published = publishedRes.ok
? ((await publishedRes.json()).events as SitemapEvent[]) || []
: [];
const completed = completedRes.ok
? ((await completedRes.json()).events as SitemapEvent[]) || []
: [];
return [...published, ...completed];
} catch {
return [];
}
}
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
// Fetch published events for dynamic event pages
const events = await getPublishedEvents();
const events = await getIndexableEvents();
const now = new Date();
// Static pages
const staticPages: MetadataRoute.Sitemap = [
{
url: siteUrl,
lastModified: new Date(),
lastModified: now,
changeFrequency: 'weekly',
priority: 1,
},
{
url: `${siteUrl}/events`,
lastModified: new Date(),
lastModified: now,
changeFrequency: 'daily',
priority: 0.9,
},
{
url: `${siteUrl}/community`,
lastModified: new Date(),
lastModified: now,
changeFrequency: 'monthly',
priority: 0.7,
},
{
url: `${siteUrl}/contact`,
lastModified: new Date(),
lastModified: now,
changeFrequency: 'monthly',
priority: 0.6,
},
{
url: `${siteUrl}/faq`,
lastModified: new Date(),
lastModified: now,
changeFrequency: 'monthly',
priority: 0.6,
},
// Legal pages
{
url: `${siteUrl}/legal/terms-policy`,
lastModified: new Date(),
lastModified: now,
changeFrequency: 'yearly',
priority: 0.3,
},
{
url: `${siteUrl}/legal/privacy-policy`,
lastModified: new Date(),
lastModified: now,
changeFrequency: 'yearly',
priority: 0.3,
},
{
url: `${siteUrl}/legal/refund-cancelation-policy`,
lastModified: new Date(),
lastModified: now,
changeFrequency: 'yearly',
priority: 0.3,
},
];
// Dynamic event pages
const eventPages: MetadataRoute.Sitemap = events.map((event) => ({
url: `${siteUrl}/events/${event.id}`,
lastModified: new Date(event.updatedAt),
changeFrequency: 'weekly' as const,
priority: 0.8,
}));
// Dynamic event pages — upcoming events get higher priority
const eventPages: MetadataRoute.Sitemap = events.map((event) => {
const isUpcoming = new Date(event.startDatetime) > now;
return {
url: `${siteUrl}/events/${event.id}`,
lastModified: new Date(event.updatedAt),
changeFrequency: isUpcoming ? ('weekly' as const) : ('monthly' as const),
priority: isUpcoming ? 0.8 : 0.5,
};
});
return [...staticPages, ...eventPages];
}