defarm-sdk
Version:
DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain
958 lines (835 loc) • 31.6 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DeFarm SDK Dashboard</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: #0a0b0d;
min-height: 100vh;
color: #e4e4e7;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
header {
background: rgba(24, 24, 27, 0.8);
backdrop-filter: blur(10px);
border: 1px solid rgba(63, 63, 70, 0.5);
border-radius: 12px;
padding: 20px 30px;
margin-bottom: 30px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
display: flex;
align-items: center;
gap: 15px;
}
.logo h1 {
color: #10b981;
font-size: 28px;
font-weight: 700;
}
.logo .version {
background: rgba(16, 185, 129, 0.1);
border: 1px solid rgba(16, 185, 129, 0.3);
padding: 4px 10px;
border-radius: 20px;
font-size: 12px;
color: #10b981;
}
.status-bar {
display: flex;
gap: 15px;
}
.status-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 15px;
background: rgba(39, 39, 42, 0.5);
border: 1px solid rgba(63, 63, 70, 0.5);
border-radius: 20px;
font-size: 14px;
color: #a1a1aa;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
animation: pulse 2s infinite;
}
.status-indicator.connected {
background: #10b981;
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
}
.status-indicator.disconnected {
background: #ef4444;
box-shadow: 0 0 10px rgba(239, 68, 68, 0.5);
}
.status-indicator.initializing {
background: #f59e0b;
box-shadow: 0 0 10px rgba(245, 158, 11, 0.5);
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.metric-card {
background: rgba(24, 24, 27, 0.8);
backdrop-filter: blur(10px);
border: 1px solid rgba(63, 63, 70, 0.5);
border-radius: 12px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
transition: all 0.3s ease;
}
.metric-card:hover {
transform: translateY(-5px);
border-color: rgba(16, 185, 129, 0.3);
box-shadow: 0 15px 40px rgba(16, 185, 129, 0.1);
}
.metric-label {
color: #71717a;
font-size: 12px;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1.5px;
font-weight: 600;
}
.metric-value {
font-size: 36px;
font-weight: bold;
color: #fafafa;
margin-bottom: 10px;
}
.metric-change {
font-size: 14px;
color: #10b981;
}
.metric-change.negative {
color: #ef4444;
}
.chart-container {
background: rgba(24, 24, 27, 0.8);
backdrop-filter: blur(10px);
border: 1px solid rgba(63, 63, 70, 0.5);
border-radius: 12px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
margin-bottom: 30px;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.chart-title {
font-size: 18px;
font-weight: 600;
color: #fafafa;
}
.chart-options {
display: flex;
gap: 10px;
}
.chart-option {
padding: 6px 12px;
background: rgba(39, 39, 42, 0.5);
border: 1px solid rgba(63, 63, 70, 0.5);
color: #a1a1aa;
border-radius: 8px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s ease;
}
.chart-option:hover {
background: rgba(39, 39, 42, 0.8);
border-color: rgba(16, 185, 129, 0.3);
}
.chart-option.active {
background: rgba(16, 185, 129, 0.2);
border-color: #10b981;
color: #10b981;
}
#realtimeChart {
height: 300px;
position: relative;
}
.events-list {
background: rgba(24, 24, 27, 0.8);
backdrop-filter: blur(10px);
border: 1px solid rgba(63, 63, 70, 0.5);
border-radius: 12px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
max-height: 400px;
overflow-y: auto;
}
.event-item {
display: flex;
align-items: center;
padding: 12px;
border-bottom: 1px solid rgba(63, 63, 70, 0.3);
transition: background 0.3s ease;
}
.event-item:hover {
background: rgba(39, 39, 42, 0.3);
}
.event-icon {
width: 40px;
height: 40px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
font-size: 20px;
}
.event-icon.process {
background: rgba(59, 130, 246, 0.1);
border: 1px solid rgba(59, 130, 246, 0.3);
color: #3b82f6;
}
.event-icon.tokenize {
background: rgba(16, 185, 129, 0.1);
border: 1px solid rgba(16, 185, 129, 0.3);
color: #10b981;
}
.event-icon.error {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.3);
color: #ef4444;
}
.event-details {
flex: 1;
}
.event-title {
font-weight: 600;
margin-bottom: 4px;
color: #e4e4e7;
}
.event-time {
font-size: 12px;
color: #71717a;
}
.control-panel {
background: rgba(24, 24, 27, 0.8);
backdrop-filter: blur(10px);
border: 1px solid rgba(63, 63, 70, 0.5);
border-radius: 12px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
margin-bottom: 30px;
}
.control-buttons {
display: flex;
gap: 15px;
flex-wrap: wrap;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 10px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn-primary {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: white;
border: none;
}
.btn-primary:hover {
background: linear-gradient(135deg, #059669 0%, #047857 100%);
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(16, 185, 129, 0.4);
}
.btn-success {
background: rgba(16, 185, 129, 0.1);
color: #10b981;
border: 1px solid rgba(16, 185, 129, 0.3);
}
.btn-success:hover {
background: rgba(16, 185, 129, 0.2);
border-color: #10b981;
box-shadow: 0 5px 15px rgba(16, 185, 129, 0.2);
}
.btn-danger {
background: rgba(239, 68, 68, 0.1);
color: #ef4444;
border: 1px solid rgba(239, 68, 68, 0.3);
}
.btn-danger:hover {
background: rgba(239, 68, 68, 0.2);
border-color: #ef4444;
box-shadow: 0 5px 15px rgba(239, 68, 68, 0.2);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal.active {
display: flex;
}
.modal-content {
background: #18181b;
border: 1px solid rgba(63, 63, 70, 0.5);
border-radius: 12px;
padding: 30px;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-title {
font-size: 24px;
font-weight: 600;
color: #fafafa;
}
.modal-close {
background: none;
border: none;
font-size: 28px;
cursor: pointer;
color: #71717a;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #e4e4e7;
}
.form-input {
width: 100%;
padding: 10px 15px;
background: rgba(39, 39, 42, 0.5);
border: 1px solid rgba(63, 63, 70, 0.5);
color: #e4e4e7;
border-radius: 10px;
font-size: 14px;
transition: all 0.3s ease;
}
.form-input:focus {
outline: none;
border-color: #10b981;
background: rgba(39, 39, 42, 0.8);
}
.form-select {
width: 100%;
padding: 10px 15px;
background: rgba(39, 39, 42, 0.5);
border: 1px solid rgba(63, 63, 70, 0.5);
color: #e4e4e7;
border-radius: 10px;
font-size: 14px;
cursor: pointer;
}
.form-select option {
background: #18181b;
color: #e4e4e7;
}
@media (max-width: 768px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
header {
flex-direction: column;
gap: 20px;
}
.status-bar {
width: 100%;
justify-content: center;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">
<h1>🌾 DeFarm SDK</h1>
<span class="version">v1.0.0</span>
</div>
<div class="status-bar">
<div class="status-item">
<div class="status-indicator" id="systemStatus"></div>
<span id="systemStatusText">System</span>
</div>
<div class="status-item">
<div class="status-indicator" id="dbStatus"></div>
<span id="dbStatusText">Database</span>
</div>
<div class="status-item">
<div class="status-indicator" id="blockchainStatus"></div>
<span id="blockchainStatusText">Blockchain</span>
</div>
</div>
</header>
<div class="control-panel">
<div class="chart-header">
<h2 class="chart-title">Control Panel</h2>
</div>
<div class="control-buttons">
<button class="btn btn-primary" onclick="showInitModal()">Initialize SDK</button>
<button class="btn btn-success" onclick="testProcessing()">Test Processing</button>
<button class="btn btn-success" onclick="testTokenization()">Test Tokenization</button>
<button class="btn btn-danger" onclick="shutdownSDK()">Shutdown SDK</button>
</div>
</div>
<div class="dashboard-grid">
<div class="metric-card">
<div class="metric-label">Total Assets</div>
<div class="metric-value" id="totalAssets">0</div>
<div class="metric-change">+0% from last hour</div>
</div>
<div class="metric-card">
<div class="metric-label">Tokenizations</div>
<div class="metric-value" id="totalTokenizations">0</div>
<div class="metric-change">+0% from last hour</div>
</div>
<div class="metric-card">
<div class="metric-label">Processing Rate</div>
<div class="metric-value" id="processingRate">0</div>
<div class="metric-change">per minute</div>
</div>
<div class="metric-card">
<div class="metric-label">Error Rate</div>
<div class="metric-value" id="errorRate">0%</div>
<div class="metric-change negative" id="errorTrend"></div>
</div>
</div>
<div class="chart-container">
<div class="chart-header">
<h2 class="chart-title">Real-time Activity</h2>
<div class="chart-options">
<button class="chart-option active">1H</button>
<button class="chart-option">24H</button>
<button class="chart-option">7D</button>
</div>
</div>
<div id="realtimeChart">
<canvas id="activityCanvas" width="1000" height="300"></canvas>
</div>
</div>
<div class="events-list">
<div class="chart-header">
<h2 class="chart-title">Recent Events</h2>
</div>
<div id="eventsList"></div>
</div>
</div>
<!-- Initialize SDK Modal -->
<div class="modal" id="initModal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Initialize SDK</h3>
<button class="modal-close" onclick="closeModal('initModal')">×</button>
</div>
<form id="initForm">
<div class="form-group">
<label class="form-label">Deployment Mode</label>
<select class="form-select" name="deploymentMode">
<option value="hybrid">Hybrid (Recommended)</option>
<option value="on-premise">On-Premise</option>
<option value="cloud">Cloud</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Database Type</label>
<select class="form-select" name="dbType">
<option value="postgresql">PostgreSQL</option>
<option value="oracle">Oracle</option>
<option value="sqlserver">SQL Server</option>
<option value="mysql">MySQL</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Database Host</label>
<input class="form-input" type="text" name="dbHost" value="localhost" />
</div>
<div class="form-group">
<label class="form-label">Database Name</label>
<input class="form-input" type="text" name="dbName" value="defarm_test" />
</div>
<div class="form-group">
<label class="form-label">Enable Blockchain</label>
<select class="form-select" name="blockchainEnabled">
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Enable Relay (Gas-free)</label>
<select class="form-select" name="relayEnabled">
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Initialize</button>
</form>
</div>
</div>
<script>
let ws = null;
let metrics = {};
let chartData = [];
const maxDataPoints = 60;
// WebSocket connection
function connectWebSocket() {
ws = new WebSocket(`ws://${window.location.hostname}:${window.location.port || 3456}`);
ws.onopen = () => {
console.log('Connected to dashboard server');
updateConnectionStatus(true);
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'metrics') {
updateMetrics(message.data);
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
updateConnectionStatus(false);
};
ws.onclose = () => {
console.log('Disconnected from server');
updateConnectionStatus(false);
// Reconnect after 3 seconds
setTimeout(connectWebSocket, 3000);
};
}
function updateConnectionStatus(connected) {
const indicator = document.getElementById('systemStatus');
if (connected) {
indicator.className = 'status-indicator connected';
} else {
indicator.className = 'status-indicator disconnected';
}
}
function updateMetrics(data) {
metrics = data;
// Update metric cards
document.getElementById('totalAssets').textContent = data.totalAssets || 0;
document.getElementById('totalTokenizations').textContent = data.totalTokenizations || 0;
document.getElementById('processingRate').textContent = data.processingRate || 0;
const errorPercent = data.totalAssets > 0
? ((data.errorRate / data.totalAssets) * 100).toFixed(1)
: 0;
document.getElementById('errorRate').textContent = errorPercent + '%';
// Update status indicators
updateStatusIndicators(data);
// Update events list
updateEventsList(data.recentEvents || []);
// Update chart
updateChart(data);
}
function updateStatusIndicators(data) {
// System status
const systemIndicator = document.getElementById('systemStatus');
const systemText = document.getElementById('systemStatusText');
if (data.systemStatus === 'operational') {
systemIndicator.className = 'status-indicator connected';
systemText.textContent = 'System: Online';
} else if (data.systemStatus === 'initializing') {
systemIndicator.className = 'status-indicator initializing';
systemText.textContent = 'System: Initializing';
} else {
systemIndicator.className = 'status-indicator disconnected';
systemText.textContent = 'System: Offline';
}
// Database status
const dbIndicator = document.getElementById('dbStatus');
const dbText = document.getElementById('dbStatusText');
if (data.databaseStatus === 'connected') {
dbIndicator.className = 'status-indicator connected';
dbText.textContent = 'Database: Connected';
} else {
dbIndicator.className = 'status-indicator disconnected';
dbText.textContent = 'Database: Disconnected';
}
// Blockchain status
const blockchainIndicator = document.getElementById('blockchainStatus');
const blockchainText = document.getElementById('blockchainStatusText');
if (data.blockchainStatus === 'connected') {
blockchainIndicator.className = 'status-indicator connected';
blockchainText.textContent = 'Blockchain: Connected';
} else if (data.blockchainStatus === 'disabled') {
blockchainIndicator.className = 'status-indicator disconnected';
blockchainText.textContent = 'Blockchain: Disabled';
} else {
blockchainIndicator.className = 'status-indicator disconnected';
blockchainText.textContent = 'Blockchain: Disconnected';
}
}
function updateEventsList(events) {
const container = document.getElementById('eventsList');
container.innerHTML = '';
events.slice(0, 10).forEach(event => {
const item = document.createElement('div');
item.className = 'event-item';
const icon = document.createElement('div');
icon.className = `event-icon ${event.type}`;
icon.textContent = getEventIcon(event.type);
const details = document.createElement('div');
details.className = 'event-details';
const title = document.createElement('div');
title.className = 'event-title';
title.textContent = getEventTitle(event);
const time = document.createElement('div');
time.className = 'event-time';
time.textContent = formatTime(event.timestamp);
details.appendChild(title);
details.appendChild(time);
item.appendChild(icon);
item.appendChild(details);
container.appendChild(item);
});
}
function getEventIcon(type) {
switch(type) {
case 'process': return '⚙️';
case 'tokenize': return '🪙';
case 'error': return '❌';
default: return '📋';
}
}
function getEventTitle(event) {
const base = event.type.charAt(0).toUpperCase() + event.type.slice(1);
return event.success ? `${base} Successful` : `${base} Failed`;
}
function formatTime(timestamp) {
const date = new Date(timestamp);
const now = new Date();
const diff = now - date;
if (diff < 60000) {
return 'Just now';
} else if (diff < 3600000) {
return Math.floor(diff / 60000) + ' minutes ago';
} else {
return date.toLocaleTimeString();
}
}
function updateChart(data) {
// Add new data point
chartData.push({
time: Date.now(),
assets: data.totalAssets,
tokenizations: data.totalTokenizations,
errors: data.errorRate
});
// Keep only last N points
if (chartData.length > maxDataPoints) {
chartData.shift();
}
drawChart();
}
function drawChart() {
const canvas = document.getElementById('activityCanvas');
const ctx = canvas.getContext('2d');
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (chartData.length < 2) return;
// Draw grid
ctx.strokeStyle = 'rgba(63, 63, 70, 0.3)';
ctx.lineWidth = 1;
for (let i = 0; i <= 5; i++) {
const y = (canvas.height / 5) * i;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
// Draw data lines
const xStep = canvas.width / (maxDataPoints - 1);
const maxValue = Math.max(...chartData.map(d => d.assets), 10);
// Assets line with gradient
const gradient1 = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient1.addColorStop(0, 'rgba(16, 185, 129, 0.8)');
gradient1.addColorStop(1, 'rgba(16, 185, 129, 0.1)');
// Assets line
ctx.strokeStyle = '#10b981';
ctx.lineWidth = 2;
ctx.beginPath();
chartData.forEach((point, index) => {
const x = index * xStep;
const y = canvas.height - (point.assets / maxValue) * canvas.height;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
// Tokenizations line
ctx.strokeStyle = '#3b82f6';
ctx.lineWidth = 2;
ctx.beginPath();
chartData.forEach((point, index) => {
const x = index * xStep;
const y = canvas.height - (point.tokenizations / maxValue) * canvas.height;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
}
// Modal functions
function showInitModal() {
document.getElementById('initModal').classList.add('active');
}
function closeModal(modalId) {
document.getElementById(modalId).classList.remove('active');
}
// SDK control functions
document.getElementById('initForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const config = {
deploymentMode: formData.get('deploymentMode'),
database: {
type: formData.get('dbType'),
host: formData.get('dbHost'),
database: formData.get('dbName'),
user: 'test_user',
password: 'test_password'
},
blockchain: {
enabled: formData.get('blockchainEnabled') === 'true'
},
relay: {
enabled: formData.get('relayEnabled') === 'true'
}
};
try {
const response = await fetch('/api/sdk/initialize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
});
const result = await response.json();
if (result.success) {
alert('SDK initialized successfully!');
closeModal('initModal');
} else {
alert('Error: ' + result.error);
}
} catch (error) {
alert('Failed to initialize SDK: ' + error.message);
}
});
async function testProcessing() {
const testData = {
asset_type: 'livestock',
breed: 'Angus',
weight: 450 + Math.random() * 100,
location: 'Test Farm',
timestamp: Date.now()
};
try {
const response = await fetch('/api/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(testData)
});
const result = await response.json();
if (response.ok) {
alert('Processing successful!');
} else {
alert('Processing failed: ' + result.error);
}
} catch (error) {
alert('Error: ' + error.message);
}
}
async function testTokenization() {
const testData = {
asset_type: 'cattle',
quantity: Math.floor(Math.random() * 100) + 1,
location: 'Brazil',
certifications: ['organic']
};
try {
const response = await fetch('/api/tokenize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(testData)
});
const result = await response.json();
if (response.ok) {
alert('Tokenization successful! Token ID: ' + (result.tokenId || 'Pending'));
} else {
alert('Tokenization failed: ' + result.error);
}
} catch (error) {
alert('Error: ' + error.message);
}
}
async function shutdownSDK() {
if (!confirm('Are you sure you want to shutdown the SDK?')) return;
try {
const response = await fetch('/api/sdk/shutdown', {
method: 'POST'
});
const result = await response.json();
if (result.success) {
alert('SDK shutdown successfully');
} else {
alert('Error: ' + result.error);
}
} catch (error) {
alert('Failed to shutdown: ' + error.message);
}
}
// Initialize
connectWebSocket();
</script>
</body>
</html>