Create templates/index.html
Browse files- templates/index.html +1364 -0
templates/index.html
ADDED
@@ -0,0 +1,1364 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Water Footprint Calculator - Agricultural Products</title>
|
7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
8 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
|
9 |
+
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
10 |
+
<style>
|
11 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
12 |
+
|
13 |
+
* { font-family: 'Inter', sans-serif; }
|
14 |
+
|
15 |
+
body {
|
16 |
+
background: linear-gradient(135deg, #f0fff4 0%, #c6f6d5 100%); /* Light green base */
|
17 |
+
background-size: 400% 400%;
|
18 |
+
animation: gradientShift 18s ease infinite; /* Slightly slower animation */
|
19 |
+
min-height: 100vh;
|
20 |
+
color: #2D3748; /* Dark text color */
|
21 |
+
}
|
22 |
+
|
23 |
+
@keyframes gradientShift {
|
24 |
+
0% { background-position: 0% 50%; }
|
25 |
+
50% { background-position: 100% 50%; }
|
26 |
+
100% { background-position: 0% 50%; }
|
27 |
+
}
|
28 |
+
|
29 |
+
.glass-effect {
|
30 |
+
background: rgba(255, 255, 255, 0.85); /* More opaque white */
|
31 |
+
backdrop-filter: blur(12px);
|
32 |
+
-webkit-backdrop-filter: blur(12px);
|
33 |
+
border: 1px solid rgba(255, 255, 255, 0.9); /* Very light border */
|
34 |
+
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.08); /* Soft shadow */
|
35 |
+
}
|
36 |
+
|
37 |
+
.hover-3d {
|
38 |
+
transform-style: preserve-3d;
|
39 |
+
transition: transform 0.5s ease-out;
|
40 |
+
}
|
41 |
+
|
42 |
+
.hover-3d:hover {
|
43 |
+
transform: perspective(1000px) rotateX(1deg) rotateY(1deg); /* Minimal rotation */
|
44 |
+
}
|
45 |
+
|
46 |
+
.shine-effect {
|
47 |
+
position: relative;
|
48 |
+
overflow: hidden;
|
49 |
+
}
|
50 |
+
|
51 |
+
.shine-effect::before {
|
52 |
+
content: '';
|
53 |
+
position: absolute;
|
54 |
+
top: 0;
|
55 |
+
left: -120%; /* Start further left */
|
56 |
+
width: 60%; /* Wider shine */
|
57 |
+
height: 100%;
|
58 |
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); /* More prominent shine */
|
59 |
+
animation: shine 4s infinite ease-in-out; /* Slower shine */
|
60 |
+
}
|
61 |
+
|
62 |
+
@keyframes shine {
|
63 |
+
0% { left: -120%; }
|
64 |
+
60% { left: 120%; } /* Go further right */
|
65 |
+
100% { left: 120%; }
|
66 |
+
}
|
67 |
+
|
68 |
+
|
69 |
+
.float-animation {
|
70 |
+
animation: float 7s ease-in-out infinite; /* Slower float */
|
71 |
+
}
|
72 |
+
|
73 |
+
@keyframes float {
|
74 |
+
0%, 100% { transform: translateY(0px); }
|
75 |
+
50% { transform: translateY(-6px); } /* Slightly more float */
|
76 |
+
}
|
77 |
+
|
78 |
+
.drop-shadow-custom {
|
79 |
+
filter: drop-shadow(0 6px 12px rgba(74, 222, 128, 0.3)); /* Softer green shadow */
|
80 |
+
}
|
81 |
+
|
82 |
+
|
83 |
+
.pulse-glow {
|
84 |
+
animation: pulseGlow 2.5s ease-in-out infinite alternate; /* Slower pulse */
|
85 |
+
}
|
86 |
+
|
87 |
+
@keyframes pulseGlow {
|
88 |
+
from { box-shadow: 0 0 12px rgba(74, 222, 128, 0.7); } /* Stronger green glow */
|
89 |
+
to { box-shadow: 0 0 20px rgba(74, 222, 128, 1); }
|
90 |
+
}
|
91 |
+
|
92 |
+
.loading-dots::after {
|
93 |
+
content: '';
|
94 |
+
display: inline-block;
|
95 |
+
animation: loadingDots 1.5s infinite steps(3, end);
|
96 |
+
}
|
97 |
+
|
98 |
+
@keyframes loadingDots {
|
99 |
+
0%, 20% { content: ''; }
|
100 |
+
40% { content: '.'; }
|
101 |
+
60% { content: '..'; }
|
102 |
+
80%, 100% { content: '...'; }
|
103 |
+
}
|
104 |
+
|
105 |
+
/* Load-in Animations */
|
106 |
+
.slide-in {
|
107 |
+
animation: slideIn 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; /* Smoother curve */
|
108 |
+
opacity: 0;
|
109 |
+
}
|
110 |
+
|
111 |
+
@keyframes slideIn {
|
112 |
+
from { transform: translateY(20px); opacity: 0; }
|
113 |
+
to { transform: translateY(0); opacity: 1; }
|
114 |
+
}
|
115 |
+
|
116 |
+
.scale-in {
|
117 |
+
animation: scaleIn 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; /* Smoother curve */
|
118 |
+
opacity: 0;
|
119 |
+
}
|
120 |
+
|
121 |
+
@keyframes scaleIn {
|
122 |
+
from { transform: scale(0.98); opacity: 0; }
|
123 |
+
to { transform: scale(1); opacity: 1; }
|
124 |
+
}
|
125 |
+
|
126 |
+
.fade-in {
|
127 |
+
animation: fadeIn 0.7s ease-out forwards;
|
128 |
+
opacity: 0;
|
129 |
+
}
|
130 |
+
@keyframes fadeIn {
|
131 |
+
from { opacity: 0; }
|
132 |
+
to { opacity: 1; }
|
133 |
+
}
|
134 |
+
|
135 |
+
/* Hover Effects */
|
136 |
+
.hover-lift {
|
137 |
+
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
138 |
+
}
|
139 |
+
.hover-lift:hover {
|
140 |
+
transform: translateY(-5px);
|
141 |
+
box-shadow: 0 12px 25px rgba(0, 0, 0, 0.1); /* Increased hover shadow */
|
142 |
+
}
|
143 |
+
|
144 |
+
|
145 |
+
.upload-area {
|
146 |
+
border: 3px dashed #68D391; /* Lighter Green */
|
147 |
+
transition: all 0.3s ease;
|
148 |
+
}
|
149 |
+
|
150 |
+
.upload-area.has-image {
|
151 |
+
border-style: solid;
|
152 |
+
border-color: #38A169; /* Darker Green */
|
153 |
+
background: rgba(74, 222, 128, 0.08); /* Light greenish background */
|
154 |
+
display: flex;
|
155 |
+
flex-direction: column;
|
156 |
+
align-items: center;
|
157 |
+
justify-content: center;
|
158 |
+
padding: 2rem; /* Slightly adjusted padding when image is present */
|
159 |
+
}
|
160 |
+
|
161 |
+
|
162 |
+
.upload-area:hover {
|
163 |
+
border-color: #38A169;
|
164 |
+
background: rgba(74, 222, 128, 0.05);
|
165 |
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); /* Subtle shadow on hover */
|
166 |
+
}
|
167 |
+
|
168 |
+
.upload-area.dragover {
|
169 |
+
border-color: #2F855A; /* Even Darker Green */
|
170 |
+
background: rgba(74, 222, 128, 0.12);
|
171 |
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
|
172 |
+
}
|
173 |
+
|
174 |
+
.upload-area img {
|
175 |
+
max-width: 180px; /* Slightly larger preview */
|
176 |
+
max-height: 180px;
|
177 |
+
object-fit: cover;
|
178 |
+
border-radius: 0.75rem; /* More rounded corners */
|
179 |
+
margin-bottom: 1.5rem; /* More space below image */
|
180 |
+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
181 |
+
transition: transform 0.3s ease;
|
182 |
+
}
|
183 |
+
|
184 |
+
.upload-area img:hover {
|
185 |
+
transform: scale(1.05); /* Subtle zoom on image hover */
|
186 |
+
}
|
187 |
+
|
188 |
+
.upload-area.has-image .upload-area-content {
|
189 |
+
text-align: center;
|
190 |
+
}
|
191 |
+
|
192 |
+
|
193 |
+
.metric-card {
|
194 |
+
background: rgba(255, 255, 255, 0.9); /* Very light card background */
|
195 |
+
border: 1px solid rgba(255, 255, 255, 0.95); /* Almost invisible border */
|
196 |
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); /* Soft initial shadow */
|
197 |
+
color: #2D3748;
|
198 |
+
}
|
199 |
+
|
200 |
+
.metric-card h3, .metric-card h4 { color: #2D3748; }
|
201 |
+
.metric-card p { color: #4A5568; }
|
202 |
+
|
203 |
+
/* Apply hover-lift to metric-card */
|
204 |
+
.metric-card.hover-lift:hover {
|
205 |
+
transform: translateY(-5px);
|
206 |
+
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1); /* More pronounced hover shadow */
|
207 |
+
}
|
208 |
+
|
209 |
+
.tab-button {
|
210 |
+
transition: all 0.3s ease;
|
211 |
+
border: 1px solid transparent;
|
212 |
+
background-color: rgba(255, 255, 255, 0.6); /* Lighter inactive button */
|
213 |
+
color: #4A5568;
|
214 |
+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); /* Subtle shadow on inactive */
|
215 |
+
}
|
216 |
+
|
217 |
+
.tab-button.active {
|
218 |
+
background: linear-gradient(45deg, #48BB78, #81E6D9); /* Green to Teal gradient */
|
219 |
+
color: white;
|
220 |
+
border-color: #81E6D9;
|
221 |
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); /* Stronger shadow for active */
|
222 |
+
transform: translateY(-2px); /* Slight lift on active */
|
223 |
+
}
|
224 |
+
|
225 |
+
.tab-button:not(.active):hover {
|
226 |
+
background-color: rgba(255, 255, 255, 0.9); /* Lighter hover for inactive */
|
227 |
+
border-color: rgba(0, 0, 0, 0.05);
|
228 |
+
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
|
229 |
+
}
|
230 |
+
|
231 |
+
/* Chart Styling */
|
232 |
+
canvas {
|
233 |
+
background: rgba(255, 255, 255, 1);
|
234 |
+
border-radius: 0.75rem;
|
235 |
+
padding: 1.5rem;
|
236 |
+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
237 |
+
}
|
238 |
+
|
239 |
+
/* Custom Colors for elements on LIGHT background */
|
240 |
+
.text-primary { color: #38A169; } /* Dark Green */
|
241 |
+
.text-secondary { color: #319795; } /* Dark Teal */
|
242 |
+
.text-accent { color: #D69E2E; } /* Orange-Yellow */
|
243 |
+
|
244 |
+
/* Severity Colors */
|
245 |
+
.severity-low { color: #38A169; } /* Dark Green */
|
246 |
+
.severity-medium { color: #D69E2E; } /* Orange-Yellow */
|
247 |
+
.severity-high { color: #C53030; } /* Darker Red */
|
248 |
+
.severity-very-high { color: #9B2C2C; } /* Even Darker Red */
|
249 |
+
.severity-unknown { color: #718096; } /* Gray */
|
250 |
+
|
251 |
+
|
252 |
+
.bg-card { background: rgba(255, 255, 255, 0.9); }
|
253 |
+
.bg-chart { background: rgba(255, 255, 255, 1); }
|
254 |
+
|
255 |
+
/* Table Styling for Comparisons */
|
256 |
+
.comparison-table {
|
257 |
+
width: 100%;
|
258 |
+
border-collapse: collapse;
|
259 |
+
margin-top: 1.5rem;
|
260 |
+
color: #2D3748;
|
261 |
+
}
|
262 |
+
|
263 |
+
.comparison-table th, .comparison-table td {
|
264 |
+
padding: 0.9rem; /* Slightly more padding */
|
265 |
+
text-align: left;
|
266 |
+
border-bottom: 1px solid rgba(0, 0, 0, 0.06); /* Very light border */
|
267 |
+
}
|
268 |
+
|
269 |
+
.comparison-table th {
|
270 |
+
background-color: rgba(0, 0, 0, 0.03); /* Very light header background */
|
271 |
+
color: #2D3748;
|
272 |
+
font-weight: 600;
|
273 |
+
text-transform: uppercase;
|
274 |
+
font-size: 0.9rem;
|
275 |
+
}
|
276 |
+
|
277 |
+
.comparison-table td {
|
278 |
+
color: #4A5568;
|
279 |
+
}
|
280 |
+
|
281 |
+
.comparison-table tbody tr {
|
282 |
+
transition: background-color 0.2s ease;
|
283 |
+
}
|
284 |
+
|
285 |
+
.comparison-table tbody tr:hover {
|
286 |
+
background-color: rgba(0, 0, 0, 0.05); /* Slightly darker hover */
|
287 |
+
}
|
288 |
+
|
289 |
+
/* Water Definitions Section */
|
290 |
+
.water-definitions {
|
291 |
+
background: rgba(255, 255, 255, 0.9);
|
292 |
+
border: 1px solid rgba(255, 255, 255, 0.95);
|
293 |
+
border-radius: 0.75rem;
|
294 |
+
padding: 1.5rem;
|
295 |
+
margin-top: 2rem;
|
296 |
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
297 |
+
color: #2D3748;
|
298 |
+
}
|
299 |
+
|
300 |
+
.water-definitions h3 {
|
301 |
+
color: #2F855A;
|
302 |
+
margin-bottom: 1.2rem; /* More space below heading */
|
303 |
+
font-weight: 700;
|
304 |
+
font-size: 1.6rem;
|
305 |
+
}
|
306 |
+
|
307 |
+
.water-definitions dl {
|
308 |
+
display: grid;
|
309 |
+
grid-template-columns: auto 1fr;
|
310 |
+
gap: 1.2rem; /* Slightly more gap */
|
311 |
+
}
|
312 |
+
|
313 |
+
.water-definitions dt {
|
314 |
+
font-weight: 600;
|
315 |
+
color: #38A169;
|
316 |
+
}
|
317 |
+
|
318 |
+
.water-definitions dd {
|
319 |
+
color: #4A5568;
|
320 |
+
margin-left: 0;
|
321 |
+
}
|
322 |
+
|
323 |
+
/* Hover for list items in tabs */
|
324 |
+
.tab-content-list-item {
|
325 |
+
transition: background-color 0.2s ease;
|
326 |
+
}
|
327 |
+
.tab-content-list-item:hover {
|
328 |
+
background-color: rgba(0, 0, 0, 0.03); /* Light hover background */
|
329 |
+
}
|
330 |
+
|
331 |
+
</style>
|
332 |
+
</head>
|
333 |
+
<body>
|
334 |
+
<!-- Header -->
|
335 |
+
<header class="glass-effect p-6 mb-12 slide-in">
|
336 |
+
<div class="container mx-auto">
|
337 |
+
<div class="flex flex-col sm:flex-row items-center justify-center space-y-4 sm:space-y-0 sm:space-x-4">
|
338 |
+
<div class="text-5xl text-green-500 drop-shadow-custom">π§</div>
|
339 |
+
<h1 class="text-3xl sm:text-4xl font-bold text-gray-800 drop-shadow-lg text-center">Water Footprint Calculator</h1>
|
340 |
+
<div class="text-5xl text-green-400 float-animation">π±</div>
|
341 |
+
</div>
|
342 |
+
<p class="text-center text-gray-600 mt-3 text-base sm:text-lg">Discover the water impact of agricultural products</p>
|
343 |
+
</div>
|
344 |
+
</header>
|
345 |
+
|
346 |
+
<div class="container mx-auto px-4 max-w-6xl">
|
347 |
+
<!-- Upload Section -->
|
348 |
+
<div class="glass-effect rounded-2xl p-8 mb-12 shine-effect hover-3d origin-center hover-lift" id="uploadContainer"> <!-- Added hover-lift -->
|
349 |
+
<h2 class="text-2xl sm:text-3xl font-semibold text-gray-800 mb-8 text-center">
|
350 |
+
<i class="fas fa-camera mr-3 text-primary"></i>Upload Product Image
|
351 |
+
</h2>
|
352 |
+
|
353 |
+
<div class="upload-area rounded-xl p-10 text-center cursor-pointer transition duration-300 ease-in-out flex flex-col items-center justify-center" id="uploadArea">
|
354 |
+
<img id="imagePreview" src="" alt="Image Preview" class="hidden rounded-md mb-4">
|
355 |
+
<div class="upload-area-content">
|
356 |
+
<div class="text-6xl sm:text-7xl mb-6">πΈ</div>
|
357 |
+
<p class="text-xl sm:text-2xl text-green-700 font-medium mb-3">Drop your image here or click to browse</p>
|
358 |
+
<p class="text-green-600 text-base sm:text-lg">Supports JPG, PNG, WebP formats</p>
|
359 |
+
</div>
|
360 |
+
<input type="file" id="imageInput" accept="image/*" class="hidden">
|
361 |
+
</div>
|
362 |
+
|
363 |
+
<button
|
364 |
+
id="analyzeBtn"
|
365 |
+
class="w-full mt-8 bg-gradient-to-r from-green-500 to-teal-500 text-white py-4 sm:py-5 px-6 sm:px-8 rounded-xl font-bold text-lg sm:text-xl hover:from-green-600 hover:to-teal-600 transform hover:scale-105 transition-all duration-300 pulse-glow disabled:opacity-40 disabled:cursor-not-allowed"
|
366 |
+
disabled
|
367 |
+
>
|
368 |
+
<i class="fas fa-chart-line mr-3"></i>
|
369 |
+
Analyze Water Footprint
|
370 |
+
</button>
|
371 |
+
</div>
|
372 |
+
|
373 |
+
<!-- Loading Section -->
|
374 |
+
<div id="loadingSection" class="hidden glass-effect rounded-2xl p-8 mb-12 text-center scale-in">
|
375 |
+
<div class="text-5xl sm:text-6xl mb-6 text-green-500 pulse-glow">π§</div> <!-- Added pulse-glow to icon -->
|
376 |
+
<h3 class="text-2xl sm:text-3xl font-semibold text-gray-800 mb-4">
|
377 |
+
<span id="loadingMessage">Analyzing Image</span><span class="loading-dots"></span>
|
378 |
+
</h3>
|
379 |
+
<p id="loadingSubMessage" class="text-gray-600 text-base sm:text-lg">Processing and calculating environmental impact...</p>
|
380 |
+
<div class="mt-8">
|
381 |
+
<div class="w-full bg-gray-300 rounded-full h-3">
|
382 |
+
<div class="bg-gradient-to-r from-green-500 to-teal-500 h-3 rounded-full animate-pulse" style="width: 80%"></div>
|
383 |
+
</div>
|
384 |
+
</div>
|
385 |
+
</div>
|
386 |
+
|
387 |
+
<!-- Error Section -->
|
388 |
+
<div id="errorSection" class="hidden glass-effect rounded-2xl p-8 mb-12 text-center scale-in bg-red-100 border border-red-400 text-red-800">
|
389 |
+
<div class="text-5xl sm:text-6xl mb-6 text-red-500">β</div>
|
390 |
+
<h3 class="text-2xl sm:text-3xl font-semibold text-red-800 mb-4">Analysis Failed</h3>
|
391 |
+
<p id="errorMessage" class="text-red-700 text-base sm:text-lg mb-4">An unexpected error occurred during analysis.</p>
|
392 |
+
<p id="fallbackMessage" class="text-yellow-800 text-sm italic hidden"></p>
|
393 |
+
</div>
|
394 |
+
|
395 |
+
|
396 |
+
<!-- Results Section -->
|
397 |
+
<div id="resultsSection" class="hidden">
|
398 |
+
<!-- Product Identification -->
|
399 |
+
<div class="glass-effect rounded-2xl p-8 mb-12 slide-in hover-lift" style="animation-delay: 0.2s;"> <!-- Added hover-lift -->
|
400 |
+
<h2 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6">
|
401 |
+
<i class="fas fa-search mr-3 text-primary"></i>Product Identification
|
402 |
+
</h2>
|
403 |
+
<div id="productInfo" class="grid md:grid-cols-2 gap-8">
|
404 |
+
<!-- Product info will be populated here -->
|
405 |
+
</div>
|
406 |
+
</div>
|
407 |
+
|
408 |
+
<!-- Water Footprint Overview -->
|
409 |
+
<div class="glass-effect rounded-2xl p-8 mb-12 scale-in hover-lift" style="animation-delay: 0.4s;"> <!-- Added hover-lift -->
|
410 |
+
<h2 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6">
|
411 |
+
<i class="fas fa-tint mr-3 text-green-600"></i>Water Footprint Analysis
|
412 |
+
</h2>
|
413 |
+
<div id="overallSeverity" class="mb-8 text-center p-4 rounded-lg border border-gray-200 bg-white shadow-sm"> <!-- Lighter border -->
|
414 |
+
<h3 class="text-xl font-semibold text-gray-800 mb-2">Overall Water Footprint Severity:</h3>
|
415 |
+
<p class="text-3xl font-bold severity-unknown" id="severityText">Calculating...</p>
|
416 |
+
</div>
|
417 |
+
|
418 |
+
<div id="waterFootprintMetrics" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-10">
|
419 |
+
<!-- Metrics will be populated here -->
|
420 |
+
</div>
|
421 |
+
|
422 |
+
<!-- Water Definitions Section -->
|
423 |
+
<div id="waterDefinitions" class="water-definitions hidden">
|
424 |
+
<!-- Definitions populated here by JS -->
|
425 |
+
</div>
|
426 |
+
|
427 |
+
|
428 |
+
<div class="grid md:grid-cols-2 gap-8 mt-8">
|
429 |
+
<div class="bg-chart rounded-xl p-6 shadow-lg">
|
430 |
+
<canvas id="waterBreakdownChart"></canvas>
|
431 |
+
</div>
|
432 |
+
<div class="bg-chart rounded-xl p-6 shadow-lg">
|
433 |
+
<canvas id="regionalComparisonChart"></canvas>
|
434 |
+
</div>
|
435 |
+
</div>
|
436 |
+
</div>
|
437 |
+
|
438 |
+
<!-- Detailed Analysis Tabs -->
|
439 |
+
<div class="glass-effect rounded-2xl p-8 mb-12 slide-in hover-lift" style="animation-delay: 0.6s;"> <!-- Added hover-lift -->
|
440 |
+
<h2 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6">
|
441 |
+
<i class="fas fa-chart-pie mr-3 text-teal-700"></i>Detailed Analysis
|
442 |
+
</h2>
|
443 |
+
<div class="flex flex-wrap gap-3 mb-8" id="tabButtons">
|
444 |
+
<button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium active" data-tab="environmental">
|
445 |
+
<i class="fas fa-leaf mr-2 text-green-700"></i>Environmental Impact
|
446 |
+
</button>
|
447 |
+
<button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium" data-tab="production">
|
448 |
+
<i class="fas fa-industry mr-2 text-gray-600"></i>Production Insights
|
449 |
+
</button>
|
450 |
+
<button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium" data-tab="comparisons">
|
451 |
+
<i class="fas fa-chart-bar mr-2 text-orange-700"></i>Comparisons
|
452 |
+
</button>
|
453 |
+
<button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium" data-tab="recommendations">
|
454 |
+
<i class="fas fa-lightbulb mr-2 text-purple-700"></i>Recommendations
|
455 |
+
</button>
|
456 |
+
<button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium" data-tab="more-metrics">
|
457 |
+
<i class="fas fa-balance-scale mr-2 text-cyan-700"></i>More Metrics
|
458 |
+
</button>
|
459 |
+
</div>
|
460 |
+
|
461 |
+
<div id="tabContent" class="fade-in">
|
462 |
+
<!-- Tab content will be populated here -->
|
463 |
+
</div>
|
464 |
+
</div>
|
465 |
+
|
466 |
+
<!-- Fun Facts Section -->
|
467 |
+
<div class="glass-effect rounded-2xl p-8 mb-12 scale-in hover-lift" style="animation-delay: 0.8s;"> <!-- Added hover-lift -->
|
468 |
+
<h2 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6">
|
469 |
+
<i class="fas fa-star mr-3 text-yellow-600"></i>Interesting Facts
|
470 |
+
</h2>
|
471 |
+
<div id="funFacts" class="grid md:grid-cols-2 gap-6">
|
472 |
+
<!-- Facts will be populated here -->
|
473 |
+
</div>
|
474 |
+
</div>
|
475 |
+
</div>
|
476 |
+
</div>
|
477 |
+
|
478 |
+
<!-- Footer removed -->
|
479 |
+
|
480 |
+
<script>
|
481 |
+
let uploadedImage = null;
|
482 |
+
let currentImageFile = null;
|
483 |
+
let currentCharts = {}; // Store chart instances
|
484 |
+
|
485 |
+
// DOM Elements
|
486 |
+
const uploadContainer = document.getElementById('uploadContainer'); // New
|
487 |
+
const uploadArea = document.getElementById('uploadArea');
|
488 |
+
const imageInput = document.getElementById('imageInput');
|
489 |
+
const imagePreview = document.getElementById('imagePreview');
|
490 |
+
const uploadAreaContent = uploadArea.querySelector('.upload-area-content');
|
491 |
+
const analyzeBtn = document.getElementById('analyzeBtn');
|
492 |
+
const loadingSection = document.getElementById('loadingSection');
|
493 |
+
const loadingMessage = document.getElementById('loadingMessage');
|
494 |
+
const loadingSubMessage = document.getElementById('loadingSubMessage');
|
495 |
+
const resultsSection = document.getElementById('resultsSection');
|
496 |
+
const errorSection = document.getElementById('errorSection');
|
497 |
+
const errorMessage = document.getElementById('errorMessage');
|
498 |
+
const fallbackMessage = document.getElementById('fallbackMessage');
|
499 |
+
const severityText = document.getElementById('severityText');
|
500 |
+
const waterDefinitionsSection = document.getElementById('waterDefinitions');
|
501 |
+
|
502 |
+
|
503 |
+
// Upload functionality
|
504 |
+
uploadArea.addEventListener('click', (e) => {
|
505 |
+
// Only trigger input click if not clicking on the image itself when previewing
|
506 |
+
if (e.target !== imagePreview && !uploadArea.classList.contains('has-image')) { // Check if it's the preview or if there's no image
|
507 |
+
imageInput.click();
|
508 |
+
} else if (e.target === imagePreview && uploadArea.classList.contains('has-image')) {
|
509 |
+
// If clicking the image preview itself, also trigger input click to allow changing image
|
510 |
+
imageInput.click();
|
511 |
+
} else if (e.target !== imagePreview && uploadArea.classList.contains('has-image')) {
|
512 |
+
// If clicking the text area when an image is present, also trigger input click
|
513 |
+
imageInput.click();
|
514 |
+
}
|
515 |
+
});
|
516 |
+
uploadArea.addEventListener('dragover', handleDragOver);
|
517 |
+
uploadArea.addEventListener('dragleave', handleDragLeave);
|
518 |
+
uploadArea.addEventListener('drop', handleDrop);
|
519 |
+
imageInput.addEventListener('change', handleFileSelect);
|
520 |
+
|
521 |
+
function handleDragOver(e) {
|
522 |
+
e.preventDefault();
|
523 |
+
uploadArea.classList.add('dragover');
|
524 |
+
}
|
525 |
+
|
526 |
+
function handleDragLeave(e) {
|
527 |
+
e.preventDefault();
|
528 |
+
uploadArea.classList.remove('dragover');
|
529 |
+
}
|
530 |
+
|
531 |
+
function handleDrop(e) {
|
532 |
+
e.preventDefault();
|
533 |
+
uploadArea.classList.remove('dragover');
|
534 |
+
const files = e.dataTransfer.files;
|
535 |
+
if (files.length > 0) {
|
536 |
+
handleFile(files[0]);
|
537 |
+
}
|
538 |
+
}
|
539 |
+
|
540 |
+
function handleFileSelect(e) {
|
541 |
+
const file = e.target.files[0];
|
542 |
+
if (file) {
|
543 |
+
handleFile(file);
|
544 |
+
}
|
545 |
+
}
|
546 |
+
|
547 |
+
function handleFile(file) {
|
548 |
+
if (!file.type.startsWith('image/')) {
|
549 |
+
alert('Please select a valid image file.');
|
550 |
+
return;
|
551 |
+
}
|
552 |
+
|
553 |
+
currentImageFile = file;
|
554 |
+
const reader = new FileReader();
|
555 |
+
reader.onload = function(e) {
|
556 |
+
uploadedImage = e.target.result;
|
557 |
+
updateUploadArea(file.name, uploadedImage);
|
558 |
+
analyzeBtn.disabled = false;
|
559 |
+
};
|
560 |
+
reader.readAsDataURL(file);
|
561 |
+
}
|
562 |
+
|
563 |
+
function updateUploadArea(fileName, imageUrl) {
|
564 |
+
uploadArea.classList.remove('dragover');
|
565 |
+
uploadArea.classList.add('has-image');
|
566 |
+
uploadArea.classList.remove('shine-effect'); // Remove animation after upload
|
567 |
+
uploadContainer.classList.remove('shine-effect'); // Remove from container too
|
568 |
+
|
569 |
+
// Display image preview
|
570 |
+
imagePreview.src = imageUrl;
|
571 |
+
imagePreview.classList.remove('hidden');
|
572 |
+
|
573 |
+
// Update text content
|
574 |
+
uploadAreaContent.innerHTML = `
|
575 |
+
<div class="text-xl sm:text-2xl text-green-700 font-medium mb-2"><i class="fas fa-check-circle text-green-500 mr-2"></i>${fileName}</div>
|
576 |
+
<p class="text-green-600 text-base sm:text-lg">Image uploaded successfully! Click area or image to change.</p>
|
577 |
+
`;
|
578 |
+
|
579 |
+
// Arrange elements: image first, then content
|
580 |
+
uploadArea.insertBefore(imagePreview, uploadAreaContent);
|
581 |
+
}
|
582 |
+
|
583 |
+
function resetUploadArea() {
|
584 |
+
uploadedImage = null;
|
585 |
+
currentImageFile = null;
|
586 |
+
analyzeBtn.disabled = true;
|
587 |
+
analyzeBtn.innerHTML = '<i class="fas fa-chart-line mr-3"></i> Analyze Water Footprint';
|
588 |
+
|
589 |
+
uploadArea.classList.remove('has-image');
|
590 |
+
uploadArea.classList.add('shine-effect'); // Add shine back to area
|
591 |
+
uploadContainer.classList.add('shine-effect'); // Add shine back to container
|
592 |
+
|
593 |
+
if (imagePreview.parentNode === uploadArea) {
|
594 |
+
imagePreview.classList.add('hidden'); // Hide it
|
595 |
+
imagePreview.src = ''; // Clear the image source
|
596 |
+
// Remove the image from the parent DOM temporarily if needed, or just hide it well
|
597 |
+
// Let's just hide and clear src, re-inserting seems overly complex.
|
598 |
+
}
|
599 |
+
|
600 |
+
|
601 |
+
uploadAreaContent.innerHTML = `
|
602 |
+
<div class="text-6xl sm:text-7xl mb-6">πΈ</div>
|
603 |
+
<p class="text-xl sm:text-2xl text-green-700 font-medium mb-3">Drop your image here or click to browse</p>
|
604 |
+
<p class="text-green-600 text-base sm:text-lg">Supports JPG, PNG, WebP formats</p>
|
605 |
+
`;
|
606 |
+
imageInput.value = null; // Clear file input value
|
607 |
+
// Ensure content is correctly positioned - maybe just remove image and let content take over
|
608 |
+
if (imagePreview.parentNode === uploadArea) {
|
609 |
+
uploadArea.removeChild(imagePreview); // Remove the image element
|
610 |
+
}
|
611 |
+
// Add it back hidden so the next upload can insert it again
|
612 |
+
uploadArea.appendChild(imagePreview);
|
613 |
+
imagePreview.classList.add('hidden'); // Ensure it's hidden initially
|
614 |
+
|
615 |
+
}
|
616 |
+
|
617 |
+
|
618 |
+
// Analysis functionality
|
619 |
+
analyzeBtn.addEventListener('click', analyzeImage);
|
620 |
+
|
621 |
+
const loadingMessages = [
|
622 |
+
"Detecting product...",
|
623 |
+
"Estimating water usage...",
|
624 |
+
"Analyzing environmental impact...",
|
625 |
+
"Comparing metrics...",
|
626 |
+
"Generating recommendations...",
|
627 |
+
"Finalizing report..."
|
628 |
+
];
|
629 |
+
let messageIndex = 0;
|
630 |
+
let loadingInterval;
|
631 |
+
|
632 |
+
function startLoadingAnimation() {
|
633 |
+
loadingMessage.textContent = loadingMessages[messageIndex];
|
634 |
+
loadingSubMessage.textContent = "Processing your image and calculating environmental impact...";
|
635 |
+
|
636 |
+
loadingInterval = setInterval(() => {
|
637 |
+
messageIndex = (messageIndex + 1) % loadingMessages.length;
|
638 |
+
loadingMessage.textContent = loadingMessages[messageIndex];
|
639 |
+
}, 2000);
|
640 |
+
}
|
641 |
+
|
642 |
+
function stopLoadingAnimation() {
|
643 |
+
clearInterval(loadingInterval);
|
644 |
+
messageIndex = 0;
|
645 |
+
}
|
646 |
+
|
647 |
+
|
648 |
+
async function analyzeImage() {
|
649 |
+
if (!uploadedImage) return;
|
650 |
+
|
651 |
+
// Hide previous results/errors, show loading
|
652 |
+
resultsSection.classList.add('hidden');
|
653 |
+
errorSection.classList.add('hidden');
|
654 |
+
loadingSection.classList.remove('hidden');
|
655 |
+
startLoadingAnimation();
|
656 |
+
|
657 |
+
analyzeBtn.disabled = true;
|
658 |
+
analyzeBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Analyzing...';
|
659 |
+
|
660 |
+
|
661 |
+
try {
|
662 |
+
const response = await fetch('/analyze', {
|
663 |
+
method: 'POST',
|
664 |
+
headers: {
|
665 |
+
'Content-Type': 'application/json',
|
666 |
+
},
|
667 |
+
body: JSON.stringify({
|
668 |
+
image: uploadedImage
|
669 |
+
})
|
670 |
+
});
|
671 |
+
|
672 |
+
const result = await response.json();
|
673 |
+
stopLoadingAnimation();
|
674 |
+
|
675 |
+
if (response.ok && result.success) { // Check both HTTP status and internal success flag
|
676 |
+
displayResults(result.data);
|
677 |
+
loadingSection.classList.add('hidden');
|
678 |
+
resultsSection.classList.remove('hidden');
|
679 |
+
} else {
|
680 |
+
// Handle non-200 HTTP responses or API success: False
|
681 |
+
const errorReason = result.error || `HTTP Error: ${response.status}`;
|
682 |
+
console.error('Analysis API returned failure:', errorReason, result);
|
683 |
+
|
684 |
+
loadingSection.classList.add('hidden');
|
685 |
+
errorSection.classList.remove('hidden');
|
686 |
+
errorMessage.textContent = errorReason;
|
687 |
+
|
688 |
+
// If partial data is present, display it and show a fallback message
|
689 |
+
if (result.data && (Object.keys(result.data).length > 0 || (result.data.raw_analysis_text_fallback))) {
|
690 |
+
fallbackMessage.textContent = "Partial data might be available below. Raw AI output included for reference.";
|
691 |
+
fallbackMessage.classList.remove('hidden');
|
692 |
+
displayResults(result.data); // Try displaying partial data
|
693 |
+
resultsSection.classList.remove('hidden'); // Show results section even with partial data
|
694 |
+
} else {
|
695 |
+
// No usable data fallback
|
696 |
+
fallbackMessage.classList.add('hidden');
|
697 |
+
// Clear any potentially partial results data display from a previous run
|
698 |
+
clearResultsDisplay();
|
699 |
+
}
|
700 |
+
}
|
701 |
+
} catch (error) {
|
702 |
+
// Handle network errors or other unexpected exceptions
|
703 |
+
console.error('Error during analysis fetch:', error);
|
704 |
+
stopLoadingAnimation();
|
705 |
+
loadingSection.classList.add('hidden');
|
706 |
+
errorSection.classList.remove('hidden');
|
707 |
+
errorMessage.textContent = 'Network error or unexpected issue: ' + error.message;
|
708 |
+
fallbackMessage.classList.add('hidden');
|
709 |
+
// Clear any potentially partial results data display
|
710 |
+
clearResultsDisplay();
|
711 |
+
} finally {
|
712 |
+
analyzeBtn.disabled = false;
|
713 |
+
analyzeBtn.innerHTML = '<i class="fas fa-chart-line mr-2"></i>Analyze Water Footprint';
|
714 |
+
}
|
715 |
+
}
|
716 |
+
|
717 |
+
function clearResultsDisplay() {
|
718 |
+
document.getElementById('productInfo').innerHTML = '';
|
719 |
+
document.getElementById('waterFootprintMetrics').innerHTML = '';
|
720 |
+
document.getElementById('waterDefinitions').innerHTML = '';
|
721 |
+
document.getElementById('tabContent').innerHTML = '';
|
722 |
+
document.getElementById('funFacts').innerHTML = '';
|
723 |
+
document.getElementById('overallSeverity').querySelector('#severityText').textContent = 'N/A'; // Reset severity text
|
724 |
+
document.getElementById('overallSeverity').querySelector('#severityText').className = 'text-3xl font-bold severity-unknown'; // Reset severity styling
|
725 |
+
|
726 |
+
// Destroy charts explicitly and replace canvases with placeholders
|
727 |
+
const waterBreakdownChartContainer = document.getElementById('waterBreakdownChart').closest('.bg-chart');
|
728 |
+
if (currentCharts['waterBreakdownChart']) {
|
729 |
+
currentCharts['waterBreakdownChart'].destroy();
|
730 |
+
currentCharts['waterBreakdownChart'] = null;
|
731 |
+
}
|
732 |
+
if (waterBreakdownChartContainer) waterBreakdownChartContainer.innerHTML = '<canvas id="waterBreakdownChart"></canvas><p class="text-center text-gray-600 mt-4">Chart data not available.</p>'; // Add message
|
733 |
+
|
734 |
+
const regionalComparisonChartContainer = document.getElementById('regionalComparisonChart').closest('.bg-chart');
|
735 |
+
if (currentCharts['regionalComparisonChart']) {
|
736 |
+
currentCharts['regionalComparisonChart'].destroy();
|
737 |
+
currentCharts['regionalComparisonChart'] = null;
|
738 |
+
}
|
739 |
+
if (regionalComparisonChartContainer) regionalComparisonChartContainer.innerHTML = '<canvas id="regionalComparisonChart"></canvas><p class="text-center text-gray-600 mt-4">Chart data not available.</p>'; // Add message
|
740 |
+
|
741 |
+
// Hide the definitions section
|
742 |
+
document.getElementById('waterDefinitions').classList.add('hidden');
|
743 |
+
}
|
744 |
+
|
745 |
+
|
746 |
+
function displayResults(data) {
|
747 |
+
// Destroy previous charts if they exist
|
748 |
+
for (const chartId in currentCharts) {
|
749 |
+
if (currentCharts[chartId]) {
|
750 |
+
currentCharts[chartId].destroy();
|
751 |
+
currentCharts[chartId] = null;
|
752 |
+
}
|
753 |
+
}
|
754 |
+
|
755 |
+
displayProductInfo(data.product_identification);
|
756 |
+
displayOverallSeverity(data.overall_severity);
|
757 |
+
displayWaterFootprint(data.water_footprint);
|
758 |
+
displayWaterDefinitions(data.definitions);
|
759 |
+
setupTabs(data);
|
760 |
+
displayFunFacts(data.interesting_facts || []);
|
761 |
+
|
762 |
+
|
763 |
+
// Ensure canvas containers exist and are ready before creating charts
|
764 |
+
const waterBreakdownChartElement = document.getElementById('waterBreakdownChart');
|
765 |
+
const regionalComparisonChartElement = document.getElementById('regionalComparisonChart');
|
766 |
+
|
767 |
+
// Replace placeholder message with clean canvas if data exists
|
768 |
+
if (data.water_breakdown_chart && waterBreakdownChartElement && waterBreakdownChartElement.closest('.bg-chart').querySelector('p')) {
|
769 |
+
waterBreakdownChartElement.closest('.bg-chart').innerHTML = '<canvas id="waterBreakdownChart"></canvas>';
|
770 |
+
}
|
771 |
+
if (data.regional_comparison_chart && regionalComparisonChartElement && regionalComparisonChartElement.closest('.bg-chart').querySelector('p')) {
|
772 |
+
regionalComparisonChartElement.closest('.bg-chart').innerHTML = '<canvas id="regionalComparisonChart"></canvas>';
|
773 |
+
}
|
774 |
+
|
775 |
+
|
776 |
+
// Create charts AFTER the results section is shown and potential partial data handled
|
777 |
+
setTimeout(() => {
|
778 |
+
// Re-get the canvas elements in case they were replaced
|
779 |
+
const newWaterBreakdownCtx = document.getElementById('waterBreakdownChart')?.getContext('2d');
|
780 |
+
const newRegionalComparisonCtx = document.getElementById('regionalComparisonChart')?.getContext('2d');
|
781 |
+
|
782 |
+
if (data.water_breakdown_chart && newWaterBreakdownCtx) {
|
783 |
+
createWaterBreakdownChart(data.water_breakdown_chart, newWaterBreakdownCtx);
|
784 |
+
} else if (newWaterBreakdownCtx?.closest('.bg-chart')) {
|
785 |
+
// If element exists but no data, show message
|
786 |
+
newWaterBreakdownCtx.closest('.bg-chart').innerHTML = '<canvas id="waterBreakdownChart"></canvas><p class="text-center text-gray-600 mt-4">Chart data not available.</p>';
|
787 |
+
}
|
788 |
+
|
789 |
+
if (data.regional_comparison_chart && newRegionalComparisonCtx) {
|
790 |
+
createRegionalComparisonChart(data.regional_comparison_chart, newRegionalComparisonCtx);
|
791 |
+
} else if (newRegionalComparisonCtx?.closest('.bg-chart')) {
|
792 |
+
// If element exists but no data, show message
|
793 |
+
newRegionalComparisonCtx.closest('.bg-chart').innerHTML = '<canvas id="regionalComparisonChart"></canvas><p class="text-center text-gray-600 mt-4">Chart data not available.</p>';
|
794 |
+
}
|
795 |
+
|
796 |
+
}, 100); // Small delay
|
797 |
+
|
798 |
+
// Trigger animations for results sections
|
799 |
+
document.querySelectorAll('#resultsSection > div').forEach((el, index) => {
|
800 |
+
el.classList.remove('slide-in', 'scale-in'); // Reset if already present
|
801 |
+
void el.offsetWidth; // Trigger reflow
|
802 |
+
// Re-add classes to re-trigger animation
|
803 |
+
if (index % 2 === 0) el.classList.add('slide-in');
|
804 |
+
else el.classList.add('scale-in');
|
805 |
+
el.style.animationDelay = `${0.2 + index * 0.15}s`; // Slightly faster delay
|
806 |
+
});
|
807 |
+
}
|
808 |
+
|
809 |
+
function displayOverallSeverity(severity) {
|
810 |
+
const severityElement = document.getElementById('severityText');
|
811 |
+
let severityClass = 'severity-unknown';
|
812 |
+
|
813 |
+
severityElement.textContent = severity || 'Unknown';
|
814 |
+
severityElement.classList.remove('severity-low', 'severity-medium', 'severity-high', 'severity-very-high', 'severity-unknown');
|
815 |
+
|
816 |
+
if (severity) {
|
817 |
+
const lowerSeverity = severity.toLowerCase();
|
818 |
+
if (lowerSeverity.includes('very high')) {
|
819 |
+
severityClass = 'severity-very-high';
|
820 |
+
} else if (lowerSeverity.includes('high')) {
|
821 |
+
severityClass = 'severity-high';
|
822 |
+
} else if (lowerSeverity.includes('medium')) {
|
823 |
+
severityClass = 'severity-medium';
|
824 |
+
} else if (lowerSeverity.includes('low')) {
|
825 |
+
severityClass = 'severity-low';
|
826 |
+
}
|
827 |
+
}
|
828 |
+
severityElement.classList.add(severityClass);
|
829 |
+
}
|
830 |
+
|
831 |
+
|
832 |
+
function displayProductInfo(productInfo) {
|
833 |
+
const container = document.getElementById('productInfo');
|
834 |
+
if (!productInfo || Object.keys(productInfo).length === 0 || (!productInfo.detected_product && !productInfo.category)) {
|
835 |
+
container.innerHTML = '<p class="text-gray-600 col-span-2">Product identification data not available.</p>';
|
836 |
+
return;
|
837 |
+
}
|
838 |
+
container.innerHTML = `
|
839 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
840 |
+
<h3 class="text-xl font-semibold mb-4">
|
841 |
+
<i class="fas fa-seedling mr-2 text-primary"></i>Detected Product
|
842 |
+
</h3>
|
843 |
+
<p class="text-3xl font-bold text-primary">${productInfo?.detected_product || 'Unknown'}</p>
|
844 |
+
<p class="text-gray-600 mt-2 text-sm">Confidence: ${productInfo?.confidence || 'N/A'}</p>
|
845 |
+
</div>
|
846 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
847 |
+
<h3 class="text-xl font-semibold mb-4">
|
848 |
+
<i class="fas fa-tags mr-2 text-accent"></i>Category
|
849 |
+
</h3>
|
850 |
+
<p class="text-2xl font-bold text-secondary">${productInfo?.category || 'Agricultural Product'}</p>
|
851 |
+
<p class="text-gray-600 mt-2 text-sm">${productInfo?.scientific_name || 'Classification'}</p>
|
852 |
+
</div>
|
853 |
+
`;
|
854 |
+
}
|
855 |
+
|
856 |
+
function displayWaterFootprint(footprint) {
|
857 |
+
const container = document.getElementById('waterFootprintMetrics');
|
858 |
+
if (!footprint || Object.keys(footprint).length === 0 || (!footprint.total_footprint && !footprint.green_water && !footprint.blue_water && !footprint.grey_water)) {
|
859 |
+
container.innerHTML = '<p class="text-gray-600 col-span-4">Water footprint data not available.</p>';
|
860 |
+
return;
|
861 |
+
}
|
862 |
+
container.innerHTML = `
|
863 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
864 |
+
<h3 class="text-lg font-semibold mb-2">
|
865 |
+
<i class="fas fa-tint mr-2 text-green-600"></i>Total Footprint
|
866 |
+
</h3>
|
867 |
+
<p class="text-2xl font-bold text-green-600">${footprint?.total_footprint || 'N/A'}</p>
|
868 |
+
<p class="text-sm text-gray-600 mt-1">Global Avg: ${footprint?.global_average || 'N/A'}</p>
|
869 |
+
</div>
|
870 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
871 |
+
<h3 class="text-lg font-semibold mb-2">
|
872 |
+
<i class="fas fa-cloud-rain mr-2 text-green-700"></i>Green Water
|
873 |
+
</h3>
|
874 |
+
<p class="text-2xl font-bold text-green-700">${footprint?.green_water || 'N/A'}</p>
|
875 |
+
</div>
|
876 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
877 |
+
<h3 class="text-lg font-semibold mb-2">
|
878 |
+
<i class="fas fa-water mr-2 text-blue-500"></i>Blue Water
|
879 |
+
</h3>
|
880 |
+
<p class="text-2xl font-bold text-blue-500">${footprint?.blue_water || 'N/A'}</p>
|
881 |
+
</div>
|
882 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
883 |
+
<h3 class="text-lg font-semibold mb-2">
|
884 |
+
<i class="fas fa-flask mr-2 text-purple-600"></i>Grey Water
|
885 |
+
</h3>
|
886 |
+
<p class="text-2xl font-bold text-purple-600">${footprint?.grey_water || 'N/A'}</p>
|
887 |
+
</div>
|
888 |
+
`;
|
889 |
+
}
|
890 |
+
|
891 |
+
function displayWaterDefinitions(definitions) {
|
892 |
+
const container = document.getElementById('waterDefinitions');
|
893 |
+
if (!definitions || Object.keys(definitions).length === 0 || (!definitions.green_water && !definitions.blue_water && !definitions.grey_water)) {
|
894 |
+
container.classList.add('hidden');
|
895 |
+
container.innerHTML = '';
|
896 |
+
return;
|
897 |
+
}
|
898 |
+
container.classList.remove('hidden');
|
899 |
+
|
900 |
+
container.innerHTML = `
|
901 |
+
<h3>Understanding Your Water Footprint</h3>
|
902 |
+
<dl>
|
903 |
+
<dt><i class="fas fa-cloud-rain mr-2 text-green-700"></i>Green Water:</dt>
|
904 |
+
<dd>${definitions.green_water || 'The volume of rainwater that is stored as soil moisture and evaporates from the soil and from plants.'}</dd>
|
905 |
+
|
906 |
+
<dt><i class="fas fa-water mr-2 text-blue-500"></i>Blue Water:</dt>
|
907 |
+
<dd>${definitions.blue_water || 'The volume of surface and groundwater required (evaporated or incorporated into a product).'}</dd>
|
908 |
+
|
909 |
+
<dt><i class="fas fa-flask mr-2 text-purple-600"></i>Grey Water:</dt>
|
910 |
+
<dd>${definitions.grey_water || 'The volume of freshwater required to assimilate the load of pollutants based on existing ambient water quality standards.'}</dd>
|
911 |
+
</dl>
|
912 |
+
`;
|
913 |
+
}
|
914 |
+
|
915 |
+
|
916 |
+
function createWaterBreakdownChart(data, ctx) {
|
917 |
+
const backgroundColors = data.colors || ['#48BB78', '#4299E1', '#A78BFA']; // Green, Blue, Purple
|
918 |
+
// Ensure values are numbers for the chart
|
919 |
+
const values = (data.values || []).map(v => parseFloat(v)).filter(v => !isNaN(v));
|
920 |
+
|
921 |
+
if (currentCharts['waterBreakdownChart']) currentCharts['waterBreakdownChart'].destroy();
|
922 |
+
currentCharts['waterBreakdownChart'] = new Chart(ctx, {
|
923 |
+
type: 'doughnut',
|
924 |
+
data: {
|
925 |
+
labels: data.labels || ['Green Water', 'Blue Water', 'Grey Water'],
|
926 |
+
datasets: [{
|
927 |
+
data: values.length === 3 ? values : [0, 0, 0], // Use parsed values, default to zero
|
928 |
+
backgroundColor: backgroundColors,
|
929 |
+
borderColor: '#ffffff',
|
930 |
+
borderWidth: 2
|
931 |
+
}]
|
932 |
+
},
|
933 |
+
options: {
|
934 |
+
responsive: true,
|
935 |
+
plugins: {
|
936 |
+
title: {
|
937 |
+
display: true,
|
938 |
+
text: 'Water Footprint Breakdown (%)',
|
939 |
+
color: '#2D3748',
|
940 |
+
font: { size: 18, weight: 'bold' }
|
941 |
+
},
|
942 |
+
legend: {
|
943 |
+
position: 'bottom',
|
944 |
+
labels: { color: '#4A5568', font: { size: 12 } }
|
945 |
+
},
|
946 |
+
tooltip: {
|
947 |
+
callbacks: {
|
948 |
+
label: function(tooltipItem) {
|
949 |
+
const value = parseFloat(tooltipItem.raw);
|
950 |
+
if (isNaN(value)) return tooltipItem.label + ': N/A';
|
951 |
+
return tooltipItem.label + ': ' + value.toFixed(1) + '%';
|
952 |
+
}
|
953 |
+
}
|
954 |
+
}
|
955 |
+
}
|
956 |
+
}
|
957 |
+
});
|
958 |
+
}
|
959 |
+
|
960 |
+
function createRegionalComparisonChart(data, ctx) {
|
961 |
+
if (currentCharts['regionalComparisonChart']) currentCharts['regionalComparisonChart'].destroy();
|
962 |
+
|
963 |
+
const barColors = ['#48BB78', '#D69E2E', '#4FD1C5', '#ECC94B']; // Green, Orange, Teal, Yellow
|
964 |
+
// Ensure values are numbers for the chart
|
965 |
+
const waterUsage = (data.water_usage || []).map(v => parseFloat(v)).filter(v => !isNaN(v));
|
966 |
+
|
967 |
+
|
968 |
+
currentCharts['regionalComparisonChart'] = new Chart(ctx, {
|
969 |
+
type: 'bar',
|
970 |
+
data: {
|
971 |
+
labels: data.regions || ['Global Average', 'Arid Regions', 'Temperate', 'Tropical'],
|
972 |
+
datasets: [{
|
973 |
+
label: 'Water Usage (L/kg)',
|
974 |
+
data: waterUsage.length === 4 ? waterUsage : [0, 0, 0, 0], // Use parsed values, default to zero
|
975 |
+
backgroundColor: barColors,
|
976 |
+
borderColor: '#ffffff',
|
977 |
+
borderWidth: 1
|
978 |
+
}]
|
979 |
+
},
|
980 |
+
options: {
|
981 |
+
responsive: true,
|
982 |
+
plugins: {
|
983 |
+
title: {
|
984 |
+
display: true,
|
985 |
+
text: 'Regional Water Usage Comparison (L/kg)',
|
986 |
+
color: '#2D3748',
|
987 |
+
font: { size: 18, weight: 'bold' }
|
988 |
+
},
|
989 |
+
legend: { display: false }
|
990 |
+
},
|
991 |
+
scales: {
|
992 |
+
y: {
|
993 |
+
beginAtZero: true,
|
994 |
+
title: {
|
995 |
+
display: true,
|
996 |
+
text: 'Liters per kg',
|
997 |
+
color: '#4A5568',
|
998 |
+
font: { size: 14 }
|
999 |
+
},
|
1000 |
+
ticks: { color: '#4A5568' },
|
1001 |
+
grid: { color: 'rgba(0, 0, 0, 0.1)' }
|
1002 |
+
},
|
1003 |
+
x: {
|
1004 |
+
ticks: { color: '#4A5568' },
|
1005 |
+
grid: { color: 'rgba(0, 0, 0, 0.1)' }
|
1006 |
+
}
|
1007 |
+
}
|
1008 |
+
}
|
1009 |
+
});
|
1010 |
+
}
|
1011 |
+
|
1012 |
+
|
1013 |
+
function setupTabs(data) {
|
1014 |
+
const tabButtons = document.querySelectorAll('#tabButtons .tab-button');
|
1015 |
+
const tabContent = document.getElementById('tabContent');
|
1016 |
+
|
1017 |
+
tabContent.innerHTML = ''; // Clear previous content
|
1018 |
+
|
1019 |
+
// Add event listeners to cloned buttons to prevent duplicates
|
1020 |
+
tabButtons.forEach(button => {
|
1021 |
+
const newButton = button.cloneNode(true);
|
1022 |
+
button.parentNode.replaceChild(newButton, button);
|
1023 |
+
|
1024 |
+
newButton.addEventListener('click', () => {
|
1025 |
+
document.querySelectorAll('#tabButtons .tab-button').forEach(btn => {
|
1026 |
+
btn.classList.remove('active', 'bg-gradient-to-r', 'from-green-500', 'to-teal-500', 'text-white', 'transform', 'translate-y-[-2px]');
|
1027 |
+
btn.classList.add('bg-white', 'bg-opacity-60', 'text-gray-600');
|
1028 |
+
});
|
1029 |
+
|
1030 |
+
newButton.classList.add('active', 'bg-gradient-to-r', 'from-green-500', 'to-teal-500', 'text-white', 'transform', 'translate-y-[-2px]');
|
1031 |
+
newButton.classList.remove('bg-white', 'bg-opacity-60', 'text-gray-600');
|
1032 |
+
|
1033 |
+
const tabType = newButton.getAttribute('data-tab');
|
1034 |
+
displayTabContent(tabType, data);
|
1035 |
+
});
|
1036 |
+
});
|
1037 |
+
|
1038 |
+
|
1039 |
+
// Display initial tab content and set active state
|
1040 |
+
const firstButton = document.querySelector('#tabButtons .tab-button');
|
1041 |
+
if (firstButton) {
|
1042 |
+
firstButton.click();
|
1043 |
+
} else {
|
1044 |
+
container.innerHTML = '<p class="text-gray-600">Select a tab to view details.</p>';
|
1045 |
+
}
|
1046 |
+
}
|
1047 |
+
|
1048 |
+
function displayTabContent(tabType, data) {
|
1049 |
+
const container = document.getElementById('tabContent');
|
1050 |
+
container.innerHTML = '';
|
1051 |
+
|
1052 |
+
let contentHTML = '';
|
1053 |
+
switch(tabType) {
|
1054 |
+
case 'environmental':
|
1055 |
+
contentHTML = createEnvironmentalTab(data.environmental_impact);
|
1056 |
+
break;
|
1057 |
+
case 'production':
|
1058 |
+
contentHTML = createProductionTab(data.production_insights);
|
1059 |
+
break;
|
1060 |
+
case 'comparisons':
|
1061 |
+
contentHTML = createComparisonsTab(data.comparisons);
|
1062 |
+
break;
|
1063 |
+
case 'recommendations':
|
1064 |
+
contentHTML = createRecommendationsTab(data.recommendations);
|
1065 |
+
break;
|
1066 |
+
case 'more-metrics':
|
1067 |
+
contentHTML = createMoreMetricsTab(data.impact_metrics);
|
1068 |
+
break;
|
1069 |
+
default:
|
1070 |
+
contentHTML = '<p class="text-gray-600">Select a tab to view details.</p>';
|
1071 |
+
break;
|
1072 |
+
}
|
1073 |
+
container.innerHTML = contentHTML;
|
1074 |
+
container.classList.remove('fade-in');
|
1075 |
+
void container.offsetWidth;
|
1076 |
+
container.classList.add('fade-in');
|
1077 |
+
|
1078 |
+
// Apply hover class to list items if they exist
|
1079 |
+
container.querySelectorAll('.tab-content-list-item').forEach(item => {
|
1080 |
+
item.classList.add('hover-lift'); // Use the general hover-lift class
|
1081 |
+
});
|
1082 |
+
}
|
1083 |
+
|
1084 |
+
function createEnvironmentalTab(impact) {
|
1085 |
+
if (!impact || Object.keys(impact).length === 0 || (!impact.severity_score && !impact.impact_category && !impact.sustainability_rating && !impact.carbon_footprint && !impact.land_use)) {
|
1086 |
+
return '<p class="text-gray-600">Environmental impact data not available.</p>';
|
1087 |
+
}
|
1088 |
+
|
1089 |
+
return `
|
1090 |
+
<div class="grid md:grid-cols-2 gap-6">
|
1091 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1092 |
+
<h4 class="text-lg font-semibold mb-4">Impact Assessment</h4>
|
1093 |
+
<div class="space-y-3 text-gray-700">
|
1094 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1095 |
+
<span class="text-gray-600">Severity Score:</span>
|
1096 |
+
<span class="font-bold text-primary">${impact.severity_score || 'N/A'}</span>
|
1097 |
+
</div>
|
1098 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1099 |
+
<span class="text-gray-600">Impact Category:</span>
|
1100 |
+
<span class="font-bold text-secondary">${impact.impact_category || 'N/A'}</span>
|
1101 |
+
</div>
|
1102 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1103 |
+
<span class="text-gray-600">Sustainability Rating:</span>
|
1104 |
+
<span class="font-bold text-accent">${impact.sustainability_rating || 'N/A'}</span>
|
1105 |
+
</div>
|
1106 |
+
</div>
|
1107 |
+
</div>
|
1108 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1109 |
+
<h4 class="text-lg font-semibold mb-4">Carbon & Land Impact</h4>
|
1110 |
+
<div class="space-y-3 text-gray-700">
|
1111 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1112 |
+
<span class="text-gray-600">Carbon Footprint:</span>
|
1113 |
+
<span class="font-bold text-gray-700">${impact.carbon_footprint || 'N/A'}</span>
|
1114 |
+
</div>
|
1115 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1116 |
+
<span class="text-gray-600">Land Use:</span>
|
1117 |
+
<span class="font-bold text-gray-700">${impact.land_use || 'N/A'}</span>
|
1118 |
+
</div>
|
1119 |
+
</div>
|
1120 |
+
</div>
|
1121 |
+
</div>
|
1122 |
+
`;
|
1123 |
+
}
|
1124 |
+
|
1125 |
+
function createProductionTab(insights) {
|
1126 |
+
if (!insights || Object.keys(insights).length === 0 || (!insights.growing_season && !insights.water_efficiency && !insights.irrigation_dependency && !insights.climate_sensitivity && (insights.seasonal_availability?.length === 0 || !insights.seasonal_availability))) {
|
1127 |
+
return '<p class="text-gray-600">Production insights not available.</p>';
|
1128 |
+
}
|
1129 |
+
|
1130 |
+
return `
|
1131 |
+
<div class="grid md:grid-cols-2 gap-6">
|
1132 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1133 |
+
<h4 class="text-lg font-semibold mb-4">Production Details</h4>
|
1134 |
+
<div class="space-y-3 text-gray-700">
|
1135 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1136 |
+
<span class="text-gray-600">Growing Season:</span>
|
1137 |
+
<span class="font-bold">${insights.growing_season || 'N/A'}</span>
|
1138 |
+
</div>
|
1139 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1140 |
+
<span class="text-gray-600">Water Efficiency:</span>
|
1141 |
+
<span class="font-bold text-secondary">${insights.water_efficiency || 'N/A'}</span>
|
1142 |
+
</div>
|
1143 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1144 |
+
<span class="text-gray-600">Irrigation Dependency:</span>
|
1145 |
+
<span class="font-bold">${insights.irrigation_dependency || 'N/A'}</span>
|
1146 |
+
</div>
|
1147 |
+
</div>
|
1148 |
+
</div>
|
1149 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1150 |
+
<h4 class="text-lg font-semibold mb-4">Climate Factors</h4>
|
1151 |
+
<div class="space-y-3 text-gray-700">
|
1152 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1153 |
+
<span class="text-gray-600">Climate Sensitivity:</span>
|
1154 |
+
<span class="font-bold">${insights.climate_sensitivity || 'N/A'}</span>
|
1155 |
+
</div>
|
1156 |
+
<div class="tab-content-list-item p-2 -mx-2 rounded-md">
|
1157 |
+
<span class="text-gray-600">Seasonal Availability:</span>
|
1158 |
+
<div class="mt-2 flex flex-wrap gap-2">
|
1159 |
+
${(insights.seasonal_availability || []).map(month =>
|
1160 |
+
`<span class="bg-gray-200 text-gray-700 px-2 py-1 rounded text-sm">${month}</span>`
|
1161 |
+
).join('')}
|
1162 |
+
${(insights.seasonal_availability || []).length === 0 ? '<span class="text-gray-500 text-sm">N/A</span>' : ''}
|
1163 |
+
</div>
|
1164 |
+
</div>
|
1165 |
+
</div>
|
1166 |
+
</div>
|
1167 |
+
</div>
|
1168 |
+
`;
|
1169 |
+
}
|
1170 |
+
|
1171 |
+
function createComparisonsTab(comparisons) {
|
1172 |
+
if (!comparisons || (comparisons.vs_similar_products?.length === 0 && comparisons.vs_alternatives?.length === 0)) {
|
1173 |
+
return '<p class="text-gray-600">Comparison data not available.</p>';
|
1174 |
+
}
|
1175 |
+
|
1176 |
+
return `
|
1177 |
+
<div class="space-y-8">
|
1178 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift overflow-x-auto">
|
1179 |
+
<h4 class="text-lg font-semibold mb-4">
|
1180 |
+
<i class="fas fa-chart-line mr-2 text-green-600"></i>Similar Products Comparison
|
1181 |
+
</h4>
|
1182 |
+
${(comparisons.vs_similar_products && comparisons.vs_similar_products.length > 0) ? `
|
1183 |
+
<table class="comparison-table">
|
1184 |
+
<thead>
|
1185 |
+
<tr>
|
1186 |
+
<th>Product</th>
|
1187 |
+
<th>Water Footprint</th>
|
1188 |
+
<th>Difference</th>
|
1189 |
+
</tr>
|
1190 |
+
</thead>
|
1191 |
+
<tbody>
|
1192 |
+
${comparisons.vs_similar_products.map(product => `
|
1193 |
+
<tr>
|
1194 |
+
<td class="font-medium text-gray-800">${product.product || 'Unknown'}</td>
|
1195 |
+
<td>${product.water_footprint || 'N/A'}</td>
|
1196 |
+
<td class="${product.difference?.includes('-') ? 'text-green-600' : product.difference ? 'text-red-600' : 'text-gray-600'}">
|
1197 |
+
${product.difference || 'N/A'}
|
1198 |
+
</td>
|
1199 |
+
</tr>
|
1200 |
+
`).join('')}
|
1201 |
+
</tbody>
|
1202 |
+
</table>
|
1203 |
+
` : '<p class="text-gray-600">No similar products comparison available.</p>'}
|
1204 |
+
</div>
|
1205 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1206 |
+
<h4 class="text-lg font-semibold mb-4">
|
1207 |
+
<i class="fas fa-leaf mr-2 text-green-700"></i>Sustainable Alternatives
|
1208 |
+
</h4>
|
1209 |
+
<div class="space-y-3">
|
1210 |
+
${(comparisons.vs_alternatives || []).map(alt => `
|
1211 |
+
<div class="p-3 bg-gray-100 rounded-lg tab-content-list-item hover-lift">
|
1212 |
+
<div class="font-medium text-green-700">${alt.alternative || 'Unknown'}</div>
|
1213 |
+
<div class="text-sm text-gray-700 mt-1">Saves: ${alt.water_savings || 'N/A'}</div>
|
1214 |
+
<div class="text-sm text-gray-600 mt-1">${alt.benefit || ''}</div>
|
1215 |
+
</div>
|
1216 |
+
`).join('')}
|
1217 |
+
${(comparisons.vs_alternatives || []).length === 0 ? '<p class="text-gray-600">No sustainable alternatives suggested.</p>' : ''}
|
1218 |
+
</div>
|
1219 |
+
</div>
|
1220 |
+
</div>
|
1221 |
+
`;
|
1222 |
+
}
|
1223 |
+
|
1224 |
+
function createRecommendationsTab(recommendations) {
|
1225 |
+
if (!recommendations || (recommendations.consumer_tips?.length === 0 && recommendations.sustainable_practices?.length === 0 && recommendations.water_conservation?.length === 0 && !recommendations.seasonal_buying)) {
|
1226 |
+
return '<p class="text-gray-600">Recommendations not available.</p>';
|
1227 |
+
}
|
1228 |
+
|
1229 |
+
return `
|
1230 |
+
<div class="grid md:grid-cols-2 gap-6">
|
1231 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1232 |
+
<h4 class="text-lg font-semibold mb-4">
|
1233 |
+
<i class="fas fa-user mr-2 text-purple-700"></i>Consumer Tips
|
1234 |
+
</h4>
|
1235 |
+
<ul class="space-y-3">
|
1236 |
+
${(recommendations.consumer_tips || []).map(tip =>
|
1237 |
+
`<li class="flex items-start space-x-3 tab-content-list-item p-2 -mx-2 rounded-md">
|
1238 |
+
<i class="fas fa-check-circle text-green-600 mt-1 text-lg"></i>
|
1239 |
+
<span class="text-gray-700 leading-relaxed">${tip}</span>
|
1240 |
+
</li>`
|
1241 |
+
).join('')}
|
1242 |
+
${(recommendations.consumer_tips || []).length === 0 ? '<p class="text-gray-600">No consumer tips available.</p>' : ''}
|
1243 |
+
</ul>
|
1244 |
+
</div>
|
1245 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1246 |
+
<h4 class="text-lg font-semibold mb-4">
|
1247 |
+
<i class="fas fa-recycle mr-2 text-green-600"></i>Sustainable Practices (Production)
|
1248 |
+
</h4>
|
1249 |
+
<ul class="space-y-3">
|
1250 |
+
${(recommendations.sustainable_practices || []).map(practice =>
|
1251 |
+
`<li class="flex items-start space-x-3 tab-content-list-item p-2 -mx-2 rounded-md">
|
1252 |
+
<i class="fas fa-leaf text-teal-700 mt-1 text-lg"></i>
|
1253 |
+
<span class="text-gray-700 leading-relaxed">${practice}</span>
|
1254 |
+
</li>`
|
1255 |
+
).join('')}
|
1256 |
+
${(recommendations.sustainable_practices || []).length === 0 ? '<p class="text-gray-600">No sustainable production practices suggested.</p>' : ''}
|
1257 |
+
</ul>
|
1258 |
+
</div>
|
1259 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1260 |
+
<h4 class="text-lg font-semibold mb-4">
|
1261 |
+
<i class="fas fa-tint mr-2 text-cyan-700"></i>Water Conservation Methods
|
1262 |
+
</h4>
|
1263 |
+
<ul class="space-y-3">
|
1264 |
+
${(recommendations.water_conservation || []).map(method =>
|
1265 |
+
`<li class="flex items-start space-x-3 tab-content-list-item p-2 -mx-2 rounded-md">
|
1266 |
+
<i class="fas fa-water text-blue-600 mt-1 text-lg"></i>
|
1267 |
+
<span class="text-gray-700 leading-relaxed">${method}</span>
|
1268 |
+
</li>`
|
1269 |
+
).join('')}
|
1270 |
+
${(recommendations.water_conservation || []).length === 0 ? '<p class="text-gray-600">No water conservation methods suggested.</p>' : ''}
|
1271 |
+
</ul>
|
1272 |
+
</div>
|
1273 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1274 |
+
<h4 class="text-lg font-semibold mb-4">
|
1275 |
+
<i class="fas fa-calendar-alt mr-2 text-orange-600"></i>Seasonal Buying
|
1276 |
+
</h4>
|
1277 |
+
<p class="text-gray-700 leading-relaxed">
|
1278 |
+
<strong class="text-gray-800">Best time to buy:</strong> ${recommendations.seasonal_buying || 'Information not available'}
|
1279 |
+
</p>
|
1280 |
+
</div>
|
1281 |
+
</div>
|
1282 |
+
`;
|
1283 |
+
}
|
1284 |
+
|
1285 |
+
function createMoreMetricsTab(metrics) {
|
1286 |
+
if (!metrics || Object.keys(metrics).length === 0 || (!metrics.water_stress_contribution && !metrics.biodiversity_impact && !metrics.soil_health_impact && !metrics.economic_water_cost)) {
|
1287 |
+
return '<p class="text-gray-600">Additional metrics data not available.</p>';
|
1288 |
+
}
|
1289 |
+
|
1290 |
+
return `
|
1291 |
+
<div class="grid md:grid-cols-2 gap-6">
|
1292 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1293 |
+
<h4 class="text-lg font-semibold mb-4">
|
1294 |
+
<i class="fas fa-balance-scale mr-2 text-cyan-700"></i>Advanced Metrics
|
1295 |
+
</h4>
|
1296 |
+
<div class="space-y-3 text-gray-700">
|
1297 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1298 |
+
<span class="text-gray-600">Water Stress Contribution:</span>
|
1299 |
+
<span class="font-bold">${metrics.water_stress_contribution || 'N/A'}</span>
|
1300 |
+
</div>
|
1301 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1302 |
+
<span class="text-gray-600">Biodiversity Impact:</span>
|
1303 |
+
<span class="font-bold">${metrics.biodiversity_impact || 'N/A'}</span>
|
1304 |
+
</div>
|
1305 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1306 |
+
<span class="text-gray-600">Soil Health Impact:</span>
|
1307 |
+
<span class="font-bold">${metrics.soil_health_impact || 'N/A'}</span>
|
1308 |
+
</div>
|
1309 |
+
<div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
|
1310 |
+
<span class="text-gray-600">Economic Water Cost:</span>
|
1311 |
+
<span class="font-bold">${metrics.economic_water_cost || 'N/A'}</span>
|
1312 |
+
</div>
|
1313 |
+
</div>
|
1314 |
+
</div>
|
1315 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1316 |
+
<h4 class="text-lg font-semibold mb-4">
|
1317 |
+
<i class="fas fa-info-circle mr-2 text-green-600"></i>About These Metrics
|
1318 |
+
</h4>
|
1319 |
+
<p class="text-gray-600 text-sm leading-relaxed">
|
1320 |
+
These metrics provide a deeper look into the environmental and economic context of the product's water usage. Water stress contribution relates to the scarcity of water in the production region. Biodiversity and soil health impacts consider broader ecological effects. Economic cost is an estimate of the water's value. Data availability may vary.
|
1321 |
+
</p>
|
1322 |
+
</div>
|
1323 |
+
</div>
|
1324 |
+
`;
|
1325 |
+
}
|
1326 |
+
|
1327 |
+
|
1328 |
+
function displayFunFacts(facts) {
|
1329 |
+
const container = document.getElementById('funFacts');
|
1330 |
+
if (!facts || facts.length === 0) {
|
1331 |
+
container.innerHTML = '<p class="text-gray-600 col-span-2">No interesting facts available.</p>';
|
1332 |
+
return;
|
1333 |
+
}
|
1334 |
+
container.innerHTML = facts.map((fact, index) => `
|
1335 |
+
<div class="bg-card rounded-xl p-6 metric-card hover-lift">
|
1336 |
+
<div class="flex items-start space-x-4">
|
1337 |
+
<div class="text-3xl">${['π‘', 'π', 'π', 'π¬', 'π§', 'π±', 'π', 'π₯'][index % 8]}</div>
|
1338 |
+
<p class="text-gray-700 leading-relaxed text-sm">${fact}</p>
|
1339 |
+
</div>
|
1340 |
+
</div>
|
1341 |
+
`).join('');
|
1342 |
+
}
|
1343 |
+
|
1344 |
+
// Initialize page animations
|
1345 |
+
document.addEventListener('DOMContentLoaded', function() {
|
1346 |
+
document.querySelector('header').style.opacity = 0;
|
1347 |
+
document.getElementById('uploadContainer').style.opacity = 0;
|
1348 |
+
|
1349 |
+
setTimeout(() => {
|
1350 |
+
document.querySelector('header').style.opacity = 1;
|
1351 |
+
document.querySelector('header').classList.add('slide-in');
|
1352 |
+
}, 200);
|
1353 |
+
|
1354 |
+
setTimeout(() => {
|
1355 |
+
document.getElementById('uploadContainer').style.opacity = 1;
|
1356 |
+
document.getElementById('uploadContainer').classList.add('scale-in');
|
1357 |
+
}, 400);
|
1358 |
+
});
|
1359 |
+
|
1360 |
+
// Reset on load for clean state
|
1361 |
+
resetUploadArea();
|
1362 |
+
</script>
|
1363 |
+
</body>
|
1364 |
+
</html>
|