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
281 lines
9.4 KiB
TypeScript
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>
|
|
);
|
|
}
|