SEO: metadata, sitemap, robots, OpenGraph, per-page titles + API stream field mapping
This commit is contained in:
parent
8f4e5dea40
commit
86ce153ad2
@ -2,6 +2,15 @@ import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Eye, Mail, Users, Heart, ExternalLink } from 'lucide-react';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'About CCFW',
|
||||
description: 'Learn about Cape Coral Friends of Wildlife and our mission to protect Florida wildlife.',
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div className="max-w-5xl mx-auto px-6 py-16 space-y-20">
|
||||
|
||||
@ -3,6 +3,15 @@ import { Heart, Shield, Camera, TreePine } from 'lucide-react';
|
||||
import { api, Campaign } from '@/lib/api';
|
||||
import DonationCard from '../components/DonationCard';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Donate',
|
||||
description: 'Support burrowing owl conservation with a donation to CCFW.',
|
||||
};
|
||||
|
||||
|
||||
|
||||
const FALLBACK_CAMPAIGNS: Campaign[] = [
|
||||
{
|
||||
id: '1', title: 'Land Preservation Fund',
|
||||
|
||||
@ -3,6 +3,15 @@ import { Calendar } from 'lucide-react';
|
||||
import { api, Event } from '@/lib/api';
|
||||
import EventCard from '../components/EventCard';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Events',
|
||||
description: 'Join CCFW conservation events, bird walks, and community programs.',
|
||||
};
|
||||
|
||||
|
||||
|
||||
const FALLBACK_EVENTS: Event[] = [
|
||||
{
|
||||
id: '1',
|
||||
|
||||
@ -1,11 +1,44 @@
|
||||
import type { Metadata } from 'next';
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
import './globals.css';
|
||||
import Navbar from './components/Navbar';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
export const viewport: Viewport = {
|
||||
width: 'device-width',
|
||||
initialScale: 1,
|
||||
themeColor: '#0a1a15',
|
||||
};
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Owl Stream | Cape Coral Friends of Wildlife',
|
||||
description: 'Live burrowing owl cams, wildlife conservation, and nature in Cape Coral, Florida.',
|
||||
title: {
|
||||
default: 'Owl Stream | Cape Coral Friends of Wildlife',
|
||||
template: '%s | Owl Stream',
|
||||
},
|
||||
description: 'Live burrowing owl cams, wildlife conservation, and nature in Cape Coral, Florida. Watch 24/7 streams, donate, and volunteer with CCFW.',
|
||||
keywords: ['burrowing owl', 'cape coral', 'wildlife camera', 'live stream', 'conservation', 'Florida wildlife', 'CCFW'],
|
||||
authors: [{ name: 'Cape Coral Friends of Wildlife' }],
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
locale: 'en_US',
|
||||
siteName: 'Owl Stream',
|
||||
title: 'Owl Stream — Live Burrowing Owl Cams',
|
||||
description: 'Watch Cape Coral\'s burrowing owls live, 24/7. Free wildlife cameras by CCFW.',
|
||||
images: [{ url: '/og-image.png', width: 1200, height: 630, alt: 'Owl Stream - Live Wildlife Cameras' }],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'Owl Stream — Live Burrowing Owl Cams',
|
||||
description: 'Watch Cape Coral\'s burrowing owls live, 24/7.',
|
||||
images: ['/og-image.png'],
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
icons: {
|
||||
icon: '/favicon.svg',
|
||||
apple: '/apple-touch-icon.png',
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
import React from 'react';
|
||||
import { Map } from 'lucide-react';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Burrow Map',
|
||||
description: 'Interactive map of burrowing owl burrows in Cape Coral, Florida.',
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default function MapPage() {
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-6 py-16 space-y-10">
|
||||
|
||||
11
app/robots.ts
Normal file
11
app/robots.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: {
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
},
|
||||
sitemap: 'https://owlstream.bizzle.cloud/sitemap.xml',
|
||||
};
|
||||
}
|
||||
17
app/sitemap.ts
Normal file
17
app/sitemap.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const base = 'https://owlstream.bizzle.cloud';
|
||||
const now = new Date();
|
||||
|
||||
return [
|
||||
{ url: base, lastModified: now, changeFrequency: 'daily', priority: 1 },
|
||||
{ url: `${base}/streams`, lastModified: now, changeFrequency: 'hourly', priority: 0.9 },
|
||||
{ url: `${base}/wildlife`, lastModified: now, changeFrequency: 'monthly', priority: 0.8 },
|
||||
{ url: `${base}/donate`, lastModified: now, changeFrequency: 'monthly', priority: 0.8 },
|
||||
{ url: `${base}/volunteer`, lastModified: now, changeFrequency: 'monthly', priority: 0.7 },
|
||||
{ url: `${base}/events`, lastModified: now, changeFrequency: 'weekly', priority: 0.7 },
|
||||
{ url: `${base}/map`, lastModified: now, changeFrequency: 'weekly', priority: 0.7 },
|
||||
{ url: `${base}/about`, lastModified: now, changeFrequency: 'monthly', priority: 0.5 },
|
||||
];
|
||||
}
|
||||
@ -3,6 +3,15 @@ import { Radio } from 'lucide-react';
|
||||
import { api, Stream } from '@/lib/api';
|
||||
import StreamCard from '../components/StreamCard';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Live Cams',
|
||||
description: 'Watch live wildlife cameras streaming 24/7 from Cape Coral.',
|
||||
};
|
||||
|
||||
|
||||
|
||||
async function getStreams(): Promise<Stream[]> {
|
||||
try { return await api.getStreams(); }
|
||||
catch { return []; }
|
||||
|
||||
@ -2,6 +2,15 @@ import React from 'react';
|
||||
import { Lock, Users, ClipboardList } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Volunteer',
|
||||
description: 'Join the CCFW volunteer team for owl surveys and habitat restoration.',
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default function VolunteerPage() {
|
||||
return (
|
||||
<div className="max-w-5xl mx-auto px-6 py-16 space-y-12">
|
||||
|
||||
@ -3,6 +3,15 @@ import { TreePine } from 'lucide-react';
|
||||
import { api, Wildlife } from '@/lib/api';
|
||||
import WildlifeCard from '../components/WildlifeCard';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Wildlife Guide',
|
||||
description: 'Discover Cape Coral wildlife including burrowing owls and gopher tortoises.',
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Fallback data when API unavailable
|
||||
const FALLBACK: Wildlife[] = [
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user