yandri918 commited on
Commit
4dddda3
ยท
1 Parent(s): fa943e3

Enhance Price Trend (1-Year), Fix Dashboard, Modernize Home (No Icons)

Browse files
AgriSensa_Portfolio.html ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="id">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>AgriSensa - Portfolio Project | Yandri918</title>
8
+ <link
9
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Poppins:wght@600;700;800&display=swap"
10
+ rel="stylesheet">
11
+ <style>
12
+ /* Minimized CSS for file size */
13
+ :root {
14
+ --primary: #10b981;
15
+ --dark: #0f172a;
16
+ --gray-50: #f8fafc;
17
+ --gray-600: #475569;
18
+ --gray-700: #334155;
19
+ }
20
+
21
+ * {
22
+ margin: 0;
23
+ padding: 0;
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ body {
28
+ font-family: 'Inter', sans-serif;
29
+ line-height: 1.6;
30
+ color: #1e293b;
31
+ background: var(--gray-50);
32
+ }
33
+
34
+ nav {
35
+ position: fixed;
36
+ top: 0;
37
+ width: 100%;
38
+ background: rgba(255, 255, 255, 0.95);
39
+ backdrop-filter: blur(10px);
40
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
41
+ z-index: 1000;
42
+ }
43
+
44
+ .nav-container {
45
+ max-width: 1200px;
46
+ margin: 0 auto;
47
+ padding: 1rem 2rem;
48
+ display: flex;
49
+ justify-content: space-between;
50
+ align-items: center;
51
+ }
52
+
53
+ .logo {
54
+ font-family: 'Poppins', sans-serif;
55
+ font-size: 1.5rem;
56
+ font-weight: 700;
57
+ color: var(--primary);
58
+ text-decoration: none;
59
+ }
60
+
61
+ .nav-links {
62
+ display: flex;
63
+ gap: 2rem;
64
+ list-style: none;
65
+ }
66
+
67
+ .nav-links a {
68
+ color: var(--gray-700);
69
+ text-decoration: none;
70
+ font-weight: 500;
71
+ transition: color 0.3s;
72
+ }
73
+
74
+ .nav-links a:hover {
75
+ color: var(--primary);
76
+ }
77
+
78
+ .hero {
79
+ padding: 8rem 2rem 4rem;
80
+ background: linear-gradient(135deg, var(--dark) 0%, #1e293b 100%);
81
+ color: white;
82
+ text-align: center;
83
+ }
84
+
85
+ .hero h1 {
86
+ font-family: 'Poppins', sans-serif;
87
+ font-size: 3.5rem;
88
+ font-weight: 800;
89
+ margin-bottom: 1rem;
90
+ background: linear-gradient(to right, #34d399, #60a5fa);
91
+ -webkit-background-clip: text;
92
+ -webkit-text-fill-color: transparent;
93
+ }
94
+
95
+ .container {
96
+ max-width: 1200px;
97
+ margin: 0 auto;
98
+ padding: 4rem 2rem;
99
+ }
100
+
101
+ .section {
102
+ margin-bottom: 4rem;
103
+ }
104
+
105
+ .section-title {
106
+ font-family: 'Poppins', sans-serif;
107
+ font-size: 2.5rem;
108
+ font-weight: 700;
109
+ margin-bottom: 1rem;
110
+ }
111
+
112
+ .grid {
113
+ display: grid;
114
+ gap: 2rem;
115
+ }
116
+
117
+ .grid-4 {
118
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
119
+ }
120
+
121
+ .grid-3 {
122
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
123
+ }
124
+
125
+ .card {
126
+ background: white;
127
+ padding: 2rem;
128
+ border-radius: 12px;
129
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
130
+ }
131
+
132
+ .card:hover {
133
+ transform: translateY(-5px);
134
+ transition: all 0.3s;
135
+ }
136
+
137
+ .btn {
138
+ padding: 1rem 2rem;
139
+ border-radius: 8px;
140
+ text-decoration: none;
141
+ font-weight: 600;
142
+ transition: all 0.3s;
143
+ display: inline-flex;
144
+ gap: 0.5rem;
145
+ }
146
+
147
+ .btn-primary {
148
+ background: var(--primary);
149
+ color: white;
150
+ }
151
+
152
+ footer {
153
+ background: var(--dark);
154
+ color: white;
155
+ padding: 3rem 2rem;
156
+ text-align: center;
157
+ }
158
+ </style>
159
+ </head>
160
+
161
+ <body>
162
+ <nav>
163
+ <div class="nav-container"><a href="#" class="logo">๐ŸŒพ AgriSensa</a></div>
164
+ </nav>
165
+
166
+ <section class="hero">
167
+ <h1>๐ŸŒพ AgriSensa</h1>
168
+ <p style="font-size: 1.5rem; margin-bottom: 2rem;">Platform Pertanian Cerdas Berbasis AI</p>
169
+ <p style="max-width: 800px; margin: 0 auto 2rem;">Platform komprehensif dengan 25+ modul AI untuk memberdayakan
170
+ petani Indonesia</p>
171
+ <a href="https://huggingface.co/spaces/yandri918/agrisensa-api" class="btn btn-primary" target="_blank">๐Ÿš€ Live
172
+ Demo</a>
173
+ </section>
174
+
175
+ <div class="container">
176
+ <section class="section">
177
+ <h2 class="section-title">Project Overview</h2>
178
+ <div class="grid grid-4">
179
+ <div class="card" style="text-align: center;">
180
+ <div style="font-size: 3rem; font-weight: 800; color: var(--primary);">25+</div>
181
+ <div>Modul AI</div>
182
+ </div>
183
+ <div class="card" style="text-align: center;">
184
+ <div style="font-size: 3rem; font-weight: 800; color: var(--primary);">7</div>
185
+ <div>ML Models</div>
186
+ </div>
187
+ <div class="card" style="text-align: center;">
188
+ <div style="font-size: 3rem; font-weight: 800; color: var(--primary);">20+</div>
189
+ <div>Komoditas</div>
190
+ </div>
191
+ <div class="card" style="text-align: center;">
192
+ <div style="font-size: 3rem; font-weight: 800; color: var(--primary);">99.5%</div>
193
+ <div>Uptime</div>
194
+ </div>
195
+ </div>
196
+ </section>
197
+
198
+ <section class="section">
199
+ <h2 class="section-title">Key Features</h2>
200
+ <div class="grid grid-3">
201
+ <div class="card">
202
+ <div style="font-size: 3rem;">๐Ÿค–</div>
203
+ <h3>AgriBot AI</h3>
204
+ <p>Chatbot cerdas berbasis Google Gemini AI</p>
205
+ </div>
206
+ <div class="card">
207
+ <div style="font-size: 3rem;">๐ŸŒพ</div>
208
+ <h3>Crop Recommendation</h3>
209
+ <p>Analisis 7 parameter untuk rekomendasi optimal</p>
210
+ </div>
211
+ <div class="card">
212
+ <div style="font-size: 3rem;">๐Ÿ”ฌ</div>
213
+ <h3>Disease Detection</h3>
214
+ <p>Computer Vision dengan Roboflow</p>
215
+ </div>
216
+ <div class="card">
217
+ <div style="font-size: 3rem;">๐Ÿ“Š</div>
218
+ <h3>Market Intelligence</h3>
219
+ <p>Prediksi harga dengan ML</p>
220
+ </div>
221
+ <div class="card">
222
+ <div style="font-size: 3rem;">๐Ÿงฎ</div>
223
+ <h3>Fertilizer Calculator</h3>
224
+ <p>Perhitungan pupuk presisi</p>
225
+ </div>
226
+ <div class="card">
227
+ <div style="font-size: 3rem;">๐Ÿ“š</div>
228
+ <h3>Knowledge Base</h3>
229
+ <p>Database lengkap SOP budidaya</p>
230
+ </div>
231
+ </div>
232
+ </section>
233
+
234
+ <section class="section">
235
+ <h2 class="section-title">Technology Stack</h2>
236
+ <div class="grid grid-3">
237
+ <div class="card">
238
+ <h3 style="color: var(--primary);">Backend</h3>
239
+ <ul style="list-style: none;">
240
+ <li>โœ“ Python 3.12</li>
241
+ <li>โœ“ Flask 3.0</li>
242
+ <li>โœ“ SQLAlchemy</li>
243
+ </ul>
244
+ </div>
245
+ <div class="card">
246
+ <h3 style="color: var(--primary);">Machine Learning</h3>
247
+ <ul style="list-style: none;">
248
+ <li>โœ“ scikit-learn</li>
249
+ <li>โœ“ TensorFlow</li>
250
+ <li>โœ“ SHAP (XAI)</li>
251
+ </ul>
252
+ </div>
253
+ <div class="card">
254
+ <h3 style="color: var(--primary);">AI Services</h3>
255
+ <ul style="list-style: none;">
256
+ <li>โœ“ Google Gemini</li>
257
+ <li>โœ“ Roboflow CV</li>
258
+ <li>โœ“ Custom Models</li>
259
+ </ul>
260
+ </div>
261
+ </div>
262
+ </section>
263
+ </div>
264
+
265
+ <footer>
266
+ <h2 style="font-family: 'Poppins', sans-serif; margin-bottom: 1rem;">Get In Touch</h2>
267
+ <div style="display: flex; justify-content: center; gap: 2rem; margin: 2rem 0;">
268
+ <a href="https://github.com/yandri918" style="color: white; text-decoration: none;" target="_blank">๐Ÿ“‚
269
+ GitHub</a>
270
+ <a href="https://linkedin.com/in/yandri918" style="color: white; text-decoration: none;" target="_blank">๐Ÿ’ผ
271
+ LinkedIn</a>
272
+ </div>
273
+ <p>&copy; 2025 Yandri918. Built with โค๏ธ for Indonesian Farmers</p>
274
+ </footer>
275
+ </body>
276
+
277
+ </html>
agrisensa-portfolio ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit bcf4b4a3f222c1a1191b1dbf22849bca718561f0
api/index.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from app import create_app
2
+
3
+ app = create_app()
4
+
5
+ # Vercel expects a variable named 'app' to be exposed
app/routes/legacy.py CHANGED
@@ -621,10 +621,12 @@ def get_historical_prices_endpoint():
621
  try:
622
  data = request.get_json()
623
  commodity = data.get('commodity')
 
 
624
  if not commodity:
625
  return jsonify({'success': False, 'error': 'Komoditas tidak dipilih'}), 400
626
 
627
- history = market_service.get_historical_prices(commodity, days=30)
628
  if not history:
629
  return jsonify({'success': False, 'error': 'Data tidak ditemukan'}), 404
630
 
 
621
  try:
622
  data = request.get_json()
623
  commodity = data.get('commodity')
624
+ days = int(data.get('range', 30)) # Default to 30 days if not specified
625
+
626
  if not commodity:
627
  return jsonify({'success': False, 'error': 'Komoditas tidak dipilih'}), 400
628
 
629
+ history = market_service.get_historical_prices(commodity, days=days)
630
  if not history:
631
  return jsonify({'success': False, 'error': 'Data tidak ditemukan'}), 404
632
 
app/routes/main.py CHANGED
@@ -11,25 +11,6 @@ def home():
11
  return render_template('home.html')
12
 
13
 
14
- @main_bp.route('/dashboard')
15
- def dashboard():
16
- """Render the new main dashboard page."""
17
- return render_template('home.html')
18
-
19
-
20
- @main_bp.route('/fruit-guide')
21
- def fruit_guide():
22
- """Render the fruit guide page."""
23
- return render_template('fruit_guide.html')
24
-
25
-
26
- @main_bp.route('/modules/analis-risiko-keberhasilan-ai')
27
- def analis_risiko_keberhasilan_ai():
28
- return render_template('modules/analis_risiko_keberhasilan_ai.html')
29
-
30
- @main_bp.route('/modules/analisis-npk-manual')
31
- def analisis_npk_manual():
32
- return render_template('modules/analisis_npk_manual.html')
33
 
34
  @main_bp.route('/modules/analisis-tren-harga')
35
  def analisis_tren_harga():
 
11
  return render_template('home.html')
12
 
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  @main_bp.route('/modules/analisis-tren-harga')
16
  def analisis_tren_harga():
fix_encoding.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import codecs
2
+
3
+ # Read the original file from git
4
+ import subprocess
5
+ result = subprocess.run(['git', 'show', '686dcb4:templates/home.html'],
6
+ capture_output=True, text=False, cwd=r'c:\Users\yandr\OneDrive\Desktop\agrisensa-api')
7
+ content = result.stdout.decode('utf-8')
8
+
9
+ # Add PWA support
10
+ # Find and add manifest link after the font link
11
+ content = content.replace(
12
+ ' rel="stylesheet" />',
13
+ ' rel="stylesheet" />\n <link rel="manifest" href="/static/manifest.json">\n <meta name="theme-color" content="#10b981">',
14
+ 1
15
+ )
16
+
17
+ # Add service worker registration before the ticker update
18
+ sw_code = ''' // Register Service Worker for PWA
19
+ if ('serviceWorker' in navigator) {
20
+ window.addEventListener('load', () => {
21
+ navigator.serviceWorker.register('/sw.js')
22
+ .then(registration => {
23
+ console.log('ServiceWorker registration successful');
24
+ })
25
+ .catch(err => {
26
+ console.log('ServiceWorker registration failed: ', err);
27
+ });
28
+ });
29
+ }
30
+
31
+ '''
32
+ content = content.replace(' // Init\n updateTicker();', sw_code + '// Init\n updateTicker();')
33
+
34
+ # Write with UTF-8 encoding
35
+ with codecs.open(r'c:\Users\yandr\OneDrive\Desktop\agrisensa-api\templates\home.html', 'w', encoding='utf-8') as f:
36
+ f.write(content)
37
+
38
+ print("File restored with correct UTF-8 encoding and PWA support added!")
list_models.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+
3
+ API_KEY = "AIzaSyBsO07f4MK3Sg7EPynrFmrriZgScmFBiQU"
4
+ genai.configure(api_key=API_KEY)
5
+
6
+ try:
7
+ print("Listing models...")
8
+ for m in genai.list_models():
9
+ if 'generateContent' in m.supported_generation_methods:
10
+ print(m.name)
11
+ except Exception as e:
12
+ print(f"Error: {e}")
templates/home.html CHANGED
@@ -8,33 +8,28 @@
8
  <link href="https://fonts.googleapis.com" rel="preconnect" />
9
  <link href="https://fonts.gstatic.com" crossorigin="" rel="preconnect" />
10
  <link
11
- href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Inter:wght@400;500;600&display=swap"
12
  rel="stylesheet" />
13
  <link rel="manifest" href="/static/manifest.json">
14
  <meta name="theme-color" content="#10b981">
15
  <style>
16
  :root {
17
  --primary: #10b981;
18
- /* Emerald 500 */
19
  --primary-dark: #059669;
20
- /* Emerald 600 */
21
  --secondary: #3b82f6;
22
- /* Blue 500 */
23
  --accent: #f59e0b;
24
- /* Amber 500 */
25
- --bg-body: #f8fafc;
26
- /* Slate 50 */
27
- --bg-card: #ffffff;
28
- --text-main: #1e293b;
29
- /* Slate 800 */
30
  --text-muted: #64748b;
31
- /* Slate 500 */
32
- --border: #e2e8f0;
33
- /* Slate 200 */
34
  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
35
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
36
- --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
37
- --radius: 16px;
 
 
 
38
  }
39
 
40
  * {
@@ -48,6 +43,12 @@
48
  background-color: var(--bg-body);
49
  color: var(--text-main);
50
  -webkit-font-smoothing: antialiased;
 
 
 
 
 
 
51
  }
52
 
53
  h1,
@@ -61,73 +62,71 @@
61
 
62
  /* Hero Section */
63
  .hero {
64
- background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
65
- color: white;
66
- padding: 80px 20px 60px;
67
- text-align: center;
68
  position: relative;
 
 
69
  overflow: hidden;
70
  }
71
 
72
- .hero::before {
73
- content: '';
74
- position: absolute;
75
- top: 0;
76
- left: 0;
77
- right: 0;
78
- bottom: 0;
79
- background: radial-gradient(circle at 20% 150%, rgba(16, 185, 129, 0.3) 0%, transparent 50%),
80
- radial-gradient(circle at 80% -50%, rgba(59, 130, 246, 0.3) 0%, transparent 50%);
81
- z-index: 1;
82
- }
83
-
84
  .hero-content {
85
  position: relative;
86
  z-index: 2;
87
- max-width: 800px;
88
  margin: 0 auto;
89
  }
90
 
91
  .hero h1 {
92
- font-size: 3.5rem;
93
- font-weight: 700;
94
- margin-bottom: 15px;
95
- letter-spacing: -1px;
96
- background: linear-gradient(to right, #34d399, #60a5fa);
97
  -webkit-background-clip: text;
98
  -webkit-text-fill-color: transparent;
 
 
99
  }
100
 
101
  .hero p {
102
  font-size: 1.25rem;
103
- color: #94a3b8;
104
- margin-bottom: 30px;
105
  line-height: 1.6;
 
 
 
 
106
  }
107
 
108
  .hero-badge {
109
  display: inline-flex;
110
  align-items: center;
111
- gap: 8px;
112
- background: rgba(255, 255, 255, 0.1);
113
- backdrop-filter: blur(10px);
114
- padding: 8px 16px;
115
  border-radius: 50px;
116
- border: 1px solid rgba(255, 255, 255, 0.2);
117
- font-size: 0.9rem;
118
- color: #e2e8f0;
 
 
 
119
  }
120
 
121
  /* Ticker */
122
  .ticker-wrap {
123
- background: #10b981;
124
- color: white;
 
 
 
125
  overflow: hidden;
126
- height: 40px;
127
- line-height: 40px;
128
- position: relative;
129
- z-index: 10;
130
- box-shadow: var(--shadow-md);
 
131
  }
132
 
133
  .ticker {
@@ -141,6 +140,7 @@
141
  display: inline-block;
142
  padding: 0 2rem;
143
  font-weight: 500;
 
144
  }
145
 
146
  @keyframes ticker {
@@ -157,93 +157,129 @@
157
  .container {
158
  max-width: 1280px;
159
  margin: 0 auto;
160
- padding: 40px 20px;
161
  }
162
 
163
  .section-header {
164
- margin-bottom: 40px;
165
  text-align: center;
 
166
  }
167
 
168
  .section-title {
169
- font-size: 2rem;
170
  font-weight: 700;
171
  color: var(--text-main);
172
- margin-bottom: 10px;
173
  }
174
 
175
  .section-subtitle {
 
176
  color: var(--text-muted);
177
  }
178
 
179
  /* Grid */
180
  .module-grid {
181
  display: grid;
182
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
183
- gap: 24px;
184
  }
185
 
186
  /* Module Card */
187
  .module-card {
188
- background: var(--bg-card);
 
 
189
  border-radius: var(--radius);
190
- padding: 24px;
191
- border: 1px solid var(--border);
192
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
193
  display: flex;
194
  flex-direction: column;
195
  position: relative;
196
  overflow: hidden;
197
  text-decoration: none;
198
  color: inherit;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
 
 
 
 
 
 
 
 
 
 
 
201
  .module-card:hover {
202
- transform: translateY(-5px);
203
  box-shadow: var(--shadow-lg);
204
- border-color: var(--primary);
 
205
  }
206
 
207
  .module-icon {
208
- font-size: 2.5rem;
209
- margin-bottom: 16px;
210
- background: var(--bg-body);
211
- width: 60px;
212
- height: 60px;
213
  display: flex;
214
  align-items: center;
215
  justify-content: center;
216
- border-radius: 12px;
217
- transition: transform 0.3s ease;
 
218
  }
219
 
220
  .module-card:hover .module-icon {
221
  transform: scale(1.1) rotate(5deg);
222
- background: #ecfdf5;
223
  }
224
 
225
  .module-title {
226
- font-size: 1.1rem;
227
  font-weight: 700;
228
- margin-bottom: 8px;
229
  color: var(--text-main);
 
230
  }
231
 
232
  .module-desc {
233
- font-size: 0.9rem;
234
  color: var(--text-muted);
235
- line-height: 1.5;
236
- margin-bottom: 20px;
237
  flex-grow: 1;
238
  }
239
 
240
  .module-link {
241
- font-size: 0.9rem;
242
  font-weight: 600;
243
  color: var(--primary-dark);
244
  display: flex;
245
  align-items: center;
246
- gap: 5px;
 
247
  }
248
 
249
  .module-link::after {
@@ -252,61 +288,87 @@
252
  }
253
 
254
  .module-card:hover .module-link::after {
255
- transform: translateX(5px);
256
  }
257
 
258
  /* Badges */
259
  .badge {
260
  position: absolute;
261
- top: 12px;
262
- right: 12px;
263
- padding: 4px 10px;
264
  border-radius: 20px;
265
  font-size: 0.75rem;
266
  font-weight: 700;
267
  text-transform: uppercase;
268
  letter-spacing: 0.5px;
 
269
  }
270
 
271
  .badge-new {
272
- background: #dbeafe;
273
  color: #1e40af;
274
  }
275
 
276
  .badge-hot {
277
- background: #fef3c7;
278
  color: #92400e;
279
  }
280
 
281
  /* Footer */
282
  .footer {
283
  text-align: center;
284
- padding: 40px 20px;
285
  color: var(--text-muted);
286
- font-size: 0.9rem;
287
- border-top: 1px solid var(--border);
288
- margin-top: 60px;
289
- background: white;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  }
291
 
292
  /* Responsive */
293
  @media (max-width: 768px) {
294
  .hero h1 {
295
- font-size: 2.5rem;
296
  }
297
 
298
  .hero p {
299
- font-size: 1rem;
300
  }
301
 
302
  .module-grid {
303
  grid-template-columns: 1fr;
304
  }
 
 
 
 
305
  }
306
  </style>
307
  </head>
308
 
309
  <body>
 
 
 
 
 
 
 
310
  <!-- Hero -->
311
  <div class="hero">
312
  <div class="hero-content">
@@ -316,13 +378,6 @@
316
  </div>
317
  </div>
318
 
319
- <!-- Ticker -->
320
- <div class="ticker-wrap">
321
- <div class="ticker" id="price-ticker">
322
- <div class="ticker-item">Memuat data harga pasar terkini...</div>
323
- </div>
324
- </div>
325
-
326
  <!-- Main Content -->
327
  <div class="container">
328
  <div class="section-header">
@@ -333,7 +388,7 @@
333
  <div class="module-grid">
334
 
335
  <!-- Featured Modules -->
336
- <a href="/modules/katalog-pupuk" class="module-card" style="border-color: var(--primary);">
337
  <span class="badge badge-new">Baru</span>
338
  <div class="module-icon">๐Ÿ“‹</div>
339
  <h3 class="module-title">Katalog Pupuk</h3>
@@ -341,11 +396,11 @@
341
  <span class="module-link">Buka Katalog</span>
342
  </a>
343
 
344
- <a href="/modules/chatbot" class="module-card" style="border-color: var(--primary);">
345
  <span class="badge badge-hot">AI</span>
346
  <div class="module-icon">๐Ÿค–</div>
347
  <h3 class="module-title">AgriBot</h3>
348
- <p class="module-desc">Asisten pertanian cerdas siap menjawab pertanyaan Anda.</p>
349
  <span class="module-link">Chat Sekarang</span>
350
  </a>
351
 
@@ -390,7 +445,7 @@
390
  <a href="/modules/analisis-tren-harga" class="module-card">
391
  <div class="module-icon">๐Ÿ“ˆ</div>
392
  <h3 class="module-title">Analisis Tren</h3>
393
- <p class="module-desc">Visualisasi dan prediksi tren harga komoditas.</p>
394
  <span class="module-link">Lihat Tren</span>
395
  </a>
396
 
@@ -551,7 +606,7 @@
551
 
552
  items.forEach(item => {
553
  html += `<div class="ticker-item">
554
- ${item.name}: <span style="color: #a7f3d0; font-weight: 700;">${new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(item.price)}</span> / ${item.unit}
555
  </div>`;
556
  });
557
  ticker.innerHTML = html;
 
8
  <link href="https://fonts.googleapis.com" rel="preconnect" />
9
  <link href="https://fonts.gstatic.com" crossorigin="" rel="preconnect" />
10
  <link
11
+ href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=Inter:wght@400;500;600&display=swap"
12
  rel="stylesheet" />
13
  <link rel="manifest" href="/static/manifest.json">
14
  <meta name="theme-color" content="#10b981">
15
  <style>
16
  :root {
17
  --primary: #10b981;
 
18
  --primary-dark: #059669;
 
19
  --secondary: #3b82f6;
 
20
  --accent: #f59e0b;
21
+ --bg-body: #f0fdf4;
22
+ --bg-card: rgba(255, 255, 255, 0.8);
23
+ --text-main: #0f172a;
 
 
 
24
  --text-muted: #64748b;
25
+ --border: rgba(226, 232, 240, 0.8);
 
 
26
  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
27
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
28
+ --shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
29
+ --radius: 24px;
30
+ --glass-border: 1px solid rgba(255, 255, 255, 0.5);
31
+ --glass-bg: rgba(255, 255, 255, 0.7);
32
+ --glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.07);
33
  }
34
 
35
  * {
 
43
  background-color: var(--bg-body);
44
  color: var(--text-main);
45
  -webkit-font-smoothing: antialiased;
46
+ background-image:
47
+ radial-gradient(at 0% 0%, rgba(16, 185, 129, 0.15) 0px, transparent 50%),
48
+ radial-gradient(at 100% 0%, rgba(59, 130, 246, 0.15) 0px, transparent 50%),
49
+ radial-gradient(at 100% 100%, rgba(245, 158, 11, 0.1) 0px, transparent 50%);
50
+ background-attachment: fixed;
51
+ min-height: 100vh;
52
  }
53
 
54
  h1,
 
62
 
63
  /* Hero Section */
64
  .hero {
 
 
 
 
65
  position: relative;
66
+ padding: 100px 20px 80px;
67
+ text-align: center;
68
  overflow: hidden;
69
  }
70
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  .hero-content {
72
  position: relative;
73
  z-index: 2;
74
+ max-width: 900px;
75
  margin: 0 auto;
76
  }
77
 
78
  .hero h1 {
79
+ font-size: 4rem;
80
+ font-weight: 800;
81
+ margin-bottom: 20px;
82
+ line-height: 1.1;
83
+ background: linear-gradient(135deg, #059669 0%, #2563eb 100%);
84
  -webkit-background-clip: text;
85
  -webkit-text-fill-color: transparent;
86
+ letter-spacing: -0.02em;
87
+ animation: fadeUp 0.8s ease-out;
88
  }
89
 
90
  .hero p {
91
  font-size: 1.25rem;
92
+ color: var(--text-muted);
93
+ margin-bottom: 40px;
94
  line-height: 1.6;
95
+ max-width: 700px;
96
+ margin-left: auto;
97
+ margin-right: auto;
98
+ animation: fadeUp 0.8s ease-out 0.2s backwards;
99
  }
100
 
101
  .hero-badge {
102
  display: inline-flex;
103
  align-items: center;
104
+ gap: 10px;
105
+ background: white;
106
+ padding: 10px 24px;
 
107
  border-radius: 50px;
108
+ border: var(--glass-border);
109
+ box-shadow: var(--shadow-md);
110
+ font-size: 0.95rem;
111
+ font-weight: 600;
112
+ color: var(--text-main);
113
+ animation: fadeUp 0.8s ease-out 0.4s backwards;
114
  }
115
 
116
  /* Ticker */
117
  .ticker-wrap {
118
+ background: rgba(255, 255, 255, 0.9);
119
+ backdrop-filter: blur(10px);
120
+ border-top: 1px solid rgba(0, 0, 0, 0.05);
121
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
122
+ color: var(--text-main);
123
  overflow: hidden;
124
+ height: 50px;
125
+ line-height: 50px;
126
+ position: sticky;
127
+ top: 0;
128
+ z-index: 100;
129
+ box-shadow: var(--shadow-sm);
130
  }
131
 
132
  .ticker {
 
140
  display: inline-block;
141
  padding: 0 2rem;
142
  font-weight: 500;
143
+ font-size: 0.95rem;
144
  }
145
 
146
  @keyframes ticker {
 
157
  .container {
158
  max-width: 1280px;
159
  margin: 0 auto;
160
+ padding: 60px 20px;
161
  }
162
 
163
  .section-header {
164
+ margin-bottom: 50px;
165
  text-align: center;
166
+ animation: fadeUp 0.8s ease-out 0.6s backwards;
167
  }
168
 
169
  .section-title {
170
+ font-size: 2.5rem;
171
  font-weight: 700;
172
  color: var(--text-main);
173
+ margin-bottom: 12px;
174
  }
175
 
176
  .section-subtitle {
177
+ font-size: 1.1rem;
178
  color: var(--text-muted);
179
  }
180
 
181
  /* Grid */
182
  .module-grid {
183
  display: grid;
184
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
185
+ gap: 30px;
186
  }
187
 
188
  /* Module Card */
189
  .module-card {
190
+ background: var(--glass-bg);
191
+ backdrop-filter: blur(12px);
192
+ border: var(--glass-border);
193
  border-radius: var(--radius);
194
+ padding: 30px;
195
+ box-shadow: var(--glass-shadow);
196
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
197
  display: flex;
198
  flex-direction: column;
199
  position: relative;
200
  overflow: hidden;
201
  text-decoration: none;
202
  color: inherit;
203
+ height: 100%;
204
+ animation: fadeUp 0.8s ease-out backwards;
205
+ }
206
+
207
+ /* Staggered animation for cards */
208
+ .module-card:nth-child(1) {
209
+ animation-delay: 0.1s;
210
+ }
211
+
212
+ .module-card:nth-child(2) {
213
+ animation-delay: 0.15s;
214
+ }
215
+
216
+ .module-card:nth-child(3) {
217
+ animation-delay: 0.2s;
218
+ }
219
+
220
+ .module-card:nth-child(4) {
221
+ animation-delay: 0.25s;
222
  }
223
 
224
+ .module-card:nth-child(5) {
225
+ animation-delay: 0.3s;
226
+ }
227
+
228
+ .module-card:nth-child(6) {
229
+ animation-delay: 0.35s;
230
+ }
231
+
232
+ /* ... add more if needed or use JS */
233
+
234
  .module-card:hover {
235
+ transform: translateY(-8px) scale(1.02);
236
  box-shadow: var(--shadow-lg);
237
+ background: rgba(255, 255, 255, 0.9);
238
+ border-color: rgba(16, 185, 129, 0.3);
239
  }
240
 
241
  .module-icon {
242
+ font-size: 3rem;
243
+ margin-bottom: 20px;
244
+ background: linear-gradient(135deg, #ecfdf5 0%, #eff6ff 100%);
245
+ width: 70px;
246
+ height: 70px;
247
  display: flex;
248
  align-items: center;
249
  justify-content: center;
250
+ border-radius: 20px;
251
+ transition: transform 0.4s ease;
252
+ box-shadow: inset 0 2px 4px rgba(255, 255, 255, 0.8), 0 4px 6px rgba(0, 0, 0, 0.05);
253
  }
254
 
255
  .module-card:hover .module-icon {
256
  transform: scale(1.1) rotate(5deg);
 
257
  }
258
 
259
  .module-title {
260
+ font-size: 1.25rem;
261
  font-weight: 700;
262
+ margin-bottom: 10px;
263
  color: var(--text-main);
264
+ letter-spacing: -0.01em;
265
  }
266
 
267
  .module-desc {
268
+ font-size: 0.95rem;
269
  color: var(--text-muted);
270
+ line-height: 1.6;
271
+ margin-bottom: 24px;
272
  flex-grow: 1;
273
  }
274
 
275
  .module-link {
276
+ font-size: 0.95rem;
277
  font-weight: 600;
278
  color: var(--primary-dark);
279
  display: flex;
280
  align-items: center;
281
+ gap: 6px;
282
+ margin-top: auto;
283
  }
284
 
285
  .module-link::after {
 
288
  }
289
 
290
  .module-card:hover .module-link::after {
291
+ transform: translateX(6px);
292
  }
293
 
294
  /* Badges */
295
  .badge {
296
  position: absolute;
297
+ top: 16px;
298
+ right: 16px;
299
+ padding: 6px 12px;
300
  border-radius: 20px;
301
  font-size: 0.75rem;
302
  font-weight: 700;
303
  text-transform: uppercase;
304
  letter-spacing: 0.5px;
305
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
306
  }
307
 
308
  .badge-new {
309
+ background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
310
  color: #1e40af;
311
  }
312
 
313
  .badge-hot {
314
+ background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
315
  color: #92400e;
316
  }
317
 
318
  /* Footer */
319
  .footer {
320
  text-align: center;
321
+ padding: 60px 20px;
322
  color: var(--text-muted);
323
+ font-size: 0.95rem;
324
+ border-top: 1px solid rgba(0, 0, 0, 0.05);
325
+ margin-top: 80px;
326
+ background: rgba(255, 255, 255, 0.5);
327
+ backdrop-filter: blur(5px);
328
+ }
329
+
330
+ /* Animations */
331
+ @keyframes fadeUp {
332
+ from {
333
+ opacity: 0;
334
+ transform: translateY(20px);
335
+ }
336
+
337
+ to {
338
+ opacity: 1;
339
+ transform: translateY(0);
340
+ }
341
  }
342
 
343
  /* Responsive */
344
  @media (max-width: 768px) {
345
  .hero h1 {
346
+ font-size: 2.75rem;
347
  }
348
 
349
  .hero p {
350
+ font-size: 1.1rem;
351
  }
352
 
353
  .module-grid {
354
  grid-template-columns: 1fr;
355
  }
356
+
357
+ .container {
358
+ padding: 40px 20px;
359
+ }
360
  }
361
  </style>
362
  </head>
363
 
364
  <body>
365
+ <!-- Ticker -->
366
+ <div class="ticker-wrap">
367
+ <div class="ticker" id="price-ticker">
368
+ <div class="ticker-item">Memuat data harga pasar terkini...</div>
369
+ </div>
370
+ </div>
371
+
372
  <!-- Hero -->
373
  <div class="hero">
374
  <div class="hero-content">
 
378
  </div>
379
  </div>
380
 
 
 
 
 
 
 
 
381
  <!-- Main Content -->
382
  <div class="container">
383
  <div class="section-header">
 
388
  <div class="module-grid">
389
 
390
  <!-- Featured Modules -->
391
+ <a href="/modules/katalog-pupuk" class="module-card">
392
  <span class="badge badge-new">Baru</span>
393
  <div class="module-icon">๐Ÿ“‹</div>
394
  <h3 class="module-title">Katalog Pupuk</h3>
 
396
  <span class="module-link">Buka Katalog</span>
397
  </a>
398
 
399
+ <a href="/modules/chatbot" class="module-card">
400
  <span class="badge badge-hot">AI</span>
401
  <div class="module-icon">๐Ÿค–</div>
402
  <h3 class="module-title">AgriBot</h3>
403
+ <p class="module-desc">Asisten pertanian cerdas siap menjawab pertanyaan Anda 24/7.</p>
404
  <span class="module-link">Chat Sekarang</span>
405
  </a>
406
 
 
445
  <a href="/modules/analisis-tren-harga" class="module-card">
446
  <div class="module-icon">๐Ÿ“ˆ</div>
447
  <h3 class="module-title">Analisis Tren</h3>
448
+ <p class="module-desc">Visualisasi dan prediksi tren harga komoditas dengan AI.</p>
449
  <span class="module-link">Lihat Tren</span>
450
  </a>
451
 
 
606
 
607
  items.forEach(item => {
608
  html += `<div class="ticker-item">
609
+ ${item.name}: <span style="color: #10b981; font-weight: 700;">${new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(item.price)}</span> / ${item.unit}
610
  </div>`;
611
  });
612
  ticker.innerHTML = html;
templates/modules/analisis_tren_harga.html CHANGED
@@ -198,6 +198,11 @@
198
  <option value="telur_ayam">Telur Ayam</option>
199
  <option value="daging_ayam">Daging Ayam</option>
200
  </select>
 
 
 
 
 
201
  <button class="btn btn-primary" onclick="predictTrend()" id="predict-btn">
202
  <span>๐Ÿ”ฎ</span> Prediksi Tren 7 Hari
203
  </button>
@@ -227,17 +232,22 @@
227
  loadHistoricalData();
228
  document.getElementById('insight-box').style.display = 'none';
229
  });
 
 
 
 
230
  });
231
 
232
  async function loadHistoricalData() {
233
  const commodity = document.getElementById('commodity-select').value;
 
234
  showLoading(true);
235
 
236
  try {
237
  const response = await fetch('/get-historical-prices', {
238
  method: 'POST',
239
  headers: { 'Content-Type': 'application/json' },
240
- body: JSON.stringify({ commodity: commodity })
241
  });
242
  const data = await response.json();
243
 
 
198
  <option value="telur_ayam">Telur Ayam</option>
199
  <option value="daging_ayam">Daging Ayam</option>
200
  </select>
201
+ <select id="range-select">
202
+ <option value="30">1 Bulan Terakhir</option>
203
+ <option value="180">6 Bulan Terakhir</option>
204
+ <option value="365" selected>1 Tahun Terakhir</option>
205
+ </select>
206
  <button class="btn btn-primary" onclick="predictTrend()" id="predict-btn">
207
  <span>๐Ÿ”ฎ</span> Prediksi Tren 7 Hari
208
  </button>
 
232
  loadHistoricalData();
233
  document.getElementById('insight-box').style.display = 'none';
234
  });
235
+
236
+ document.getElementById('range-select').addEventListener('change', () => {
237
+ loadHistoricalData();
238
+ });
239
  });
240
 
241
  async function loadHistoricalData() {
242
  const commodity = document.getElementById('commodity-select').value;
243
+ const range = document.getElementById('range-select').value;
244
  showLoading(true);
245
 
246
  try {
247
  const response = await fetch('/get-historical-prices', {
248
  method: 'POST',
249
  headers: { 'Content-Type': 'application/json' },
250
+ body: JSON.stringify({ commodity: commodity, range: range })
251
  });
252
  const data = await response.json();
253
 
vercel.json CHANGED
@@ -2,14 +2,14 @@
2
  "version": 2,
3
  "builds": [
4
  {
5
- "src": "run.py",
6
- "use": "@vercel/python"
7
  }
8
  ],
9
  "routes": [
10
  {
11
- "src": "/(.*)",
12
- "dest": "run.py"
13
  }
14
  ]
15
- }
 
2
  "version": 2,
3
  "builds": [
4
  {
5
+ "src": "AgriSensa_Portfolio.html",
6
+ "use": "@vercel/static"
7
  }
8
  ],
9
  "routes": [
10
  {
11
+ "src": "/",
12
+ "dest": "/AgriSensa_Portfolio.html"
13
  }
14
  ]
15
+ }