| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Analytics - Agrosynth OS</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <style> |
| .chart-container { |
| height: 300px; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 font-sans antialiased"> |
| <div class="flex h-screen overflow-hidden"> |
| |
| <div class="bg-white shadow-md w-64 flex flex-col"> |
| <div class="flex items-center justify-center h-16 px-4 border-b"> |
| <div class="flex items-center"> |
| <i data-feather="layers" class="text-green-600 h-6 w-6"></i> |
| <span class="ml-2 text-lg font-bold text-gray-900">Agrosynth OS</span> |
| </div> |
| </div> |
| <nav class="flex-1 overflow-y-auto py-4"> |
| <div class="px-4 space-y-1"> |
| <a href="/dashboard" class="flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900"> |
| <i data-feather="home" class="text-gray-500 h-5 w-5"></i> |
| <span class="ml-3">Dashboard</span> |
| </a> |
| <a href="/field-maps" class="flex items-center px-2 py-2 text-sm font-medium rounded-md text-gray-600 hover:bg-gray-50 hover:text-gray-900"> |
| <i data-feather="map" class="text-gray-500 h-5 w-5"></i> |
| <span class="ml-3">Field Maps</span> |
| </a> |
| <a href="/analytics" class="flex items-center px-2 py-2 text-sm font-medium rounded-md bg-green-50 text-green-700"> |
| <i data-feather="activity" class="text-green-600 h-5 w-5"></i> |
| <span class="ml-3">Analytics</span> |
| </a> |
| </div> |
| </nav> |
| </div> |
|
|
| |
| <div class="flex-1 overflow-auto"> |
| <header class="bg-white shadow-sm"> |
| <div class="flex justify-between items-center px-4 py-4 sm:px-6"> |
| <h1 class="text-xl font-semibold text-gray-900">Analytics</h1> |
| </div> |
| </header> |
|
|
| <main class="p-4 sm:p-6"> |
| <div id="analytics-container"> |
| <div class="rounded-xl border p-6 text-sm text-gray-500">Loading analytics...</div> |
| </div> |
| </main> |
| </div> |
| </div> |
|
|
| <script> |
| feather.replace(); |
| |
| |
| (async () => { |
| try { |
| const r = await fetch('/api/proxy/api/v1/farms/analytics'); |
| const text = await r.text(); |
| const data = JSON.parse(text); |
| const container = document.getElementById('analytics-container'); |
| |
| container.innerHTML = ` |
| <h1 class="text-2xl font-semibold mb-4">Analytics</h1> |
| <div class="grid md:grid-cols-2 gap-6"> |
| <div class="border rounded-xl p-4"> |
| <div class="font-semibold mb-2">Yield Trend (t/ha)</div> |
| <div class="chart-container"> |
| <canvas id="yieldChart"></canvas> |
| </div> |
| </div> |
| <div class="border rounded-xl p-4"> |
| <div class="font-semibold mb-2">Soil Moisture (%)</div> |
| <div class="chart-container"> |
| <canvas id="moistureChart"></canvas> |
| </div> |
| </div> |
| </div> |
| `; |
| |
| |
| const yieldCtx = document.getElementById('yieldChart').getContext('2d'); |
| new Chart(yieldCtx, { |
| type: 'line', |
| data: { |
| labels: data.yield.labels, |
| datasets: [{ |
| label: 'Yield (t/ha)', |
| data: data.yield.values, |
| borderColor: '#22c55e', |
| backgroundColor: 'rgba(34, 197, 94, 0.1)', |
| borderWidth: 2, |
| tension: 0.4, |
| fill: true |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| plugins: { |
| legend: { display: false }, |
| tooltip: { |
| callbacks: { |
| label: (context) => `${context.dataset.label}: ${context.parsed.y} t/ha` |
| } |
| } |
| }, |
| scales: { |
| y: { |
| beginAtZero: false, |
| min: Math.min(...data.yield.values) - 0.5, |
| max: Math.max(...data.yield.values) + 0.5 |
| } |
| } |
| } |
| }); |
| |
| |
| const moistureCtx = document.getElementById('moistureChart').getContext('2d'); |
| new Chart(moistureCtx, { |
| type: 'line', |
| data: { |
| labels: data.moisture.labels, |
| datasets: [{ |
| label: 'Soil Moisture (%)', |
| data: data.moisture.values, |
| borderColor: '#3b82f6', |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', |
| borderWidth: 2, |
| tension: 0.4, |
| fill: true |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| plugins: { |
| legend: { display: false }, |
| tooltip: { |
| callbacks: { |
| label: (context) => `${context.dataset.label}: ${context.parsed.y}%` |
| } |
| } |
| }, |
| scales: { |
| y: { |
| beginAtZero: false, |
| min: Math.min(...data.moisture.values) - 5, |
| max: Math.max(...data.moisture.values) + 5 |
| } |
| } |
| } |
| }); |
| }) |
| |
| const container = document.getElementById('analytics-container'); |
| container.innerHTML = ` |
| <h1 class="text-2xl font-semibold mb-4">Analytics</h1> |
| <div class="grid md:grid-cols-2 gap-6"> |
| <div class="border rounded-xl p-4"> |
| <div class="font-semibold mb-2">Yield Trend (t/ha)</div> |
| <div class="chart-container"> |
| <canvas id="yieldChart"></canvas> |
| </div> |
| </div> |
| <div class="border rounded-xl p-4"> |
| <div class="font-semibold mb-2">Soil Moisture (%)</div> |
| <div class="chart-container"> |
| <canvas id="moistureChart"></canvas> |
| </div> |
| </div> |
| </div> |
| `; |
| |
| |
| const yieldCtx = document.getElementById('yieldChart').getContext('2d'); |
| new Chart(yieldCtx, { |
| type: 'line', |
| data: { |
| labels: data.yield.labels, |
| datasets: [{ |
| label: 'Yield (t/ha)', |
| data: data.yield.values, |
| borderColor: '#22c55e', |
| backgroundColor: 'rgba(34, 197, 94, 0.1)', |
| borderWidth: 2, |
| tension: 0.4, |
| fill: true |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| plugins: { |
| legend: { display: false }, |
| tooltip: { |
| callbacks: { |
| label: (context) => `${context.dataset.label}: ${context.parsed.y} t/ha` |
| } |
| } |
| }, |
| scales: { |
| y: { |
| beginAtZero: false, |
| min: Math.min(...data.yield.values) - 0.5, |
| max: Math.max(...data.yield.values) + 0.5 |
| } |
| } |
| } |
| }); |
| |
| |
| const moistureCtx = document.getElementById('moistureChart').getContext('2d'); |
| new Chart(moistureCtx, { |
| type: 'line', |
| data: { |
| labels: data.moisture.labels, |
| datasets: [{ |
| label: 'Soil Moisture (%)', |
| data: data.moisture.values, |
| borderColor: '#3b82f6', |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', |
| borderWidth: 2, |
| tension: 0.4, |
| fill: true |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| plugins: { |
| legend: { display: false }, |
| tooltip: { |
| callbacks: { |
| label: (context) => `${context.dataset.label}: ${context.parsed.y}%` |
| } |
| } |
| }, |
| scales: { |
| y: { |
| beginAtZero: false, |
| min: Math.min(...data.moisture.values) - 5, |
| max: Math.max(...data.moisture.values) + 5 |
| } |
| } |
| } |
| }); |
| } catch (e) { |
| const container = document.getElementById('analytics-container'); |
| container.innerHTML = ` |
| <div class="rounded-xl border p-6"> |
| <h1 class="text-2xl font-semibold mb-4">Analytics</h1> |
| <div class="text-red-600"> |
| Failed to load analytics data: ${e} |
| </div> |
| <button onclick="window.location.reload()" |
| class="mt-4 px-4 py-2 bg-primary-600 text-white rounded hover:bg-primary-700"> |
| Retry |
| </button> |
| </div> |
| `; |
| } |
| })(); |
| </script> |
| </body> |
| </html> |