yashdave182 commited on
Commit
839b88c
Β·
1 Parent(s): dfe5873

Refactor demo management page for clarity and usability

Browse files
Files changed (1) hide show
  1. pages/demos.py +515 -219
pages/demos.py CHANGED
@@ -3,95 +3,165 @@ import streamlit as st
3
  import pandas as pd
4
  import plotly.express as px
5
  from datetime import datetime, timedelta
 
 
6
 
7
  def show_demos_page(db, whatsapp_manager=None):
8
  """Show demo management and tracking page"""
9
  st.title("🎯 Demo Management Center")
10
-
11
  if not db:
12
  st.error("Database not available. Please check initialization.")
13
  return
14
-
15
  # Tabs for different demo functions
16
- tab1, tab2, tab3, tab4 = st.tabs(["βž• Schedule Demo", "πŸ“‹ Demo Calendar", "πŸ“Š Demo Analytics", "πŸ”„ Follow-ups"])
17
-
 
 
18
  with tab1:
19
  show_schedule_demo_tab(db, whatsapp_manager)
20
-
21
  with tab2:
22
  show_demo_calendar_tab(db)
23
-
24
  with tab3:
25
  show_demo_analytics_tab(db)
26
-
27
  with tab4:
28
  show_follow_ups_tab(db, whatsapp_manager)
29
 
 
30
  def show_schedule_demo_tab(db, whatsapp_manager):
31
  """Show form to schedule new demos"""
32
  st.subheader("βž• Schedule New Demo")
33
-
 
 
 
 
 
 
34
  with st.form("schedule_demo_form"):
35
  st.markdown("### πŸ‘₯ Demo Information")
36
-
37
  col1, col2 = st.columns(2)
38
-
39
  with col1:
40
  # Customer selection
41
- customers = db.get_dataframe('customers', "SELECT customer_id, name, village FROM customers")
 
 
42
  if not customers.empty:
43
- customer_options = {f"{row['name']} ({row['village']})": row['customer_id']
44
- for _, row in customers.iterrows()}
45
- selected_customer = st.selectbox("Select Customer*", options=list(customer_options.keys()))
46
- customer_id = customer_options[selected_customer] if selected_customer else None
 
 
 
 
 
 
47
  else:
48
  st.warning("No customers found. Please add customers first.")
49
  customer_id = None
50
-
51
  # Distributor selection
52
- distributors = db.get_dataframe('distributors', "SELECT distributor_id, name, village FROM distributors")
 
 
53
  if not distributors.empty:
54
- distributor_options = {f"{row['name']} ({row['village']})": row['distributor_id']
55
- for _, row in distributors.iterrows()}
56
- selected_distributor = st.selectbox("Assign Distributor", options=[""] + list(distributor_options.keys()))
57
- distributor_id = distributor_options[selected_distributor] if selected_distributor else None
 
 
 
 
 
 
 
 
 
58
  else:
59
  distributor_id = None
60
-
61
  with col2:
62
  # Product selection
63
- products = db.get_dataframe('products', "SELECT product_id, product_name FROM products WHERE is_active = 1")
 
 
 
64
  if not products.empty:
65
- product_options = {row['product_name']: row['product_id'] for _, row in products.iterrows()}
66
- selected_product = st.selectbox("Product to Demo*", options=list(product_options.keys()))
67
- product_id = product_options[selected_product] if selected_product else None
 
 
 
 
 
 
 
68
  else:
69
  st.warning("No products found. Please add products first.")
70
  product_id = None
71
-
72
  # Demo details
73
- demo_date = st.date_input("Demo Date*", datetime.now())
74
  demo_time = st.time_input("Demo Time", datetime.now().time())
75
-
76
  st.markdown("### πŸ“ Demo Details")
77
-
78
  col1, col2 = st.columns(2)
79
-
80
  with col1:
81
- quantity_provided = st.number_input("Quantity Provided", min_value=0, value=1,
82
- help="Number of units provided for demo")
83
- demo_location = st.selectbox("Demo Location", ["Customer Home", "Distributor Office", "Public Place", "Other"])
84
-
 
 
 
 
 
 
 
85
  with col2:
86
- follow_up_date = st.date_input("Follow-up Date", datetime.now() + timedelta(days=7))
87
- conversion_status = st.selectbox("Initial Status", ["Scheduled", "Completed", "Cancelled"])
88
-
89
- notes = st.text_area("Demo Notes", placeholder="Any special instructions, customer requirements, or observations...")
90
-
 
 
 
 
 
 
 
91
  # Submit button
92
- submitted = st.form_submit_button("🎯 Schedule Demo", type="primary")
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
- if submitted:
 
 
 
 
95
  # Validation
96
  errors = []
97
  if not customer_id:
@@ -100,92 +170,175 @@ def show_schedule_demo_tab(db, whatsapp_manager):
100
  errors.append("Product selection is required")
101
  if not demo_date:
102
  errors.append("Demo date is required")
103
-
104
  if errors:
105
  for error in errors:
106
  st.error(f"❌ {error}")
107
  else:
 
108
  try:
109
- # Combine date and time
110
- demo_datetime = datetime.combine(demo_date, demo_time)
 
 
 
 
 
 
 
 
 
 
111
 
112
- # Add demo to database
113
- demo_id = add_demo_to_database(db, {
114
- 'customer_id': customer_id,
115
- 'distributor_id': distributor_id,
116
- 'product_id': product_id,
117
- 'demo_date': demo_datetime.date(),
118
- 'demo_time': demo_time,
119
- 'quantity_provided': quantity_provided,
120
- 'follow_up_date': follow_up_date,
121
- 'conversion_status': conversion_status,
122
- 'notes': notes,
123
- 'demo_location': demo_location
124
- })
 
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  if demo_id and demo_id > 0:
127
- st.success(f"βœ… Demo scheduled successfully! Demo ID: {demo_id}")
 
128
 
 
 
 
 
129
  # Send notification if WhatsApp available
130
  if whatsapp_manager and customer_id:
131
- send_demo_notification(whatsapp_manager, db, customer_id, demo_datetime, product_id)
132
-
133
- # Show demo summary
134
- show_demo_summary(db, demo_id)
135
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  else:
137
  st.error("❌ Failed to schedule demo. Please try again.")
138
-
139
  except Exception as e:
140
  st.error(f"❌ Error scheduling demo: {e}")
 
 
 
141
 
142
  def add_demo_to_database(db, demo_data):
143
  """Add demo record to database"""
144
  try:
145
- db.execute_query('''
146
- INSERT INTO demos (customer_id, distributor_id, product_id, demo_date, demo_time,
 
 
147
  quantity_provided, follow_up_date, conversion_status, notes, demo_location)
148
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
149
- ''', (
150
- demo_data['customer_id'],
151
- demo_data['distributor_id'],
152
- demo_data['product_id'],
153
- demo_data['demo_date'],
154
- demo_data['demo_time'].strftime('%H:%M:%S') if demo_data['demo_time'] else None,
155
- demo_data['quantity_provided'],
156
- demo_data['follow_up_date'],
157
- demo_data['conversion_status'],
158
- demo_data['notes'],
159
- demo_data['demo_location']
160
- ), log_action=False)
 
 
 
 
 
161
 
162
- # Get the inserted demo_id
163
- result = db.execute_query('SELECT last_insert_rowid()', log_action=False)
164
- return result[0][0] if result else -1
165
 
 
 
 
 
 
 
166
  except Exception as e:
167
  st.error(f"Database error: {e}")
 
 
168
  return -1
169
 
170
- def send_demo_notification(whatsapp_manager, db, customer_id, demo_datetime, product_id):
 
 
 
171
  """Send demo notification to customer"""
172
  try:
173
  # Get customer and product details
174
- customer = db.get_dataframe('customers', f"SELECT * FROM customers WHERE customer_id = {customer_id}")
175
- product = db.get_dataframe('products', f"SELECT * FROM products WHERE product_id = {product_id}")
176
-
 
 
 
 
177
  if not customer.empty and not product.empty:
178
  customer_data = customer.iloc[0]
179
  product_data = product.iloc[0]
180
-
181
- if customer_data.get('mobile'):
182
- message = f"""Hello {customer_data['name']}! πŸŽ‰
 
 
 
183
 
184
  We're excited to confirm your product demo!
185
 
186
- πŸ“… Date: {demo_datetime.strftime('%d %b %Y')}
187
- ⏰ Time: {demo_datetime.strftime('%I:%M %p')}
188
- πŸ“¦ Product: {product_data['product_name']}
189
 
190
  Our team will demonstrate the product features and answer any questions you may have.
191
 
@@ -193,144 +346,202 @@ We look forward to meeting you!
193
 
194
  Best regards,
195
  Sales Team"""
196
-
197
- success = whatsapp_manager.send_message(customer_data['mobile'], message)
 
 
198
  if success:
199
  st.success("πŸ“± Demo notification sent to customer!")
200
  else:
201
  st.warning("⚠️ Could not send demo notification")
202
-
203
  except Exception as e:
204
  st.warning(f"Could not send demo notification: {e}")
205
 
 
206
  def show_demo_summary(db, demo_id):
207
  """Show summary of scheduled demo"""
208
  try:
209
- demo_data = db.get_dataframe('demos', f'''
210
- SELECT d.*, c.name as customer_name, c.village, p.product_name,
 
 
211
  dist.name as distributor_name
212
  FROM demos d
213
  LEFT JOIN customers c ON d.customer_id = c.customer_id
214
  LEFT JOIN products p ON d.product_id = p.product_id
215
  LEFT JOIN distributors dist ON d.distributor_id = dist.distributor_id
216
  WHERE d.demo_id = {demo_id}
217
- ''')
218
-
 
219
  if not demo_data.empty:
220
  demo = demo_data.iloc[0]
221
-
222
  st.markdown("## πŸŽ‰ Demo Scheduled Successfully!")
223
-
224
  col1, col2 = st.columns(2)
225
-
226
  with col1:
227
  st.subheader("πŸ‘₯ Demo Details")
228
  st.write(f"**Demo ID:** {demo_id}")
229
  st.write(f"**Customer:** {demo['customer_name']}")
230
  st.write(f"**Village:** {demo['village']}")
231
  st.write(f"**Product:** {demo['product_name']}")
232
- if demo['distributor_name']:
233
  st.write(f"**Distributor:** {demo['distributor_name']}")
234
-
235
  with col2:
236
  st.subheader("πŸ“… Schedule")
237
  st.write(f"**Demo Date:** {demo['demo_date']}")
238
- if demo['demo_time']:
239
  st.write(f"**Demo Time:** {demo['demo_time']}")
240
  st.write(f"**Follow-up Date:** {demo['follow_up_date']}")
241
  st.write(f"**Status:** {demo['conversion_status']}")
242
-
243
  # Quick actions
244
  st.markdown("### ⚑ Quick Actions")
245
- col1, col2, col3 = st.columns(3)
246
-
247
  with col1:
 
 
 
 
 
248
  if st.button("πŸ“‹ View All Demos"):
249
  st.session_state.current_tab = "πŸ“‹ Demo Calendar"
250
-
251
- with col2:
252
- if st.button("βž• Schedule Another"):
253
  st.rerun()
254
-
255
  with col3:
 
 
 
 
256
  if st.button("πŸ“Š View Analytics"):
257
  st.session_state.current_tab = "πŸ“Š Demo Analytics"
258
-
 
259
  except Exception as e:
260
  st.error(f"Error displaying demo summary: {e}")
261
 
 
262
  def show_demo_calendar_tab(db):
263
  """Show demo calendar and upcoming demos"""
264
  st.subheader("πŸ“‹ Demo Calendar & Schedule")
265
-
266
  try:
267
  # Date range filter
268
  col1, col2 = st.columns(2)
269
  with col1:
270
- start_date = st.date_input("Start Date", datetime.now())
271
  with col2:
272
- end_date = st.date_input("End Date", datetime.now() + timedelta(days=30))
273
-
274
  # Status filter
275
- status_filter = st.multiselect("Filter by Status",
276
- ["Scheduled", "Completed", "Cancelled", "Converted", "Not Converted"],
277
- default=["Scheduled", "Completed"])
278
-
 
 
279
  # Get demos data
280
  demos_data = get_demos_data(db, start_date, end_date, status_filter)
281
-
282
  if not demos_data.empty:
 
 
 
283
  st.write(f"**πŸ“… Showing {len(demos_data)} demos**")
284
-
285
  # Upcoming demos (next 7 days)
286
  upcoming_demos = demos_data[
287
- (demos_data['demo_date'] <= datetime.now().date() + timedelta(days=7)) &
288
- (demos_data['conversion_status'] == 'Scheduled')
289
  ]
290
-
291
  if not upcoming_demos.empty:
292
  st.subheader("πŸš€ Upcoming Demos (Next 7 Days)")
293
- display_upcoming = upcoming_demos[['demo_date', 'customer_name', 'village', 'product_name', 'distributor_name']].copy()
294
- display_upcoming.columns = ['Date', 'Customer', 'Village', 'Product', 'Distributor']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  st.dataframe(display_upcoming, use_container_width=True)
296
-
297
  # All demos in date range
298
  st.subheader("πŸ“‹ All Demos")
299
- display_all = demos_data[['demo_date', 'customer_name', 'village', 'product_name', 'conversion_status', 'distributor_name']].copy()
300
- display_all.columns = ['Date', 'Customer', 'Village', 'Product', 'Status', 'Distributor']
301
- display_all = display_all.sort_values('Date', ascending=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  st.dataframe(display_all, use_container_width=True)
303
-
304
  # Demo statistics
305
  st.subheader("πŸ“Š Demo Statistics")
306
  col1, col2, col3, col4 = st.columns(4)
307
-
308
  with col1:
309
  total_demos = len(demos_data)
310
  st.metric("Total Demos", total_demos)
311
-
312
  with col2:
313
- scheduled = len(demos_data[demos_data['conversion_status'] == 'Scheduled'])
 
 
314
  st.metric("Scheduled", scheduled)
315
-
316
  with col3:
317
- completed = len(demos_data[demos_data['conversion_status'] == 'Completed'])
 
 
318
  st.metric("Completed", completed)
319
-
320
  with col4:
321
- converted = len(demos_data[demos_data['conversion_status'] == 'Converted'])
 
 
322
  st.metric("Converted", converted)
323
-
324
  else:
325
  st.info("No demos found for the selected criteria.")
326
-
327
  except Exception as e:
328
  st.error(f"Error loading demo calendar: {e}")
 
 
 
329
 
330
  def get_demos_data(db, start_date, end_date, status_filter):
331
  """Get demos data with filters"""
332
  try:
333
- query = '''
334
  SELECT d.*, c.name as customer_name, c.village, c.mobile,
335
  p.product_name, dist.name as distributor_name
336
  FROM demos d
@@ -338,30 +549,33 @@ def get_demos_data(db, start_date, end_date, status_filter):
338
  LEFT JOIN products p ON d.product_id = p.product_id
339
  LEFT JOIN distributors dist ON d.distributor_id = dist.distributor_id
340
  WHERE d.demo_date BETWEEN ? AND ?
341
- '''
342
-
343
  params = [start_date, end_date]
344
-
345
  if status_filter:
346
- placeholders = ','.join(['?' for _ in status_filter])
347
- query += f' AND d.conversion_status IN ({placeholders})'
348
  params.extend(status_filter)
349
-
350
- query += ' ORDER BY d.demo_date, d.demo_time'
351
-
352
- return db.get_dataframe('demos', query, params=params)
353
-
354
  except Exception as e:
355
  st.error(f"Error getting demos data: {e}")
356
  return pd.DataFrame()
357
 
 
358
  def show_demo_analytics_tab(db):
359
  """Show demo analytics and conversion rates"""
360
  st.subheader("πŸ“Š Demo Analytics")
361
-
362
  try:
363
  # Get demo conversion data
364
- demos_data = db.get_dataframe('demos', '''
 
 
365
  SELECT d.*, c.name as customer_name, c.village,
366
  p.product_name, dist.name as distributor_name
367
  FROM demos d
@@ -369,71 +583,99 @@ def show_demo_analytics_tab(db):
369
  LEFT JOIN products p ON d.product_id = p.product_id
370
  LEFT JOIN distributors dist ON d.distributor_id = dist.distributor_id
371
  ORDER BY d.demo_date DESC
372
- ''')
373
-
 
374
  if not demos_data.empty:
 
 
 
375
  # Conversion statistics
376
  st.subheader("🎯 Conversion Analytics")
377
-
378
  col1, col2, col3, col4 = st.columns(4)
379
-
380
  with col1:
381
  total_demos = len(demos_data)
382
  st.metric("Total Demos", total_demos)
383
-
384
  with col2:
385
- converted = len(demos_data[demos_data['conversion_status'] == 'Converted'])
 
 
386
  st.metric("Converted", converted)
387
-
388
  with col3:
389
- not_converted = len(demos_data[demos_data['conversion_status'] == 'Not Converted'])
 
 
390
  st.metric("Not Converted", not_converted)
391
-
392
  with col4:
393
- conversion_rate = (converted / total_demos * 100) if total_demos > 0 else 0
 
 
394
  st.metric("Conversion Rate", f"{conversion_rate:.1f}%")
395
-
396
  # Product-wise conversion
397
  st.subheader("πŸ“¦ Product Performance")
398
- if 'product_name' in demos_data.columns:
399
- product_stats = demos_data.groupby('product_name').agg({
400
- 'demo_id': 'count',
401
- 'conversion_status': lambda x: (x == 'Converted').sum()
402
- }).reset_index()
403
- product_stats.columns = ['Product', 'Total Demos', 'Converted']
404
- product_stats['Conversion Rate'] = (product_stats['Converted'] / product_stats['Total Demos'] * 100).round(1)
405
- product_stats = product_stats.sort_values('Total Demos', ascending=False)
406
-
 
 
 
 
 
 
 
 
 
 
407
  st.dataframe(product_stats, use_container_width=True)
408
-
409
  # Monthly trend
410
  st.subheader("πŸ“ˆ Monthly Demo Trend")
411
  try:
412
- demos_data['demo_date'] = pd.to_datetime(demos_data['demo_date'])
413
- monthly_trend = demos_data.groupby(demos_data['demo_date'].dt.to_period('M')).size()
 
 
414
  monthly_trend.index = monthly_trend.index.astype(str)
415
-
416
  if not monthly_trend.empty:
417
- fig = px.line(x=monthly_trend.index, y=monthly_trend.values,
418
- title='Monthly Demo Trend',
419
- labels={'x': 'Month', 'y': 'Number of Demos'})
 
 
 
420
  st.plotly_chart(fig, use_container_width=True)
421
  except:
422
  st.info("Could not generate monthly trend chart")
423
-
424
  else:
425
  st.info("No demo data available for analytics.")
426
-
427
  except Exception as e:
428
  st.error(f"Error loading demo analytics: {e}")
429
 
 
430
  def show_follow_ups_tab(db, whatsapp_manager):
431
  """Show demo follow-ups and conversion tracking"""
432
  st.subheader("πŸ”„ Demo Follow-ups")
433
-
434
  try:
435
  # Get demos needing follow-up
436
- follow_up_data = db.get_dataframe('demos', '''
 
 
437
  SELECT d.*, c.name as customer_name, c.mobile, c.village,
438
  p.product_name, dist.name as distributor_name
439
  FROM demos d
@@ -443,75 +685,129 @@ def show_follow_ups_tab(db, whatsapp_manager):
443
  WHERE d.follow_up_date <= date('now', '+7 days')
444
  AND d.conversion_status IN ('Completed', 'Not Converted')
445
  ORDER BY d.follow_up_date ASC
446
- ''')
447
-
 
448
  if not follow_up_data.empty:
 
 
 
 
 
449
  # Overdue follow-ups
450
- overdue = follow_up_data[follow_up_data['follow_up_date'] < datetime.now().date()]
 
 
451
  if not overdue.empty:
452
  st.warning(f"🚨 {len(overdue)} Overdue Follow-ups!")
453
- display_overdue = overdue[['follow_up_date', 'customer_name', 'village', 'product_name', 'conversion_status']].copy()
454
- display_overdue.columns = ['Due Date', 'Customer', 'Village', 'Product', 'Status']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  st.dataframe(display_overdue, use_container_width=True)
456
-
457
  # Upcoming follow-ups
458
- upcoming = follow_up_data[follow_up_data['follow_up_date'] >= datetime.now().date()]
 
 
459
  if not upcoming.empty:
460
  st.subheader("πŸ“… Upcoming Follow-ups")
461
- display_upcoming = upcoming[['follow_up_date', 'customer_name', 'village', 'product_name', 'conversion_status']].copy()
462
- display_upcoming.columns = ['Due Date', 'Customer', 'Village', 'Product', 'Status']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  st.dataframe(display_upcoming, use_container_width=True)
464
-
465
  # Follow-up actions
466
  st.subheader("πŸ”„ Follow-up Actions")
467
- selected_demo = st.selectbox("Select Demo for Follow-up",
468
- options=[f"{row['customer_name']} - {row['product_name']} ({row['follow_up_date']})"
469
- for _, row in follow_up_data.iterrows()])
470
-
 
 
 
 
471
  if selected_demo:
472
- demo_index = [f"{row['customer_name']} - {row['product_name']} ({row['follow_up_date']})"
473
- for _, row in follow_up_data.iterrows()].index(selected_demo)
 
 
474
  selected_demo_data = follow_up_data.iloc[demo_index]
475
-
476
  col1, col2 = st.columns(2)
477
-
478
  with col1:
479
- new_status = st.selectbox("Update Conversion Status",
480
- ["Converted", "Not Converted", "Follow-up Required", "Lost"])
481
-
 
 
482
  if st.button("πŸ”„ Update Status"):
483
- update_demo_status(db, selected_demo_data['demo_id'], new_status)
 
 
484
  st.success("βœ… Status updated successfully!")
485
  st.rerun()
486
-
487
  with col2:
488
  if whatsapp_manager and st.button("πŸ“± Send Follow-up Message"):
489
  send_follow_up_message(whatsapp_manager, selected_demo_data)
490
  st.success("βœ… Follow-up message sent!")
491
-
492
  else:
493
  st.success("πŸŽ‰ No pending follow-ups! All demos are up to date.")
494
-
495
  except Exception as e:
496
  st.error(f"Error loading follow-ups: {e}")
497
 
 
498
  def update_demo_status(db, demo_id, new_status):
499
  """Update demo conversion status"""
500
  try:
501
- db.execute_query('''
 
502
  UPDATE demos SET conversion_status = ?, updated_date = CURRENT_TIMESTAMP
503
  WHERE demo_id = ?
504
- ''', (new_status, demo_id), log_action=False)
 
 
 
505
  except Exception as e:
506
  st.error(f"Error updating demo status: {e}")
507
 
 
508
  def send_follow_up_message(whatsapp_manager, demo_data):
509
  """Send follow-up message for demo"""
510
  try:
511
- if demo_data.get('mobile'):
512
- message = f"""Hello {demo_data['customer_name']}! πŸ‘‹
513
 
514
- Following up on your {demo_data['product_name']} demo from {demo_data['demo_date']}.
515
 
516
  We'd love to hear about your experience and answer any questions you may have.
517
 
@@ -519,10 +815,10 @@ Would you be interested in placing an order or scheduling another demo?
519
 
520
  Best regards,
521
  Sales Team"""
522
-
523
- success = whatsapp_manager.send_message(demo_data['mobile'], message)
524
  return success
525
  return False
526
  except Exception as e:
527
  st.error(f"Error sending follow-up message: {e}")
528
- return False
 
3
  import pandas as pd
4
  import plotly.express as px
5
  from datetime import datetime, timedelta
6
+ import time
7
+
8
 
9
  def show_demos_page(db, whatsapp_manager=None):
10
  """Show demo management and tracking page"""
11
  st.title("🎯 Demo Management Center")
12
+
13
  if not db:
14
  st.error("Database not available. Please check initialization.")
15
  return
16
+
17
  # Tabs for different demo functions
18
+ tab1, tab2, tab3, tab4 = st.tabs(
19
+ ["βž• Schedule Demo", "πŸ“‹ Demo Calendar", "πŸ“Š Demo Analytics", "πŸ”„ Follow-ups"]
20
+ )
21
+
22
  with tab1:
23
  show_schedule_demo_tab(db, whatsapp_manager)
24
+
25
  with tab2:
26
  show_demo_calendar_tab(db)
27
+
28
  with tab3:
29
  show_demo_analytics_tab(db)
30
+
31
  with tab4:
32
  show_follow_ups_tab(db, whatsapp_manager)
33
 
34
+
35
  def show_schedule_demo_tab(db, whatsapp_manager):
36
  """Show form to schedule new demos"""
37
  st.subheader("βž• Schedule New Demo")
38
+
39
+ # Show demo summary if we just created one
40
+ if "last_demo_id" in st.session_state and st.session_state.last_demo_id:
41
+ show_demo_summary(db, st.session_state.last_demo_id)
42
+ st.session_state.last_demo_id = None # Clear after showing
43
+ st.divider()
44
+
45
  with st.form("schedule_demo_form"):
46
  st.markdown("### πŸ‘₯ Demo Information")
47
+
48
  col1, col2 = st.columns(2)
49
+
50
  with col1:
51
  # Customer selection
52
+ customers = db.get_dataframe(
53
+ "customers", "SELECT customer_id, name, village FROM customers"
54
+ )
55
  if not customers.empty:
56
+ customer_options = {
57
+ f"{row['name']} ({row['village']})": row["customer_id"]
58
+ for _, row in customers.iterrows()
59
+ }
60
+ selected_customer = st.selectbox(
61
+ "Select Customer*", options=list(customer_options.keys())
62
+ )
63
+ customer_id = (
64
+ customer_options[selected_customer] if selected_customer else None
65
+ )
66
  else:
67
  st.warning("No customers found. Please add customers first.")
68
  customer_id = None
69
+
70
  # Distributor selection
71
+ distributors = db.get_dataframe(
72
+ "distributors", "SELECT distributor_id, name, village FROM distributors"
73
+ )
74
  if not distributors.empty:
75
+ distributor_options = {
76
+ f"{row['name']} ({row['village']})": row["distributor_id"]
77
+ for _, row in distributors.iterrows()
78
+ }
79
+ selected_distributor = st.selectbox(
80
+ "Assign Distributor",
81
+ options=[""] + list(distributor_options.keys()),
82
+ )
83
+ distributor_id = (
84
+ distributor_options[selected_distributor]
85
+ if selected_distributor
86
+ else None
87
+ )
88
  else:
89
  distributor_id = None
90
+
91
  with col2:
92
  # Product selection
93
+ products = db.get_dataframe(
94
+ "products",
95
+ "SELECT product_id, product_name FROM products WHERE is_active = 1",
96
+ )
97
  if not products.empty:
98
+ product_options = {
99
+ row["product_name"]: row["product_id"]
100
+ for _, row in products.iterrows()
101
+ }
102
+ selected_product = st.selectbox(
103
+ "Product to Demo*", options=list(product_options.keys())
104
+ )
105
+ product_id = (
106
+ product_options[selected_product] if selected_product else None
107
+ )
108
  else:
109
  st.warning("No products found. Please add products first.")
110
  product_id = None
111
+
112
  # Demo details
113
+ demo_date = st.date_input("Demo Date*", datetime.now().date())
114
  demo_time = st.time_input("Demo Time", datetime.now().time())
115
+
116
  st.markdown("### πŸ“ Demo Details")
117
+
118
  col1, col2 = st.columns(2)
119
+
120
  with col1:
121
+ quantity_provided = st.number_input(
122
+ "Quantity Provided",
123
+ min_value=0,
124
+ value=1,
125
+ help="Number of units provided for demo",
126
+ )
127
+ demo_location = st.selectbox(
128
+ "Demo Location",
129
+ ["Customer Home", "Distributor Office", "Public Place", "Other"],
130
+ )
131
+
132
  with col2:
133
+ follow_up_date = st.date_input(
134
+ "Follow-up Date", datetime.now().date() + timedelta(days=7)
135
+ )
136
+ conversion_status = st.selectbox(
137
+ "Initial Status", ["Scheduled", "Completed", "Cancelled"]
138
+ )
139
+
140
+ notes = st.text_area(
141
+ "Demo Notes",
142
+ placeholder="Any special instructions, customer requirements, or observations...",
143
+ )
144
+
145
  # Submit button
146
+ submitted = st.form_submit_button(
147
+ "🎯 Schedule Demo",
148
+ type="primary",
149
+ )
150
+
151
+ # Handle form submission OUTSIDE the form to prevent resubmission
152
+ if submitted:
153
+ # Create unique submission ID to prevent duplicates
154
+ submission_id = f"{customer_id}_{product_id}_{demo_date}_{demo_time}_{int(time.time())}"
155
+
156
+ # Initialize submission tracking if not exists
157
+ if "processed_submissions" not in st.session_state:
158
+ st.session_state.processed_submissions = set()
159
 
160
+ # Check if this exact submission was already processed in this session
161
+ if submission_id in st.session_state.processed_submissions:
162
+ st.warning("⚠️ This demo was already submitted. Showing previously created demo.")
163
+ # Don't rerun, just show the existing demo
164
+ else:
165
  # Validation
166
  errors = []
167
  if not customer_id:
 
170
  errors.append("Product selection is required")
171
  if not demo_date:
172
  errors.append("Demo date is required")
173
+
174
  if errors:
175
  for error in errors:
176
  st.error(f"❌ {error}")
177
  else:
178
+ # Check if a similar demo already exists in database (within last 5 minutes)
179
  try:
180
+ recent_demos = db.get_dataframe(
181
+ "demos",
182
+ """
183
+ SELECT demo_id FROM demos
184
+ WHERE customer_id = ?
185
+ AND product_id = ?
186
+ AND demo_date = ?
187
+ AND created_date >= datetime('now', '-5 minutes')
188
+ ORDER BY demo_id DESC LIMIT 1
189
+ """,
190
+ params=(customer_id, product_id, demo_date)
191
+ )
192
 
193
+ if not recent_demos.empty:
194
+ existing_demo_id = recent_demos.iloc[0]['demo_id']
195
+ st.warning(f"⚠️ A similar demo (ID: {existing_demo_id}) was already created recently. Showing that demo instead.")
196
+ st.session_state.last_demo_id = existing_demo_id
197
+ st.session_state.processed_submissions.add(submission_id)
198
+ st.rerun()
199
+ return
200
+ except Exception as check_error:
201
+ st.warning(f"Could not check for existing demos: {check_error}")
202
+
203
+ try:
204
+ # Ensure demo_date is a single date object (not tuple)
205
+ if isinstance(demo_date, tuple):
206
+ demo_date = demo_date[0] if demo_date else datetime.now().date()
207
 
208
+ # Ensure follow_up_date is a single date object
209
+ if isinstance(follow_up_date, tuple):
210
+ follow_up_date = follow_up_date[0] if follow_up_date else datetime.now().date() + timedelta(days=7)
211
+
212
+ # Combine date and time for notification
213
+ demo_datetime = datetime.combine(demo_date, demo_time)
214
+
215
+ # Add demo to database
216
+ demo_id = add_demo_to_database(
217
+ db,
218
+ {
219
+ "customer_id": customer_id,
220
+ "distributor_id": distributor_id,
221
+ "product_id": product_id,
222
+ "demo_date": demo_date,
223
+ "demo_time": demo_time,
224
+ "quantity_provided": quantity_provided,
225
+ "follow_up_date": follow_up_date,
226
+ "conversion_status": conversion_status,
227
+ "notes": notes,
228
+ "demo_location": demo_location,
229
+ },
230
+ )
231
+
232
  if demo_id and demo_id > 0:
233
+ # Mark this submission as processed
234
+ st.session_state.processed_submissions.add(submission_id)
235
 
236
+ st.success(
237
+ f"βœ… Demo scheduled successfully! Demo ID: {demo_id}"
238
+ )
239
+
240
  # Send notification if WhatsApp available
241
  if whatsapp_manager and customer_id:
242
+ send_demo_notification(
243
+ whatsapp_manager,
244
+ db,
245
+ customer_id,
246
+ demo_datetime,
247
+ product_id,
248
+ )
249
+
250
+ # Store demo_id in session state to show summary outside form
251
+ st.session_state.last_demo_id = demo_id
252
+
253
+ # Set flag for dashboard notification
254
+ st.session_state.demo_created_notification = demo_id
255
+
256
+ # Update refresh time to ensure dashboard shows latest demos
257
+ st.session_state.demo_refresh_time = time.time()
258
+
259
+ # Rerun to show summary
260
+ st.rerun()
261
+
262
  else:
263
  st.error("❌ Failed to schedule demo. Please try again.")
264
+
265
  except Exception as e:
266
  st.error(f"❌ Error scheduling demo: {e}")
267
+ import traceback
268
+ st.code(traceback.format_exc())
269
+
270
 
271
  def add_demo_to_database(db, demo_data):
272
  """Add demo record to database"""
273
  try:
274
+ # Insert the demo - execute_query returns [(lastrowid,)] for INSERT
275
+ result = db.execute_query(
276
+ """
277
+ INSERT INTO demos (customer_id, distributor_id, product_id, demo_date, demo_time,
278
  quantity_provided, follow_up_date, conversion_status, notes, demo_location)
279
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
280
+ """,
281
+ (
282
+ demo_data["customer_id"],
283
+ demo_data["distributor_id"],
284
+ demo_data["product_id"],
285
+ demo_data["demo_date"],
286
+ demo_data["demo_time"].strftime("%H:%M:%S")
287
+ if demo_data.get("demo_time")
288
+ else None,
289
+ demo_data["quantity_provided"],
290
+ demo_data["follow_up_date"],
291
+ demo_data["conversion_status"],
292
+ demo_data["notes"],
293
+ demo_data["demo_location"],
294
+ ),
295
+ log_action=False,
296
+ )
297
 
298
+ # The execute_query method returns [(lastrowid,)] for INSERT queries
299
+ demo_id = result[0][0] if result and len(result) > 0 and result[0][0] else None
 
300
 
301
+ if demo_id:
302
+ return demo_id
303
+ else:
304
+ st.error("❌ Failed to get demo_id after insertion")
305
+ return -1
306
+
307
  except Exception as e:
308
  st.error(f"Database error: {e}")
309
+ import traceback
310
+ st.code(traceback.format_exc())
311
  return -1
312
 
313
+
314
+ def send_demo_notification(
315
+ whatsapp_manager, db, customer_id, demo_datetime, product_id
316
+ ):
317
  """Send demo notification to customer"""
318
  try:
319
  # Get customer and product details
320
+ customer = db.get_dataframe(
321
+ "customers", f"SELECT * FROM customers WHERE customer_id = {customer_id}"
322
+ )
323
+ product = db.get_dataframe(
324
+ "products", f"SELECT * FROM products WHERE product_id = {product_id}"
325
+ )
326
+
327
  if not customer.empty and not product.empty:
328
  customer_data = customer.iloc[0]
329
  product_data = product.iloc[0]
330
+
331
+ if customer_data.get("mobile"):
332
+ # Format time safely
333
+ time_str = demo_datetime.strftime("%I:%M %p")
334
+
335
+ message = f"""Hello {customer_data["name"]}! πŸŽ‰
336
 
337
  We're excited to confirm your product demo!
338
 
339
+ πŸ“… Date: {demo_datetime.strftime("%d %b %Y")}
340
+ ⏰ Time: {time_str}
341
+ πŸ“¦ Product: {product_data["product_name"]}
342
 
343
  Our team will demonstrate the product features and answer any questions you may have.
344
 
 
346
 
347
  Best regards,
348
  Sales Team"""
349
+
350
+ success = whatsapp_manager.send_message(
351
+ customer_data["mobile"], message
352
+ )
353
  if success:
354
  st.success("πŸ“± Demo notification sent to customer!")
355
  else:
356
  st.warning("⚠️ Could not send demo notification")
357
+
358
  except Exception as e:
359
  st.warning(f"Could not send demo notification: {e}")
360
 
361
+
362
  def show_demo_summary(db, demo_id):
363
  """Show summary of scheduled demo"""
364
  try:
365
+ demo_data = db.get_dataframe(
366
+ "demos",
367
+ f"""
368
+ SELECT d.*, c.name as customer_name, c.village, p.product_name,
369
  dist.name as distributor_name
370
  FROM demos d
371
  LEFT JOIN customers c ON d.customer_id = c.customer_id
372
  LEFT JOIN products p ON d.product_id = p.product_id
373
  LEFT JOIN distributors dist ON d.distributor_id = dist.distributor_id
374
  WHERE d.demo_id = {demo_id}
375
+ """,
376
+ )
377
+
378
  if not demo_data.empty:
379
  demo = demo_data.iloc[0]
380
+
381
  st.markdown("## πŸŽ‰ Demo Scheduled Successfully!")
382
+
383
  col1, col2 = st.columns(2)
384
+
385
  with col1:
386
  st.subheader("πŸ‘₯ Demo Details")
387
  st.write(f"**Demo ID:** {demo_id}")
388
  st.write(f"**Customer:** {demo['customer_name']}")
389
  st.write(f"**Village:** {demo['village']}")
390
  st.write(f"**Product:** {demo['product_name']}")
391
+ if demo.get("distributor_name"):
392
  st.write(f"**Distributor:** {demo['distributor_name']}")
393
+
394
  with col2:
395
  st.subheader("πŸ“… Schedule")
396
  st.write(f"**Demo Date:** {demo['demo_date']}")
397
+ if demo.get("demo_time"):
398
  st.write(f"**Demo Time:** {demo['demo_time']}")
399
  st.write(f"**Follow-up Date:** {demo['follow_up_date']}")
400
  st.write(f"**Status:** {demo['conversion_status']}")
401
+
402
  # Quick actions
403
  st.markdown("### ⚑ Quick Actions")
404
+ col1, col2, col3, col4 = st.columns(4)
405
+
406
  with col1:
407
+ if st.button("🏠 Go to Dashboard"):
408
+ st.session_state.current_page = "dashboard"
409
+ st.rerun()
410
+
411
+ with col2:
412
  if st.button("πŸ“‹ View All Demos"):
413
  st.session_state.current_tab = "πŸ“‹ Demo Calendar"
 
 
 
414
  st.rerun()
415
+
416
  with col3:
417
+ if st.button("βž• Schedule Another"):
418
+ st.rerun()
419
+
420
+ with col4:
421
  if st.button("πŸ“Š View Analytics"):
422
  st.session_state.current_tab = "πŸ“Š Demo Analytics"
423
+ st.rerun()
424
+
425
  except Exception as e:
426
  st.error(f"Error displaying demo summary: {e}")
427
 
428
+
429
  def show_demo_calendar_tab(db):
430
  """Show demo calendar and upcoming demos"""
431
  st.subheader("πŸ“‹ Demo Calendar & Schedule")
432
+
433
  try:
434
  # Date range filter
435
  col1, col2 = st.columns(2)
436
  with col1:
437
+ start_date = st.date_input("Start Date", datetime.now().date())
438
  with col2:
439
+ end_date = st.date_input("End Date", datetime.now().date() + timedelta(days=30))
440
+
441
  # Status filter
442
+ status_filter = st.multiselect(
443
+ "Filter by Status",
444
+ ["Scheduled", "Completed", "Cancelled", "Converted", "Not Converted"],
445
+ default=["Scheduled", "Completed"],
446
+ )
447
+
448
  # Get demos data
449
  demos_data = get_demos_data(db, start_date, end_date, status_filter)
450
+
451
  if not demos_data.empty:
452
+ # Convert demo_date to datetime for comparison
453
+ demos_data["demo_date"] = pd.to_datetime(demos_data["demo_date"]).dt.date
454
+
455
  st.write(f"**πŸ“… Showing {len(demos_data)} demos**")
456
+
457
  # Upcoming demos (next 7 days)
458
  upcoming_demos = demos_data[
459
+ (demos_data["demo_date"] <= datetime.now().date() + timedelta(days=7))
460
+ & (demos_data["conversion_status"] == "Scheduled")
461
  ]
462
+
463
  if not upcoming_demos.empty:
464
  st.subheader("πŸš€ Upcoming Demos (Next 7 Days)")
465
+ display_upcoming = upcoming_demos[
466
+ [
467
+ "demo_date",
468
+ "customer_name",
469
+ "village",
470
+ "product_name",
471
+ "distributor_name",
472
+ ]
473
+ ].copy()
474
+ display_upcoming.columns = [
475
+ "Date",
476
+ "Customer",
477
+ "Village",
478
+ "Product",
479
+ "Distributor",
480
+ ]
481
  st.dataframe(display_upcoming, use_container_width=True)
482
+
483
  # All demos in date range
484
  st.subheader("πŸ“‹ All Demos")
485
+ display_all = demos_data[
486
+ [
487
+ "demo_date",
488
+ "customer_name",
489
+ "village",
490
+ "product_name",
491
+ "conversion_status",
492
+ "distributor_name",
493
+ ]
494
+ ].copy()
495
+ display_all.columns = [
496
+ "Date",
497
+ "Customer",
498
+ "Village",
499
+ "Product",
500
+ "Status",
501
+ "Distributor",
502
+ ]
503
+ display_all = display_all.sort_values("Date", ascending=False)
504
  st.dataframe(display_all, use_container_width=True)
505
+
506
  # Demo statistics
507
  st.subheader("πŸ“Š Demo Statistics")
508
  col1, col2, col3, col4 = st.columns(4)
509
+
510
  with col1:
511
  total_demos = len(demos_data)
512
  st.metric("Total Demos", total_demos)
513
+
514
  with col2:
515
+ scheduled = len(
516
+ demos_data[demos_data["conversion_status"] == "Scheduled"]
517
+ )
518
  st.metric("Scheduled", scheduled)
519
+
520
  with col3:
521
+ completed = len(
522
+ demos_data[demos_data["conversion_status"] == "Completed"]
523
+ )
524
  st.metric("Completed", completed)
525
+
526
  with col4:
527
+ converted = len(
528
+ demos_data[demos_data["conversion_status"] == "Converted"]
529
+ )
530
  st.metric("Converted", converted)
531
+
532
  else:
533
  st.info("No demos found for the selected criteria.")
534
+
535
  except Exception as e:
536
  st.error(f"Error loading demo calendar: {e}")
537
+ import traceback
538
+ st.code(traceback.format_exc())
539
+
540
 
541
  def get_demos_data(db, start_date, end_date, status_filter):
542
  """Get demos data with filters"""
543
  try:
544
+ query = """
545
  SELECT d.*, c.name as customer_name, c.village, c.mobile,
546
  p.product_name, dist.name as distributor_name
547
  FROM demos d
 
549
  LEFT JOIN products p ON d.product_id = p.product_id
550
  LEFT JOIN distributors dist ON d.distributor_id = dist.distributor_id
551
  WHERE d.demo_date BETWEEN ? AND ?
552
+ """
553
+
554
  params = [start_date, end_date]
555
+
556
  if status_filter:
557
+ placeholders = ",".join(["?" for _ in status_filter])
558
+ query += f" AND d.conversion_status IN ({placeholders})"
559
  params.extend(status_filter)
560
+
561
+ query += " ORDER BY d.demo_date, d.demo_time"
562
+
563
+ return db.get_dataframe("demos", query, params=params)
564
+
565
  except Exception as e:
566
  st.error(f"Error getting demos data: {e}")
567
  return pd.DataFrame()
568
 
569
+
570
  def show_demo_analytics_tab(db):
571
  """Show demo analytics and conversion rates"""
572
  st.subheader("πŸ“Š Demo Analytics")
573
+
574
  try:
575
  # Get demo conversion data
576
+ demos_data = db.get_dataframe(
577
+ "demos",
578
+ """
579
  SELECT d.*, c.name as customer_name, c.village,
580
  p.product_name, dist.name as distributor_name
581
  FROM demos d
 
583
  LEFT JOIN products p ON d.product_id = p.product_id
584
  LEFT JOIN distributors dist ON d.distributor_id = dist.distributor_id
585
  ORDER BY d.demo_date DESC
586
+ """,
587
+ )
588
+
589
  if not demos_data.empty:
590
+ # Convert demo_date to datetime for proper handling
591
+ demos_data["demo_date"] = pd.to_datetime(demos_data["demo_date"])
592
+
593
  # Conversion statistics
594
  st.subheader("🎯 Conversion Analytics")
595
+
596
  col1, col2, col3, col4 = st.columns(4)
597
+
598
  with col1:
599
  total_demos = len(demos_data)
600
  st.metric("Total Demos", total_demos)
601
+
602
  with col2:
603
+ converted = len(
604
+ demos_data[demos_data["conversion_status"] == "Converted"]
605
+ )
606
  st.metric("Converted", converted)
607
+
608
  with col3:
609
+ not_converted = len(
610
+ demos_data[demos_data["conversion_status"] == "Not Converted"]
611
+ )
612
  st.metric("Not Converted", not_converted)
613
+
614
  with col4:
615
+ conversion_rate = (
616
+ (converted / total_demos * 100) if total_demos > 0 else 0
617
+ )
618
  st.metric("Conversion Rate", f"{conversion_rate:.1f}%")
619
+
620
  # Product-wise conversion
621
  st.subheader("πŸ“¦ Product Performance")
622
+ if "product_name" in demos_data.columns:
623
+ product_stats = (
624
+ demos_data.groupby("product_name")
625
+ .agg(
626
+ {
627
+ "demo_id": "count",
628
+ "conversion_status": lambda x: (x == "Converted").sum(),
629
+ }
630
+ )
631
+ .reset_index()
632
+ )
633
+ product_stats.columns = ["Product", "Total Demos", "Converted"]
634
+ product_stats["Conversion Rate"] = (
635
+ product_stats["Converted"] / product_stats["Total Demos"] * 100
636
+ ).round(1)
637
+ product_stats = product_stats.sort_values(
638
+ "Total Demos", ascending=False
639
+ )
640
+
641
  st.dataframe(product_stats, use_container_width=True)
642
+
643
  # Monthly trend
644
  st.subheader("πŸ“ˆ Monthly Demo Trend")
645
  try:
646
+ demos_data["demo_date"] = pd.to_datetime(demos_data["demo_date"])
647
+ monthly_trend = demos_data.groupby(
648
+ demos_data["demo_date"].dt.to_period("M")
649
+ ).size()
650
  monthly_trend.index = monthly_trend.index.astype(str)
651
+
652
  if not monthly_trend.empty:
653
+ fig = px.line(
654
+ x=monthly_trend.index,
655
+ y=monthly_trend.values,
656
+ title="Monthly Demo Trend",
657
+ labels={"x": "Month", "y": "Number of Demos"},
658
+ )
659
  st.plotly_chart(fig, use_container_width=True)
660
  except:
661
  st.info("Could not generate monthly trend chart")
662
+
663
  else:
664
  st.info("No demo data available for analytics.")
665
+
666
  except Exception as e:
667
  st.error(f"Error loading demo analytics: {e}")
668
 
669
+
670
  def show_follow_ups_tab(db, whatsapp_manager):
671
  """Show demo follow-ups and conversion tracking"""
672
  st.subheader("πŸ”„ Demo Follow-ups")
673
+
674
  try:
675
  # Get demos needing follow-up
676
+ follow_up_data = db.get_dataframe(
677
+ "demos",
678
+ """
679
  SELECT d.*, c.name as customer_name, c.mobile, c.village,
680
  p.product_name, dist.name as distributor_name
681
  FROM demos d
 
685
  WHERE d.follow_up_date <= date('now', '+7 days')
686
  AND d.conversion_status IN ('Completed', 'Not Converted')
687
  ORDER BY d.follow_up_date ASC
688
+ """,
689
+ )
690
+
691
  if not follow_up_data.empty:
692
+ # Convert dates to datetime for comparison
693
+ follow_up_data["follow_up_date"] = pd.to_datetime(
694
+ follow_up_data["follow_up_date"]
695
+ ).dt.date
696
+
697
  # Overdue follow-ups
698
+ overdue = follow_up_data[
699
+ follow_up_data["follow_up_date"] < datetime.now().date()
700
+ ]
701
  if not overdue.empty:
702
  st.warning(f"🚨 {len(overdue)} Overdue Follow-ups!")
703
+ display_overdue = overdue[
704
+ [
705
+ "follow_up_date",
706
+ "customer_name",
707
+ "village",
708
+ "product_name",
709
+ "conversion_status",
710
+ ]
711
+ ].copy()
712
+ display_overdue.columns = [
713
+ "Due Date",
714
+ "Customer",
715
+ "Village",
716
+ "Product",
717
+ "Status",
718
+ ]
719
  st.dataframe(display_overdue, use_container_width=True)
720
+
721
  # Upcoming follow-ups
722
+ upcoming = follow_up_data[
723
+ follow_up_data["follow_up_date"] >= datetime.now().date()
724
+ ]
725
  if not upcoming.empty:
726
  st.subheader("πŸ“… Upcoming Follow-ups")
727
+ display_upcoming = upcoming[
728
+ [
729
+ "follow_up_date",
730
+ "customer_name",
731
+ "village",
732
+ "product_name",
733
+ "conversion_status",
734
+ ]
735
+ ].copy()
736
+ display_upcoming.columns = [
737
+ "Due Date",
738
+ "Customer",
739
+ "Village",
740
+ "Product",
741
+ "Status",
742
+ ]
743
  st.dataframe(display_upcoming, use_container_width=True)
744
+
745
  # Follow-up actions
746
  st.subheader("πŸ”„ Follow-up Actions")
747
+ selected_demo = st.selectbox(
748
+ "Select Demo for Follow-up",
749
+ options=[
750
+ f"{row['customer_name']} - {row['product_name']} ({row['follow_up_date']})"
751
+ for _, row in follow_up_data.iterrows()
752
+ ],
753
+ )
754
+
755
  if selected_demo:
756
+ demo_index = [
757
+ f"{row['customer_name']} - {row['product_name']} ({row['follow_up_date']})"
758
+ for _, row in follow_up_data.iterrows()
759
+ ].index(selected_demo)
760
  selected_demo_data = follow_up_data.iloc[demo_index]
761
+
762
  col1, col2 = st.columns(2)
763
+
764
  with col1:
765
+ new_status = st.selectbox(
766
+ "Update Conversion Status",
767
+ ["Converted", "Not Converted", "Follow-up Required", "Lost"],
768
+ )
769
+
770
  if st.button("πŸ”„ Update Status"):
771
+ update_demo_status(
772
+ db, selected_demo_data["demo_id"], new_status
773
+ )
774
  st.success("βœ… Status updated successfully!")
775
  st.rerun()
776
+
777
  with col2:
778
  if whatsapp_manager and st.button("πŸ“± Send Follow-up Message"):
779
  send_follow_up_message(whatsapp_manager, selected_demo_data)
780
  st.success("βœ… Follow-up message sent!")
781
+
782
  else:
783
  st.success("πŸŽ‰ No pending follow-ups! All demos are up to date.")
784
+
785
  except Exception as e:
786
  st.error(f"Error loading follow-ups: {e}")
787
 
788
+
789
  def update_demo_status(db, demo_id, new_status):
790
  """Update demo conversion status"""
791
  try:
792
+ db.execute_query(
793
+ """
794
  UPDATE demos SET conversion_status = ?, updated_date = CURRENT_TIMESTAMP
795
  WHERE demo_id = ?
796
+ """,
797
+ (new_status, demo_id),
798
+ log_action=False,
799
+ )
800
  except Exception as e:
801
  st.error(f"Error updating demo status: {e}")
802
 
803
+
804
  def send_follow_up_message(whatsapp_manager, demo_data):
805
  """Send follow-up message for demo"""
806
  try:
807
+ if demo_data.get("mobile"):
808
+ message = f"""Hello {demo_data["customer_name"]}! πŸ‘‹
809
 
810
+ Following up on your {demo_data["product_name"]} demo from {demo_data["demo_date"]}.
811
 
812
  We'd love to hear about your experience and answer any questions you may have.
813
 
 
815
 
816
  Best regards,
817
  Sales Team"""
818
+
819
+ success = whatsapp_manager.send_message(demo_data["mobile"], message)
820
  return success
821
  return False
822
  except Exception as e:
823
  st.error(f"Error sending follow-up message: {e}")
824
+ return False