SEO: metadata, sitemap, robots, OpenGraph, per-page titles + API stream field mapping

This commit is contained in:
BizzleBot 2026-02-19 17:55:55 +00:00
parent 8f4e5dea40
commit 86ce153ad2
10 changed files with 127 additions and 3 deletions

View File

@ -2,6 +2,15 @@ import React from 'react';
import Link from 'next/link'; import Link from 'next/link';
import { Eye, Mail, Users, Heart, ExternalLink } from 'lucide-react'; 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() { export default function AboutPage() {
return ( return (
<div className="max-w-5xl mx-auto px-6 py-16 space-y-20"> <div className="max-w-5xl mx-auto px-6 py-16 space-y-20">

View File

@ -3,6 +3,15 @@ import { Heart, Shield, Camera, TreePine } from 'lucide-react';
import { api, Campaign } from '@/lib/api'; import { api, Campaign } from '@/lib/api';
import DonationCard from '../components/DonationCard'; 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[] = [ const FALLBACK_CAMPAIGNS: Campaign[] = [
{ {
id: '1', title: 'Land Preservation Fund', id: '1', title: 'Land Preservation Fund',

View File

@ -3,6 +3,15 @@ import { Calendar } from 'lucide-react';
import { api, Event } from '@/lib/api'; import { api, Event } from '@/lib/api';
import EventCard from '../components/EventCard'; 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[] = [ const FALLBACK_EVENTS: Event[] = [
{ {
id: '1', id: '1',

View File

@ -1,11 +1,44 @@
import type { Metadata } from 'next'; import type { Metadata, Viewport } from 'next';
import './globals.css'; import './globals.css';
import Navbar from './components/Navbar'; import Navbar from './components/Navbar';
import Footer from './components/Footer'; import Footer from './components/Footer';
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
themeColor: '#0a1a15',
};
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Owl Stream | Cape Coral Friends of Wildlife', title: {
description: 'Live burrowing owl cams, wildlife conservation, and nature in Cape Coral, Florida.', 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 }) { export default function RootLayout({ children }: { children: React.ReactNode }) {

View File

@ -1,6 +1,15 @@
import React from 'react'; import React from 'react';
import { Map } from 'lucide-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() { export default function MapPage() {
return ( return (
<div className="max-w-7xl mx-auto px-6 py-16 space-y-10"> <div className="max-w-7xl mx-auto px-6 py-16 space-y-10">

11
app/robots.ts Normal file
View 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
View 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 },
];
}

View File

@ -3,6 +3,15 @@ import { Radio } from 'lucide-react';
import { api, Stream } from '@/lib/api'; import { api, Stream } from '@/lib/api';
import StreamCard from '../components/StreamCard'; 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[]> { async function getStreams(): Promise<Stream[]> {
try { return await api.getStreams(); } try { return await api.getStreams(); }
catch { return []; } catch { return []; }

View File

@ -2,6 +2,15 @@ import React from 'react';
import { Lock, Users, ClipboardList } from 'lucide-react'; import { Lock, Users, ClipboardList } from 'lucide-react';
import Link from 'next/link'; 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() { export default function VolunteerPage() {
return ( return (
<div className="max-w-5xl mx-auto px-6 py-16 space-y-12"> <div className="max-w-5xl mx-auto px-6 py-16 space-y-12">

View File

@ -3,6 +3,15 @@ import { TreePine } from 'lucide-react';
import { api, Wildlife } from '@/lib/api'; import { api, Wildlife } from '@/lib/api';
import WildlifeCard from '../components/WildlifeCard'; 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 // Fallback data when API unavailable
const FALLBACK: Wildlife[] = [ const FALLBACK: Wildlife[] = [
{ {