localgreenchain/pages/index.tsx
Claude 1e14a700c7
Implement LocalGreenChain: Plant Cloning Blockchain System
This commit implements a complete blockchain-based plant tracking system
that preserves lineage across clones, seeds, and all plant offspring while
connecting growers through geographic proximity.

Features implemented:
- Custom blockchain with proof-of-work consensus
- Plant registration and cloning with lineage tracking
- Geographic discovery to find nearby plants and growers
- Integration with plants.net API for plant identification
- Comprehensive web UI for plant management
- RESTful API endpoints for all operations
- Network statistics and visualization

Core Components:
- lib/blockchain/: PlantBlock, PlantChain, and blockchain manager
- lib/services/: plants.net API and geolocation services
- pages/api/plants/: REST API endpoints for all operations
- pages/: Frontend UI pages for registration, exploration, and lineage

Technical Details:
- TypeScript for type safety
- Next.js for server-side rendering
- Tailwind CSS for responsive design
- JSON file-based blockchain storage
- Haversine distance calculations for geolocation
- OpenStreetMap integration for geocoding

This system enables large-scale adoption by:
- Making plant lineage tracking accessible to everyone
- Connecting local communities through plant sharing
- Providing immutable proof of plant provenance
- Supporting unlimited generations of plant propagation
- Scaling from individual growers to global networks

Documentation includes comprehensive README with:
- Quick start guide
- API reference
- Architecture details
- Scaling recommendations
- Use cases for various audiences
- Roadmap for future enhancements
2025-11-16 05:11:55 +00:00

281 lines
9.4 KiB
TypeScript

import { useState, useEffect } from 'react';
import Link from 'next/link';
import Head from 'next/head';
interface NetworkStats {
totalPlants: number;
totalOwners: number;
species: { [key: string]: number };
globalDistribution: { [key: string]: number };
}
export default function Home() {
const [stats, setStats] = useState<NetworkStats | null>(null);
const [blockchainInfo, setBlockchainInfo] = useState<any>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchNetworkStats();
}, []);
const fetchNetworkStats = async () => {
try {
const response = await fetch('/api/plants/network');
const data = await response.json();
if (data.success) {
setStats(data.network);
setBlockchainInfo(data.blockchain);
}
} catch (error) {
console.error('Error fetching network stats:', error);
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 to-emerald-100">
<Head>
<title>LocalGreenChain - Plant Cloning Blockchain</title>
<meta
name="description"
content="Track plant lineage and connect with growers worldwide"
/>
</Head>
{/* Header */}
<header className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-green-800">
🌱 LocalGreenChain
</h1>
<p className="mt-1 text-sm text-gray-600">
Plant Cloning Blockchain Network
</p>
</div>
<nav className="flex gap-4">
<Link href="/plants/register">
<a className="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition">
Register Plant
</a>
</Link>
<Link href="/plants/explore">
<a className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">
Explore Network
</a>
</Link>
</nav>
</div>
</div>
</header>
{/* Hero Section */}
<main className="max-w-7xl mx-auto px-4 py-12 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-4xl font-bold text-gray-900 mb-4">
Track Your Plant's Journey
</h2>
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
A blockchain for plants that preserves lineage across clones and
seeds. Connect with growers, share plants, and build a global green
network.
</p>
</div>
{/* Network Stats */}
{loading ? (
<div className="text-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-green-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading network stats...</p>
</div>
) : (
<>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-12">
<StatCard
title="Total Plants"
value={stats?.totalPlants || 0}
icon="🌿"
color="green"
/>
<StatCard
title="Growers"
value={stats?.totalOwners || 0}
icon="👥"
color="blue"
/>
<StatCard
title="Species"
value={Object.keys(stats?.species || {}).length}
icon="🌺"
color="purple"
/>
<StatCard
title="Blockchain Blocks"
value={blockchainInfo?.totalBlocks || 0}
icon="⛓️"
color="orange"
/>
</div>
{/* Features */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-12">
<FeatureCard
title="Track Lineage"
description="Every clone, cutting, and seed is recorded on the blockchain, preserving your plant's family tree forever."
icon="🌳"
/>
<FeatureCard
title="Connect Locally"
description="Find growers near you with similar plants. Share clones, trade seeds, and build your local plant community."
icon="📍"
/>
<FeatureCard
title="Global Network"
description="Join a worldwide network of plant enthusiasts. Track how your plants spread across the globe."
icon="🌍"
/>
</div>
{/* Top Species */}
{stats && Object.keys(stats.species).length > 0 && (
<div className="bg-white rounded-lg shadow-lg p-6 mb-12">
<h3 className="text-2xl font-bold text-gray-900 mb-4">
Popular Species
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{Object.entries(stats.species)
.sort((a, b) => b[1] - a[1])
.slice(0, 6)
.map(([species, count]) => (
<div
key={species}
className="flex justify-between items-center p-3 bg-gray-50 rounded-lg"
>
<span className="font-medium text-gray-800">
{species}
</span>
<span className="px-3 py-1 bg-green-100 text-green-800 rounded-full text-sm font-semibold">
{count}
</span>
</div>
))}
</div>
</div>
)}
{/* Blockchain Status */}
<div className="bg-white rounded-lg shadow-lg p-6">
<h3 className="text-2xl font-bold text-gray-900 mb-4">
Blockchain Status
</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<p className="text-gray-600 text-sm">Status</p>
<p className="text-lg font-semibold">
{blockchainInfo?.isValid ? (
<span className="text-green-600">✓ Valid</span>
) : (
<span className="text-red-600">✗ Invalid</span>
)}
</p>
</div>
<div>
<p className="text-gray-600 text-sm">Mining Difficulty</p>
<p className="text-lg font-semibold text-gray-900">
{blockchainInfo?.difficulty || 'N/A'}
</p>
</div>
<div>
<p className="text-gray-600 text-sm">Total Blocks</p>
<p className="text-lg font-semibold text-gray-900">
{blockchainInfo?.totalBlocks || 0}
</p>
</div>
</div>
</div>
</>
)}
{/* CTA Section */}
<div className="mt-12 bg-gradient-to-r from-green-600 to-emerald-600 rounded-lg shadow-xl p-8 text-center text-white">
<h3 className="text-3xl font-bold mb-4">Ready to Get Started?</h3>
<p className="text-lg mb-6 opacity-90">
Register your first plant and join the global green blockchain
network.
</p>
<Link href="/plants/register">
<a className="inline-block px-8 py-3 bg-white text-green-600 font-semibold rounded-lg hover:bg-gray-100 transition">
Register Your First Plant
</a>
</Link>
</div>
</main>
{/* Footer */}
<footer className="mt-20 bg-white border-t border-gray-200">
<div className="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
<p className="text-center text-gray-600">
© 2025 LocalGreenChain. Powered by blockchain technology. 🌱
</p>
</div>
</footer>
</div>
);
}
// Helper Components
function StatCard({
title,
value,
icon,
color,
}: {
title: string;
value: number;
icon: string;
color: string;
}) {
const colorClasses = {
green: 'bg-green-100 text-green-800',
blue: 'bg-blue-100 text-blue-800',
purple: 'bg-purple-100 text-purple-800',
orange: 'bg-orange-100 text-orange-800',
};
return (
<div className="bg-white rounded-lg shadow-lg p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-600 text-sm">{title}</p>
<p className="text-3xl font-bold text-gray-900 mt-1">{value}</p>
</div>
<div
className={`text-4xl ${colorClasses[color as keyof typeof colorClasses]
} w-16 h-16 rounded-full flex items-center justify-center`}
>
{icon}
</div>
</div>
</div>
);
}
function FeatureCard({
title,
description,
icon,
}: {
title: string;
description: string;
icon: string;
}) {
return (
<div className="bg-white rounded-lg shadow-lg p-6 text-center">
<div className="text-5xl mb-4">{icon}</div>
<h3 className="text-xl font-bold text-gray-900 mb-2">{title}</h3>
<p className="text-gray-600">{description}</p>
</div>
);
}