import pandas as pd import gspread import gradio as gr from oauth2client.service_account import ServiceAccountCredentials from datetime import datetime # ------------------ AUTH ------------------ VALID_USERS = { "andrew@lortechnologies.com": "Pass.123", "phonnie@carfind.co.za": "Pass.123", "monique@carfind.co.za": "Pass.123" } # ------------------ GOOGLE SHEET SETUP ------------------ scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"] creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope) client = gspread.authorize(creds) sheet_file = client.open("userAccess") # Ensure this is the exact sheet name in Google Sheets # ------------------ HELPERS ------------------ def load_tab(sheet_name): return pd.DataFrame(sheet_file.worksheet(sheet_name).get_all_records()) # ------------------ FIELD SALES ------------------ def load_field_sales(): df = load_tab("Field Sales") df['Date'] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce') df = df.dropna(subset=["Date"]) df['DateStr'] = df['Date'].dt.date.astype(str) # ✅ Fix: ensure column exists before numeric conversion if "Order Value" not in df.columns: df["Order Value"] = 0 else: df["Order Value"] = pd.to_numeric(df["Order Value"], errors="coerce").fillna(0) return df def generate_summary(date_str): df = load_field_sales() all_reps = sorted(df['Rep'].dropna().unique()) day_df = df[df['DateStr'] == date_str] total_visits = day_df.groupby("Rep").size().reset_index(name="Total Visits") curr_col = "Current/Prospect Custor" if curr_col not in df.columns: df[curr_col] = "" current = day_df[day_df[curr_col] == "Current"] prospect = day_df[day_df[curr_col] == "Prospect"] breakdown = pd.DataFrame({ "Rep": all_reps, "Current": [len(current[current["Rep"] == rep]) for rep in all_reps], "Prospect": [len(prospect[prospect["Rep"] == rep]) for rep in all_reps] }) active_list = total_visits['Rep'].tolist() inactive_list = [rep for rep in all_reps if rep not in active_list] inactive_df = pd.DataFrame({'Inactive Reps': inactive_list}) return total_visits, breakdown, inactive_df def get_order_summary(date_str): df = load_field_sales() day_df = df[df['DateStr'] == date_str] if "Order Received" not in df.columns: df["Order Received"] = "" rep_group = day_df.groupby("Rep").agg({ "Order Received": lambda x: (x == "Yes").sum(), "Order Value": "sum" }).reset_index().rename(columns={ "Order Received": "Orders Received", "Order Value": "Total Order Value" }) return rep_group.sort_values(by="Total Order Value", ascending=False) def get_escalations(): df = load_field_sales() col = "Customer Type & Status" if col in df.columns: flagged = df[df[col].str.contains("Second", na=False)] return flagged if not flagged.empty else pd.DataFrame([["No second-hand dealerships flagged."]], columns=["Status"]) else: return pd.DataFrame([["⚠️ Column 'Customer Type & Status' not found."]], columns=["Status"]) # ------------------ TELESALeS ------------------ def get_telesales_summary(): df = load_tab("TeleSales") df["Date"] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce') df["DateStr"] = df["Date"].dt.date.astype(str) return df.groupby(["Rep Email", "DateStr"]).size().reset_index(name="Calls Made") # ------------------ OEM VISITS ------------------ def get_oem_summary(): df = load_tab("OEM Visit") df["Date"] = pd.to_datetime(df.get("Date", datetime.today()), errors='coerce') df["DateStr"] = df["Date"].dt.date.astype(str) return df.groupby(["Rep", "DateStr"]).size().reset_index(name="OEM Visits") # ------------------ CUSTOMER REQUESTS ------------------ def get_requests(): return load_tab("Customer Requests") # ------------------ CUSTOMER LISTINGS ------------------ def get_listings(): return load_tab("CustomerListings") # ------------------ USERS ------------------ def get_users(): return load_tab("Users") # ------------------ GRADIO APP ------------------ with gr.Blocks() as app: with gr.Row(): with gr.Column(visible=True) as login_ui: gr.Markdown("## 🔐 Login Required") email = gr.Textbox(label="Email") password = gr.Textbox(label="Password", type="password") login_btn = gr.Button("Login") login_msg = gr.Markdown("") with gr.Column(visible=False) as main_ui: gr.Markdown("## 🗂️ CarMat Dashboard") df_initial = load_field_sales() unique_dates = sorted(df_initial["DateStr"].unique(), reverse=True) # --- Summary Tab --- with gr.Tab("📊 Summary"): date_summary = gr.Dropdown(label="Select Date", choices=unique_dates) visits = gr.Dataframe(label="✅ Total Visits") breakdown = gr.Dataframe(label="🏷️ Current vs. Prospect") inactive = gr.Dataframe(label="⚠️ Inactive Reps") date_summary.change(fn=generate_summary, inputs=date_summary, outputs=[visits, breakdown, inactive]) # --- Orders Tab --- with gr.Tab("📦 Orders"): order_date = gr.Dropdown(label="Select Date", choices=unique_dates) order_table = gr.Dataframe(label="💰 Orders Summary") order_date.change(fn=get_order_summary, inputs=order_date, outputs=order_table) # --- Escalations --- with gr.Tab("🚨 Escalations"): esc_table = gr.Dataframe(value=get_escalations, label="Second-hand Dealerships") esc_btn = gr.Button("🔄 Refresh") esc_btn.click(fn=get_escalations, outputs=esc_table) # --- TeleSales --- with gr.Tab("📞 TeleSales"): ts_table = gr.Dataframe(value=get_telesales_summary, label="TeleSales Summary") ts_refresh = gr.Button("🔄 Refresh TeleSales") ts_refresh.click(fn=get_telesales_summary, outputs=ts_table) # --- OEM Visits --- with gr.Tab("🏭 OEM Visits"): oem_table = gr.Dataframe(value=get_oem_summary, label="OEM Visit Summary") oem_refresh = gr.Button("🔄 Refresh OEM") oem_refresh.click(fn=get_oem_summary, outputs=oem_table) # --- Requests --- with gr.Tab("📬 Customer Requests"): req_table = gr.Dataframe(value=get_requests, label="Customer Requests", interactive=False) req_refresh = gr.Button("🔄 Refresh Requests") req_refresh.click(fn=get_requests, outputs=req_table) # --- Dealerships --- with gr.Tab("📋 Dealership Directory"): listings_table = gr.Dataframe(value=get_listings, label="Customer Listings") listings_refresh = gr.Button("🔄 Refresh Listings") listings_refresh.click(fn=get_listings, outputs=listings_table) # --- Users --- with gr.Tab("👤 Users"): users_table = gr.Dataframe(value=get_users, label="Users") users_refresh = gr.Button("🔄 Refresh Users") users_refresh.click(fn=get_users, outputs=users_table) def do_login(user, pw): if VALID_USERS.get(user) == pw: return gr.update(visible=False), gr.update(visible=True), "" else: return gr.update(visible=True), gr.update(visible=False), "❌ Invalid login." login_btn.click(fn=do_login, inputs=[email, password], outputs=[login_ui, main_ui, login_msg]) app.launch()