FinSync-AI / app.py
JARVISXIRONMAN's picture
Upload app.py
a4e89b7 verified
import streamlit as st
import requests
import pandas as pd
import plotly.express as px
from datetime import datetime
# ---------- BACKEND & HIDDEN USER ----------
BACKEND_URL = "http://127.0.0.1:8000"
# backend requires user_id; keep it hidden (not editable in UI)
USER_ID = "user1"
# ---------- PAGE CONFIG ----------
st.set_page_config(page_title="FinSync AI", layout="wide", initial_sidebar_state="expanded")
# ---------- THEME COLORS ----------
PRIMARY_COLOR = "#6d28d9" # purple
ACCENT_COLOR = "#10b981" # green
BG_COLOR = "#f5f5f5" # app background
CARD_COLOR = "#ffffff" # cards / sidebar
TEXT_COLOR = "#111827" # main text
# ---------- SIDEBAR (TITLE + NAV) ----------
st.sidebar.markdown(
f"""
<div style="text-align:center; padding-top:6px; padding-bottom:6px;">
<h2 style="color:{PRIMARY_COLOR}; margin:0 0 6px 0;">πŸ’° FinSync AI</h2>
<div style="color:{TEXT_COLOR}; font-size:13px; margin-bottom:6px;">
Your AI that tracks, budgets & reminds you automatically.
</div>
</div>
<hr style="border:1px solid {PRIMARY_COLOR}; margin-bottom:8px;">
""",
unsafe_allow_html=True,
)
menu = ["Dashboard", "Expenses", "Budget", "Reminders", "AI Chat"]
choice = st.sidebar.radio("Navigation", menu)
st.sidebar.markdown(
f"<hr style='border:1px solid {PRIMARY_COLOR};'>"
f"<p style='color:{TEXT_COLOR}; font-size:12px; text-align:center; margin-top:8px;'>Hackathon Demo | Powered by Groq LLM</p>",
unsafe_allow_html=True,
)
# ---------- CRITICAL CSS FIXES ----------
# This CSS uses stable selectors (html, body, data-testid, header, section[data-testid="stSidebar"])
# to ensure background/text color consistency and avoid the "black top / black sidebar" issues.
st.markdown(
f"""
<style>
/* Root backgrounds (fix black areas and collapsed sidebar top bar) */
html, body, [data-testid="stAppViewContainer"], .block-container, header {{
background-color: {BG_COLOR} !important;
color: {TEXT_COLOR} !important;
}}
/* Sidebar box */
section[data-testid="stSidebar"] {{
background-color: {CARD_COLOR} !important;
color: {TEXT_COLOR} !important;
padding-top: 12px;
}}
/* Toolbar (top-right icons area) */
[data-testid="stToolbar"] {{
background-color: {BG_COLOR} !important;
}}
/* Inputs / textareas / selects - readable */
input, textarea, select, .stTextInput>div>div>input, .stTextArea>div>div>textarea {{
background-color: {CARD_COLOR} !important;
color: {TEXT_COLOR} !important;
border: 1px solid #e6e6e6 !important;
border-radius: 6px !important;
padding: 6px !important;
}}
/* Selectbox inner text */
.stSelectbox>div>div>div>span, .stSelectbox>div>div>div {{
color: {TEXT_COLOR} !important;
}}
/* Buttons */
.stButton>button {{
background-color: {PRIMARY_COLOR} !important;
color: white !important;
border-radius: 8px !important;
padding: 6px 14px !important;
font-weight: 600 !important;
}}
.stButton>button:hover {{
background-color: #5b21b6 !important;
}}
/* Dataframes and tables */
.stDataFrame, .stTable, .element-container .stDataFrame {{
background-color: {CARD_COLOR} !important;
color: {TEXT_COLOR} !important;
border-radius:8px !important;
padding:6px !important;
}}
/* Metrics */
[data-testid="stMetricValue"], [data-testid="stMetricLabel"] {{
color: {TEXT_COLOR} !important;
}}
/* Headings / markdown */
.css-1v0mbdj h1, .css-1v0mbdj h2, .css-1v0mbdj h3, h1, h2, h3 {{
color: {TEXT_COLOR} !important;
}}
/* Small responsive tweak: ensure block-container uses the app background */
.block-container {{
background-color: {BG_COLOR} !important;
}}
</style>
""",
unsafe_allow_html=True,
)
# ---------- HELPERS: API CALLS (include user_id param) ----------
def get_expenses():
try:
r = requests.get(f"{BACKEND_URL}/expenses", params={"user_id": USER_ID}, timeout=5)
if r.ok:
return r.json().get("expenses", [])
except Exception:
pass
return []
def add_expense(payload):
try:
payload["user_id"] = USER_ID
r = requests.post(f"{BACKEND_URL}/add_expense", json=payload, timeout=5)
return r.ok
except Exception:
return False
def get_budget():
try:
r = requests.get(f"{BACKEND_URL}/budget", params={"user_id": USER_ID}, timeout=6)
if r.ok:
return r.json().get("budget", {})
except Exception:
pass
return {}
def get_reminders():
try:
r = requests.get(f"{BACKEND_URL}/reminders", params={"user_id": USER_ID}, timeout=5)
if r.ok:
return r.json().get("reminders", [])
except Exception:
pass
return []
def add_reminder(payload):
try:
payload["user_id"] = USER_ID
r = requests.post(f"{BACKEND_URL}/reminder", json=payload, timeout=5)
return r.ok
except Exception:
return False
def analyze_text(payload):
try:
payload["user_id"] = USER_ID
r = requests.post(f"{BACKEND_URL}/analyze", json=payload, timeout=10)
if r.ok:
return r.json().get("result", {})
except Exception:
pass
return {"intent":"error","message":"AI request failed or backend unreachable."}
# ---------- UI: DASHBOARD ----------
if choice == "Dashboard":
st.header("πŸ“Š Dashboard Overview")
expenses = get_expenses()
if expenses:
df = pd.DataFrame(expenses)
# normalize types and dates
df["amount"] = pd.to_numeric(df["amount"], errors="coerce").fillna(0.0)
df["date"] = pd.to_datetime(df["date"], errors="coerce")
total = df["amount"].sum()
left, right = st.columns([1, 2], gap="large")
with left:
st.metric("πŸ’΅ Total Expenses", f"${total:.2f}")
# show top categories
cat = df.groupby("category")["amount"].sum().sort_values(ascending=False).reset_index()
if not cat.empty:
st.write("Top categories")
st.dataframe(cat.head(5), use_container_width=True)
with right:
fig = px.pie(cat, names="category", values="amount", title="Spending Breakdown",
color_discrete_sequence=px.colors.sequential.Purples)
st.plotly_chart(fig, use_container_width=True)
st.subheader("Recent Transactions")
st.dataframe(df.sort_values("date", ascending=False).head(10), use_container_width=True)
else:
st.info("No expenses recorded yet. Add one from the Expenses menu.")
# ---------- UI: EXPENSES ----------
elif choice == "Expenses":
st.header("πŸ’Έ Add Expense")
with st.form("expense_form", clear_on_submit=True):
name = st.text_input("Name", placeholder="e.g., Rent, Internet, Groceries")
amount = st.number_input("Amount (USD)", min_value=0.0, format="%.2f")
category = st.selectbox("Category", ["Rent", "Food", "Utilities", "Subscription", "Shopping", "Misc"])
date = st.date_input("Date", value=datetime.today())
submitted = st.form_submit_button("Add Expense")
if submitted:
ok = add_expense({
"name": name,
"amount": float(amount),
"category": category,
"date": date.isoformat()
})
if ok:
st.success("Expense added.")
else:
st.error("Failed to add expense. Check backend.")
st.subheader("All Expenses")
expenses = get_expenses()
if expenses:
df = pd.DataFrame(expenses)
df["date"] = pd.to_datetime(df["date"], errors="coerce")
st.dataframe(df.sort_values("date", ascending=False), use_container_width=True)
else:
st.info("No expenses yet.")
# ---------- UI: BUDGET ----------
elif choice == "Budget":
st.header("πŸ“ˆ AI Budget Recommendation")
st.markdown("FinSync AI suggests an allocation based on your expenses.")
budget = get_budget()
if budget:
left, right = st.columns([1, 2], gap="large")
with left:
st.metric("πŸ›’ Spending", f"{budget.get('Spending',0)*100:.0f}%")
st.metric("πŸ’° Savings", f"{budget.get('Savings',0)*100:.0f}%")
st.metric("πŸ“ˆ Investments", f"{budget.get('Investments',0)*100:.0f}%")
with right:
fig = px.pie(
names=["Spending", "Savings", "Investments"],
values=[budget.get("Spending",0), budget.get("Savings",0), budget.get("Investments",0)],
color_discrete_sequence=px.colors.qualitative.Set3
)
st.plotly_chart(fig, use_container_width=True)
if budget.get("message"):
st.info(budget.get("message"))
else:
st.info("Budget data not available or backend unreachable.")
# ---------- UI: REMINDERS ----------
elif choice == "Reminders":
st.header("⏰ Reminders")
with st.form("reminder_form", clear_on_submit=True):
name = st.text_input("Reminder name", placeholder="e.g., Pay Rent")
date = st.date_input("Due date", value=datetime.today())
amount = st.number_input("Amount (optional)", min_value=0.0, format="%.2f")
notes = st.text_area("Notes (optional)")
submitted = st.form_submit_button("Add Reminder")
if submitted:
ok = add_reminder({
"name": name,
"date": date.isoformat(),
"amount": float(amount),
"notes": notes
})
if ok:
st.success("Reminder added.")
else:
st.error("Failed to add reminder. Check backend.")
st.subheader("Your reminders")
reminders = get_reminders()
if reminders:
df = pd.DataFrame(reminders)
df["date"] = pd.to_datetime(df["date"], errors="coerce")
st.dataframe(df.sort_values("date"), use_container_width=True)
else:
st.info("No reminders found.")
# ---------- UI: AI CHAT ----------
elif choice == "AI Chat":
st.header("πŸ€– AI Chat")
st.markdown("Ask FinSync AI to add expenses, show budget, or set reminders. Example: \"Add expense Rent 500\"")
prompt = st.text_input("Enter command")
if st.button("Send"):
if not prompt or prompt.strip() == "":
st.warning("Please enter a command.")
else:
res = analyze_text({"text": prompt})
st.success(res.get("message", "No response"))
# ---------- END ----------