sammy786 commited on
Commit
ebda981
ยท
verified ยท
1 Parent(s): e24c1a1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +246 -41
app.py CHANGED
@@ -2,7 +2,8 @@ from typing import Dict, Any, Optional, Tuple, List
2
  import traceback
3
  import json
4
  import os
5
- from datetime import date
 
6
  import gradio as gr
7
  import plotly.graph_objects as go
8
  import httpx
@@ -544,7 +545,6 @@ Annual estimate: ${amount_float:.2f} ร— {frequency} = **${annual_spend:.2f}**
544
  except Exception as e:
545
  print(f"โŒ ERROR: {traceback.format_exc()}")
546
  yield f"โŒ **Error:** {str(e)}", None
547
-
548
 
549
  def create_agent_recommendation_chart_enhanced(result: Dict) -> go.Figure:
550
  try:
@@ -752,7 +752,6 @@ def get_recommendation_with_ai(user_id, merchant, category, amount):
752
  error_details = traceback.format_exc()
753
  print(f"Recommendation error: {error_details}")
754
  yield f"โŒ Error: {str(e)}\n\nPlease check your API connection or try again.", None
755
-
756
 
757
  def create_rewards_comparison_chart(data: Dict) -> go.Figure:
758
  """Create rewards comparison chart with proper error handling"""
@@ -900,7 +899,51 @@ def create_empty_chart(message: str) -> go.Figure:
900
  )
901
  fig.update_layout(height=400, template='plotly_white')
902
  return fig
903
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
 
905
  def update_analytics_with_charts(user_id: str):
906
  """Fetch and format analytics with charts for selected user"""
@@ -925,7 +968,7 @@ def update_analytics_with_charts(user_id: str):
925
  empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
926
  "Error loading data",
927
  "Error loading data",
928
- "Error loading data",
929
  f"*Error: {error_msg}*"
930
  )
931
 
@@ -938,11 +981,14 @@ def update_analytics_with_charts(user_id: str):
938
  empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
939
  "No data",
940
  "No data",
941
- "No data",
942
  "*No data available*"
943
  )
944
 
945
- metrics_html, table_md, insights_md, forecast_md = format_analytics_metrics(analytics_data)
 
 
 
946
 
947
  spending_fig = create_spending_chart(analytics_data)
948
  pie_fig = create_rewards_pie_chart(analytics_data)
@@ -950,7 +996,6 @@ def update_analytics_with_charts(user_id: str):
950
  trend_fig = create_trend_line_chart(analytics_data)
951
  performance_fig = create_card_performance_chart(analytics_data)
952
 
953
- from datetime import datetime
954
  status = f"*Analytics updated for {user_id} at {datetime.now().strftime('%I:%M %p')}*"
955
 
956
  return (
@@ -962,7 +1007,7 @@ def update_analytics_with_charts(user_id: str):
962
  trend_fig,
963
  table_md,
964
  insights_md,
965
- forecast_md,
966
  status
967
  )
968
 
@@ -979,10 +1024,10 @@ def update_analytics_with_charts(user_id: str):
979
  empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
980
  "Error loading table",
981
  "Error loading insights",
982
- "Error loading forecast",
983
  f"*{error_msg}*"
984
  )
985
-
986
  def _toggle_custom_mcc(use_custom: bool):
987
  return gr.update(visible=use_custom, value="")
988
 
@@ -1051,40 +1096,75 @@ def load_user_wallet(user_id: str):
1051
 
1052
  # ===================== NEW FUNCTION FOR FORECAST =====================
1053
  def load_user_forecast(user_id: str):
1054
- """Load and display spending forecast"""
1055
  try:
1056
  # Mock forecast data - replace with actual API call
1057
  forecast_data = {
1058
  'next_month_spending': 3250.50,
1059
  'predicted_rewards': 127.50,
 
1060
  'top_categories': [
1061
- {'category': 'Groceries', 'predicted': 850.00, 'confidence': 0.92},
1062
- {'category': 'Restaurants', 'predicted': 650.00, 'confidence': 0.88},
1063
- {'category': 'Gas', 'predicted': 450.00, 'confidence': 0.85},
1064
  ],
1065
  'recommendations': [
1066
  "Consider using Amex Gold for groceries to maximize 4x points",
1067
  "You're approaching your Citi Custom Cash $500 monthly cap",
1068
  "Travel spending is predicted to increase next month"
1069
- ]
 
1070
  }
1071
 
 
 
 
1072
  output = f"""
1073
- ## ๐Ÿ“ˆ Spending Forecast for Next Month
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1074
 
1075
- ### Predictions
1076
- - **Total Spending:** ${forecast_data['next_month_spending']:.2f}
1077
- - **Expected Rewards:** ${forecast_data['predicted_rewards']:.2f}
1078
 
1079
- ### Category Breakdown
1080
  """
1081
 
1082
  for cat in forecast_data['top_categories']:
1083
- output += f"- **{cat['category']}:** ${cat['predicted']:.2f} (Confidence: {cat['confidence']*100:.0f}%)\n"
 
 
 
 
 
 
1084
 
1085
- output += "\n### ๐Ÿ’ก Optimization Recommendations\n\n"
1086
- for rec in forecast_data['recommendations']:
1087
- output += f"- {rec}\n"
1088
 
1089
  # Create forecast chart
1090
  fig = go.Figure()
@@ -1101,7 +1181,7 @@ def load_user_forecast(user_id: str):
1101
  ))
1102
 
1103
  fig.update_layout(
1104
- title='Predicted Spending by Category',
1105
  xaxis_title='Category',
1106
  yaxis_title='Predicted Amount ($)',
1107
  template='plotly_white',
@@ -1160,6 +1240,81 @@ with gr.Blocks(
1160
  .metric-card-blue {
1161
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
1162
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1163
  table {
1164
  width: 100%;
1165
  border-collapse: collapse;
@@ -1339,7 +1494,6 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
1339
  # ==================== TAB 2: SMART WALLET ====================
1340
  with gr.Tab("๐Ÿ’ณ Smart Wallet"):
1341
  gr.Markdown("## Your Credit Card Portfolio")
1342
-
1343
  wallet_user = gr.Dropdown(
1344
  choices=SAMPLE_USERS,
1345
  value=SAMPLE_USERS[0],
@@ -1446,12 +1600,40 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
1446
  value="*Loading insights...*"
1447
  )
1448
 
1449
- gr.Markdown("---")
 
1450
 
1451
- forecast_display = gr.Markdown(
1452
- value="*Loading forecast...*"
 
 
 
 
 
 
 
 
 
 
 
 
 
1453
  )
1454
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1455
  analytics_status = gr.Markdown(
1456
  value="*Select a user to view analytics*",
1457
  elem_classes=["status-text"]
@@ -1470,7 +1652,7 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
1470
  trend_chart,
1471
  spending_table,
1472
  insights_display,
1473
- forecast_display,
1474
  analytics_status
1475
  ]
1476
  )
@@ -1487,7 +1669,7 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
1487
  trend_chart,
1488
  spending_table,
1489
  insights_display,
1490
- forecast_display,
1491
  analytics_status
1492
  ]
1493
  )
@@ -1504,24 +1686,47 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
1504
  trend_chart,
1505
  spending_table,
1506
  insights_display,
1507
- forecast_display,
1508
  analytics_status
1509
  ]
1510
  )
1511
 
1512
  # ==================== TAB 4: FORECAST ====================
1513
  with gr.Tab("๐Ÿ“ˆ Forecast"):
1514
- gr.Markdown("## Future Spending Predictions & Optimization")
 
 
 
 
 
 
 
 
 
1515
 
1516
- forecast_user = gr.Dropdown(
1517
- choices=SAMPLE_USERS,
1518
- value=SAMPLE_USERS[0],
1519
- label="๐Ÿ‘ค Select User"
1520
- )
 
 
 
1521
 
1522
- refresh_forecast_btn = gr.Button("๐Ÿ”„ Refresh Forecast", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
1523
 
1524
- forecast_output = gr.Markdown(value="*Loading forecast...*")
1525
  forecast_chart = gr.Plot()
1526
 
1527
  def update_forecast(user_id):
 
2
  import traceback
3
  import json
4
  import os
5
+ from datetime import date, datetime
6
+ import calendar
7
  import gradio as gr
8
  import plotly.graph_objects as go
9
  import httpx
 
545
  except Exception as e:
546
  print(f"โŒ ERROR: {traceback.format_exc()}")
547
  yield f"โŒ **Error:** {str(e)}", None
 
548
 
549
  def create_agent_recommendation_chart_enhanced(result: Dict) -> go.Figure:
550
  try:
 
752
  error_details = traceback.format_exc()
753
  print(f"Recommendation error: {error_details}")
754
  yield f"โŒ Error: {str(e)}\n\nPlease check your API connection or try again.", None
 
755
 
756
  def create_rewards_comparison_chart(data: Dict) -> go.Figure:
757
  """Create rewards comparison chart with proper error handling"""
 
899
  )
900
  fig.update_layout(height=400, template='plotly_white')
901
  return fig
902
+
903
+ # ===================== NEW: FORMAT CURRENT MONTH SUMMARY =====================
904
+ def format_current_month_summary(analytics_data):
905
+ """Format current month warnings with clear styling (for Analytics tab)"""
906
+
907
+ warnings = []
908
+
909
+ # Check for spending cap warnings
910
+ for card in analytics_data.get('card_usage', []):
911
+ if card.get('cap_percentage', 0) > 90:
912
+ warnings.append(
913
+ f"โš ๏ธ <strong>{card['name']}</strong>: "
914
+ f"${card['current_spend']:.0f} / ${card['cap']:.0f} "
915
+ f"({card['cap_percentage']:.0f}% used)"
916
+ )
917
+
918
+ # Calculate end-of-month projection
919
+ days_elapsed = datetime.now().day
920
+ days_in_month = calendar.monthrange(datetime.now().year, datetime.now().month)[1]
921
+ projection_ratio = days_in_month / days_elapsed if days_elapsed > 0 else 1
922
+
923
+ projected_spending = analytics_data.get('total_spending', 0) * projection_ratio
924
+ projected_rewards = analytics_data.get('total_rewards', 0) * projection_ratio
925
+
926
+ warnings_html = "<br>".join(warnings) if warnings else "โœ… No warnings - you're on track!"
927
+
928
+ return f"""
929
+ <div class="current-month-warning">
930
+ <h4>โš ๏ธ This Month's Status (as of {datetime.now().strftime('%B %d')})</h4>
931
+
932
+ <p><strong>Month-End Projection:</strong></p>
933
+ <ul style="margin: 10px 0; padding-left: 20px;">
934
+ <li>Estimated Total Spending: <strong>${projected_spending:.2f}</strong></li>
935
+ <li>Estimated Total Rewards: <strong>${projected_rewards:.2f}</strong></li>
936
+ </ul>
937
+
938
+ <p><strong>Spending Cap Alerts:</strong></p>
939
+ <p style="margin: 10px 0;">{warnings_html}</p>
940
+
941
+ <p style="margin-top: 15px; padding-top: 15px; border-top: 1px solid #ffcc80; font-size: 13px; color: #666;">
942
+ ๐Ÿ’ก <em>These are estimates based on your current month's activity.
943
+ For detailed future predictions, visit the <strong>Forecast tab</strong>.</em>
944
+ </p>
945
+ </div>
946
+ """
947
 
948
  def update_analytics_with_charts(user_id: str):
949
  """Fetch and format analytics with charts for selected user"""
 
968
  empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
969
  "Error loading data",
970
  "Error loading data",
971
+ f"<p>Error: {error_msg}</p>", # Changed from forecast_md
972
  f"*Error: {error_msg}*"
973
  )
974
 
 
981
  empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
982
  "No data",
983
  "No data",
984
+ "<p>No data available</p>", # Changed
985
  "*No data available*"
986
  )
987
 
988
+ metrics_html, table_md, insights_md, _ = format_analytics_metrics(analytics_data)
989
+
990
+ # Generate current month summary (NEW)
991
+ current_month_html = format_current_month_summary(analytics_data)
992
 
993
  spending_fig = create_spending_chart(analytics_data)
994
  pie_fig = create_rewards_pie_chart(analytics_data)
 
996
  trend_fig = create_trend_line_chart(analytics_data)
997
  performance_fig = create_card_performance_chart(analytics_data)
998
 
 
999
  status = f"*Analytics updated for {user_id} at {datetime.now().strftime('%I:%M %p')}*"
1000
 
1001
  return (
 
1007
  trend_fig,
1008
  table_md,
1009
  insights_md,
1010
+ current_month_html, # Changed from forecast_md
1011
  status
1012
  )
1013
 
 
1024
  empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
1025
  "Error loading table",
1026
  "Error loading insights",
1027
+ f"<p>{error_msg}</p>", # Changed
1028
  f"*{error_msg}*"
1029
  )
1030
+
1031
  def _toggle_custom_mcc(use_custom: bool):
1032
  return gr.update(visible=use_custom, value="")
1033
 
 
1096
 
1097
  # ===================== NEW FUNCTION FOR FORECAST =====================
1098
  def load_user_forecast(user_id: str):
1099
+ """Load and display spending forecast (comprehensive version for Forecast tab)"""
1100
  try:
1101
  # Mock forecast data - replace with actual API call
1102
  forecast_data = {
1103
  'next_month_spending': 3250.50,
1104
  'predicted_rewards': 127.50,
1105
+ 'confidence': 0.92,
1106
  'top_categories': [
1107
+ {'category': 'Groceries', 'predicted': 850.00, 'confidence': 0.92, 'emoji': '๐Ÿ›’'},
1108
+ {'category': 'Restaurants', 'predicted': 650.00, 'confidence': 0.88, 'emoji': '๐Ÿฝ๏ธ'},
1109
+ {'category': 'Gas', 'predicted': 450.00, 'confidence': 0.85, 'emoji': 'โ›ฝ'},
1110
  ],
1111
  'recommendations': [
1112
  "Consider using Amex Gold for groceries to maximize 4x points",
1113
  "You're approaching your Citi Custom Cash $500 monthly cap",
1114
  "Travel spending is predicted to increase next month"
1115
+ ],
1116
+ 'optimization_potential': 45.50
1117
  }
1118
 
1119
+ confidence = forecast_data.get('confidence', 0.85)
1120
+ confidence_color = '#4caf50' if confidence > 0.9 else '#ff9800' if confidence > 0.75 else '#f44336'
1121
+
1122
  output = f"""
1123
+ <div class="forecast-prediction">
1124
+ <h3>๐Ÿ”ฎ Next Month Forecast
1125
+ <span class="confidence-badge" style="background: {confidence_color};">
1126
+ {confidence*100:.0f}% Confidence
1127
+ </span>
1128
+ </h3>
1129
+
1130
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin: 20px 0;">
1131
+ <div style="background: white; padding: 15px; border-radius: 8px; text-align: center;">
1132
+ <p style="color: #666; margin: 0; font-size: 12px;">PREDICTED SPENDING</p>
1133
+ <p style="color: #0d47a1; margin: 5px 0; font-size: 28px; font-weight: 700;">
1134
+ ${forecast_data['next_month_spending']:.2f}
1135
+ </p>
1136
+ </div>
1137
+ <div style="background: white; padding: 15px; border-radius: 8px; text-align: center;">
1138
+ <p style="color: #666; margin: 0; font-size: 12px;">EXPECTED REWARDS</p>
1139
+ <p style="color: #4caf50; margin: 5px 0; font-size: 28px; font-weight: 700;">
1140
+ ${forecast_data['predicted_rewards']:.2f}
1141
+ </p>
1142
+ </div>
1143
+ <div style="background: white; padding: 15px; border-radius: 8px; text-align: center;">
1144
+ <p style="color: #666; margin: 0; font-size: 12px;">OPTIMIZATION POTENTIAL</p>
1145
+ <p style="color: #ff9800; margin: 5px 0; font-size: 28px; font-weight: 700;">
1146
+ +${forecast_data['optimization_potential']:.2f}
1147
+ </p>
1148
+ </div>
1149
+ </div>
1150
+ </div>
1151
 
1152
+ ## ๐Ÿ“Š Category Breakdown
 
 
1153
 
 
1154
  """
1155
 
1156
  for cat in forecast_data['top_categories']:
1157
+ output += f"""
1158
+ ### {cat['emoji']} {cat['category']}
1159
+ - **Predicted Amount:** ${cat['predicted']:.2f} (Confidence: {cat['confidence']*100:.0f}%)
1160
+ - **Recommendation:** Use best card for this category
1161
+ - **Potential Rewards:** Based on optimal card selection
1162
+
1163
+ """
1164
 
1165
+ output += "\n## ๐Ÿ’ก Optimization Strategies\n\n"
1166
+ for i, rec in enumerate(forecast_data['recommendations'], 1):
1167
+ output += f"{i}. {rec}\n"
1168
 
1169
  # Create forecast chart
1170
  fig = go.Figure()
 
1181
  ))
1182
 
1183
  fig.update_layout(
1184
+ title='Predicted Spending by Category (Next Month)',
1185
  xaxis_title='Category',
1186
  yaxis_title='Predicted Amount ($)',
1187
  template='plotly_white',
 
1240
  .metric-card-blue {
1241
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
1242
  }
1243
+
1244
+ /* ===== ANALYTICS TAB - WARNING BOX ===== */
1245
+ .current-month-warning {
1246
+ background: linear-gradient(135deg, #fff4e6 0%, #ffe8cc 100%);
1247
+ border-left: 4px solid #ff9800;
1248
+ padding: 15px 20px;
1249
+ border-radius: 8px;
1250
+ margin: 20px 0;
1251
+ box-shadow: 0 2px 8px rgba(255, 152, 0, 0.2);
1252
+ }
1253
+
1254
+ .current-month-warning h4 {
1255
+ color: #e65100;
1256
+ margin: 0 0 10px 0;
1257
+ font-size: 18px;
1258
+ font-weight: 600;
1259
+ }
1260
+
1261
+ .current-month-warning p {
1262
+ color: #5d4037;
1263
+ margin: 5px 0;
1264
+ font-size: 14px;
1265
+ }
1266
+
1267
+ /* ===== FORECAST TAB - PREDICTION BOX ===== */
1268
+ .forecast-prediction {
1269
+ background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
1270
+ border-left: 4px solid #2196f3;
1271
+ padding: 20px;
1272
+ border-radius: 8px;
1273
+ margin: 20px 0;
1274
+ box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2);
1275
+ }
1276
+
1277
+ .forecast-prediction h3 {
1278
+ color: #0d47a1;
1279
+ margin: 0 0 15px 0;
1280
+ font-size: 22px;
1281
+ font-weight: 700;
1282
+ }
1283
+
1284
+ .forecast-prediction .confidence-badge {
1285
+ display: inline-block;
1286
+ padding: 4px 12px;
1287
+ background: #4caf50;
1288
+ color: white;
1289
+ border-radius: 12px;
1290
+ font-size: 12px;
1291
+ font-weight: 600;
1292
+ margin-left: 10px;
1293
+ }
1294
+
1295
+ /* ===== SECTION DIVIDERS ===== */
1296
+ .section-divider {
1297
+ border: 0;
1298
+ height: 2px;
1299
+ background: linear-gradient(to right, transparent, #ddd, transparent);
1300
+ margin: 30px 0;
1301
+ }
1302
+
1303
+ /* ===== INFO BOXES ===== */
1304
+ .info-box {
1305
+ background: #f5f5f5;
1306
+ border-radius: 8px;
1307
+ padding: 15px;
1308
+ margin: 15px 0;
1309
+ border-left: 3px solid #667eea;
1310
+ }
1311
+
1312
+ .info-box-icon {
1313
+ font-size: 24px;
1314
+ margin-right: 10px;
1315
+ vertical-align: middle;
1316
+ }
1317
+
1318
  table {
1319
  width: 100%;
1320
  border-collapse: collapse;
 
1494
  # ==================== TAB 2: SMART WALLET ====================
1495
  with gr.Tab("๐Ÿ’ณ Smart Wallet"):
1496
  gr.Markdown("## Your Credit Card Portfolio")
 
1497
  wallet_user = gr.Dropdown(
1498
  choices=SAMPLE_USERS,
1499
  value=SAMPLE_USERS[0],
 
1600
  value="*Loading insights...*"
1601
  )
1602
 
1603
+ # ===== CHANGED SECTION: Current Month Summary =====
1604
+ gr.HTML('<hr class="section-divider">')
1605
 
1606
+ gr.Markdown("""
1607
+ <div class="info-box">
1608
+ <span class="info-box-icon">๐Ÿ“Š</span>
1609
+ <strong>Current Month Summary</strong> - Quick insights based on your spending so far this month
1610
+ </div>
1611
+ """)
1612
+
1613
+ current_month_summary = gr.HTML(
1614
+ value="""
1615
+ <div class="current-month-warning">
1616
+ <h4>โš ๏ธ This Month's Insights</h4>
1617
+ <p><em>Loading current month data...</em></p>
1618
+ </div>
1619
+ """,
1620
+ label=None
1621
  )
1622
 
1623
+ # Add clear call-to-action to Forecast tab
1624
+ gr.Markdown("""
1625
+ <div style="text-align: center; margin: 20px 0;">
1626
+ <p style="color: #666; font-size: 14px;">
1627
+ Want to see <strong>next month's predictions</strong> and optimization strategies?
1628
+ </p>
1629
+ <p style="margin-top: 10px;">
1630
+ ๐Ÿ‘‰ <span style="color: #667eea; font-weight: 600; font-size: 16px;">
1631
+ Go to the <strong>Forecast Tab</strong> above โ†’
1632
+ </span>
1633
+ </p>
1634
+ </div>
1635
+ """)
1636
+
1637
  analytics_status = gr.Markdown(
1638
  value="*Select a user to view analytics*",
1639
  elem_classes=["status-text"]
 
1652
  trend_chart,
1653
  spending_table,
1654
  insights_display,
1655
+ current_month_summary, # Changed from forecast_display
1656
  analytics_status
1657
  ]
1658
  )
 
1669
  trend_chart,
1670
  spending_table,
1671
  insights_display,
1672
+ current_month_summary, # Changed from forecast_display
1673
  analytics_status
1674
  ]
1675
  )
 
1686
  trend_chart,
1687
  spending_table,
1688
  insights_display,
1689
+ current_month_summary, # Changed from forecast_display
1690
  analytics_status
1691
  ]
1692
  )
1693
 
1694
  # ==================== TAB 4: FORECAST ====================
1695
  with gr.Tab("๐Ÿ“ˆ Forecast"):
1696
+ # Add clear header with explanation
1697
+ gr.Markdown("""
1698
+ <div class="forecast-prediction">
1699
+ <h3>๐Ÿ”ฎ AI-Powered Spending Forecast</h3>
1700
+ <p style="color: #1565c0; font-size: 16px; margin: 0;">
1701
+ Machine learning predictions for your <strong>next 1-3 months</strong>
1702
+ with personalized optimization strategies
1703
+ </p>
1704
+ </div>
1705
+ """)
1706
 
1707
+ gr.Markdown("""
1708
+ <div class="info-box">
1709
+ <span class="info-box-icon">๐Ÿค–</span>
1710
+ <strong>How it works:</strong> Our AI analyzes your historical spending patterns,
1711
+ seasonal trends, and card benefits to predict future spending and recommend
1712
+ the best cards to maximize your rewards.
1713
+ </div>
1714
+ """)
1715
 
1716
+ with gr.Row():
1717
+ forecast_user = gr.Dropdown(
1718
+ choices=SAMPLE_USERS,
1719
+ value=SAMPLE_USERS[0],
1720
+ label="๐Ÿ‘ค Select User"
1721
+ )
1722
+
1723
+ refresh_forecast_btn = gr.Button(
1724
+ "๐Ÿ”„ Refresh Forecast",
1725
+ variant="primary",
1726
+ size="sm"
1727
+ )
1728
 
1729
+ forecast_output = gr.HTML(value="<p><em>Loading forecast...</em></p>")
1730
  forecast_chart = gr.Plot()
1731
 
1732
  def update_forecast(user_id):