mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 14:11:13 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
152
src-old/components/seo/MetaTags.tsx
Normal file
152
src-old/components/seo/MetaTags.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { MetaTagsProps, MetaTags as MetaTagsType, StructuredData } from './types';
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_DJANGO_API_URL || 'http://localhost:8000/api/v1';
|
||||
|
||||
/**
|
||||
* MetaTags Component
|
||||
*
|
||||
* Fetches and renders SEO meta tags from Django API.
|
||||
* Includes OpenGraph tags for social sharing and optional JSON-LD structured data.
|
||||
*/
|
||||
export function MetaTags({
|
||||
entityType,
|
||||
entitySlug,
|
||||
parkSlug,
|
||||
includeStructuredData = true
|
||||
}: MetaTagsProps) {
|
||||
const [metaTags, setMetaTags] = useState<MetaTagsType | null>(null);
|
||||
const [structuredData, setStructuredData] = useState<StructuredData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMetaTags = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// Build the appropriate endpoint URL
|
||||
let metaUrl = `${API_BASE_URL}/seo/meta/`;
|
||||
let structuredDataUrl = '';
|
||||
|
||||
switch (entityType) {
|
||||
case 'home':
|
||||
metaUrl += 'home';
|
||||
break;
|
||||
case 'park':
|
||||
if (!entitySlug) throw new Error('Park slug is required');
|
||||
metaUrl += `park/${entitySlug}`;
|
||||
if (includeStructuredData) {
|
||||
structuredDataUrl = `${API_BASE_URL}/seo/structured-data/park/${entitySlug}`;
|
||||
}
|
||||
break;
|
||||
case 'ride':
|
||||
if (!parkSlug || !entitySlug) throw new Error('Park slug and ride slug are required');
|
||||
metaUrl += `ride/${parkSlug}/${entitySlug}`;
|
||||
if (includeStructuredData) {
|
||||
structuredDataUrl = `${API_BASE_URL}/seo/structured-data/ride/${parkSlug}/${entitySlug}`;
|
||||
}
|
||||
break;
|
||||
case 'company':
|
||||
if (!entitySlug) throw new Error('Company slug is required');
|
||||
metaUrl += `company/${entitySlug}`;
|
||||
break;
|
||||
case 'ride-model':
|
||||
if (!entitySlug) throw new Error('Ride model slug is required');
|
||||
metaUrl += `ride-model/${entitySlug}`;
|
||||
break;
|
||||
}
|
||||
|
||||
// Fetch meta tags
|
||||
const metaResponse = await fetch(metaUrl);
|
||||
if (!metaResponse.ok) {
|
||||
throw new Error(`Failed to fetch meta tags: ${metaResponse.status}`);
|
||||
}
|
||||
const metaData = await metaResponse.json();
|
||||
setMetaTags(metaData);
|
||||
|
||||
// Fetch structured data if available and requested
|
||||
if (structuredDataUrl) {
|
||||
try {
|
||||
const structuredResponse = await fetch(structuredDataUrl);
|
||||
if (structuredResponse.ok) {
|
||||
const data = await structuredResponse.json();
|
||||
setStructuredData(data);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('Failed to fetch structured data:', error);
|
||||
// Don't fail the entire component if structured data fails
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error fetching meta tags:', error);
|
||||
// Set fallback meta tags
|
||||
setMetaTags({
|
||||
title: 'ThrillWiki - Theme Park & Roller Coaster Database',
|
||||
description: 'Explore the world\'s most comprehensive database of theme parks, roller coasters, and thrill rides.',
|
||||
keywords: 'theme parks, roller coasters, thrill rides, amusement parks',
|
||||
canonical: window.location.href,
|
||||
'og:title': 'ThrillWiki',
|
||||
'og:description': 'Explore the world\'s most comprehensive database of theme parks, roller coasters, and thrill rides.',
|
||||
'og:type': 'website',
|
||||
'og:url': window.location.href,
|
||||
'og:image': `${window.location.origin}/og-image.png`,
|
||||
'og:image:width': '1200',
|
||||
'og:image:height': '630',
|
||||
'og:site_name': 'ThrillWiki',
|
||||
'og:locale': 'en_US',
|
||||
'twitter:card': 'summary_large_image',
|
||||
'twitter:site': '@thrillwiki',
|
||||
'twitter:title': 'ThrillWiki',
|
||||
'twitter:description': 'Explore the world\'s most comprehensive database of theme parks, roller coasters, and thrill rides.',
|
||||
'twitter:image': `${window.location.origin}/og-image.png`,
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchMetaTags();
|
||||
}, [entityType, entitySlug, parkSlug, includeStructuredData]);
|
||||
|
||||
// Don't render anything while loading to avoid flash of default tags
|
||||
if (loading || !metaTags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Helmet>
|
||||
{/* Basic SEO */}
|
||||
<title>{metaTags.title}</title>
|
||||
<meta name="description" content={metaTags.description} />
|
||||
<meta name="keywords" content={metaTags.keywords} />
|
||||
<link rel="canonical" href={metaTags.canonical} />
|
||||
|
||||
{/* OpenGraph tags for Facebook, LinkedIn, Discord */}
|
||||
<meta property="og:title" content={metaTags['og:title']} />
|
||||
<meta property="og:description" content={metaTags['og:description']} />
|
||||
<meta property="og:type" content={metaTags['og:type']} />
|
||||
<meta property="og:url" content={metaTags['og:url']} />
|
||||
<meta property="og:image" content={metaTags['og:image']} />
|
||||
<meta property="og:image:width" content={metaTags['og:image:width']} />
|
||||
<meta property="og:image:height" content={metaTags['og:image:height']} />
|
||||
<meta property="og:site_name" content={metaTags['og:site_name']} />
|
||||
<meta property="og:locale" content={metaTags['og:locale']} />
|
||||
|
||||
{/* Twitter Card tags */}
|
||||
<meta name="twitter:card" content={metaTags['twitter:card']} />
|
||||
<meta name="twitter:site" content={metaTags['twitter:site']} />
|
||||
<meta name="twitter:title" content={metaTags['twitter:title']} />
|
||||
<meta name="twitter:description" content={metaTags['twitter:description']} />
|
||||
<meta name="twitter:image" content={metaTags['twitter:image']} />
|
||||
|
||||
{/* JSON-LD Structured Data */}
|
||||
{structuredData && (
|
||||
<script type="application/ld+json">
|
||||
{JSON.stringify(structuredData)}
|
||||
</script>
|
||||
)}
|
||||
</Helmet>
|
||||
);
|
||||
}
|
||||
2
src-old/components/seo/index.ts
Normal file
2
src-old/components/seo/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { MetaTags } from './MetaTags';
|
||||
export type { MetaTagsProps, EntityType, MetaTags as MetaTagsType, StructuredData } from './types';
|
||||
54
src-old/components/seo/types.ts
Normal file
54
src-old/components/seo/types.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* SEO Meta Tags Types
|
||||
*
|
||||
* Types for SEO meta tags returned by Django API endpoints
|
||||
*/
|
||||
|
||||
/**
|
||||
* Meta tags returned from Django SEO API endpoints
|
||||
*/
|
||||
export interface MetaTags {
|
||||
// Basic SEO
|
||||
title: string;
|
||||
description: string;
|
||||
keywords: string;
|
||||
canonical: string;
|
||||
|
||||
// OpenGraph (for Facebook, LinkedIn, Discord)
|
||||
'og:title': string;
|
||||
'og:description': string;
|
||||
'og:type': 'website' | 'article';
|
||||
'og:url': string;
|
||||
'og:image': string;
|
||||
'og:image:width': string;
|
||||
'og:image:height': string;
|
||||
'og:site_name': string;
|
||||
'og:locale': string;
|
||||
|
||||
// Twitter Cards
|
||||
'twitter:card': 'summary_large_image' | 'summary';
|
||||
'twitter:site': string;
|
||||
'twitter:title': string;
|
||||
'twitter:description': string;
|
||||
'twitter:image': string;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON-LD Structured Data (Schema.org)
|
||||
*/
|
||||
export type StructuredData = Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* Entity types for MetaTags component
|
||||
*/
|
||||
export type EntityType = 'home' | 'park' | 'ride' | 'company' | 'ride-model';
|
||||
|
||||
/**
|
||||
* Props for MetaTags component
|
||||
*/
|
||||
export interface MetaTagsProps {
|
||||
entityType: EntityType;
|
||||
entitySlug?: string;
|
||||
parkSlug?: string; // Required for rides
|
||||
includeStructuredData?: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user