Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -18,7 +18,7 @@ client = gspread.authorize(creds)
|
|
| 18 |
|
| 19 |
SHEET_URL = "https://docs.google.com/spreadsheets/d/1if4KoVQvw5ZbhknfdZbzMkcTiPfsD6bz9V3a1th-bwQ"
|
| 20 |
|
| 21 |
-
# --------------------
|
| 22 |
SHEET_MAP = {
|
| 23 |
"Alice": "https://docs.google.com/spreadsheets/d/18qFpkbE2CwVOgiB6Xz4m2Ep7ZA29p5xV",
|
| 24 |
"Bob": "https://docs.google.com/spreadsheets/d/1EKngyAvq_3hzQMAOVame2nO9LKPJEV0d",
|
|
@@ -26,13 +26,11 @@ SHEET_MAP = {
|
|
| 26 |
"Dave": "https://docs.google.com/spreadsheets/d/1m5e6YXxjK62vtBxYGkJSyHpHT7lnirg6"
|
| 27 |
}
|
| 28 |
|
| 29 |
-
def
|
| 30 |
if rep_name not in SHEET_MAP:
|
| 31 |
return pd.DataFrame([{"Error": f"No sheet available for '{rep_name}'"}])
|
| 32 |
-
|
| 33 |
-
sheet_url = SHEET_MAP[rep_name]
|
| 34 |
try:
|
| 35 |
-
sheet = client.open_by_url(
|
| 36 |
worksheet = sheet.get_worksheet(0)
|
| 37 |
data = worksheet.get_all_values()
|
| 38 |
if not data:
|
|
@@ -55,10 +53,7 @@ def load_sheet_df(name):
|
|
| 55 |
header = []
|
| 56 |
for col in raw_header:
|
| 57 |
counts[col] += 1
|
| 58 |
-
if counts[col] > 1
|
| 59 |
-
header.append(f"{col}_{counts[col]}")
|
| 60 |
-
else:
|
| 61 |
-
header.append(col)
|
| 62 |
header = normalize_columns(header)
|
| 63 |
return pd.DataFrame(rows, columns=header)
|
| 64 |
|
|
@@ -116,8 +111,7 @@ def search_appointments(y, m, d, rep=None):
|
|
| 116 |
|
| 117 |
# -------------------- LEADS --------------------
|
| 118 |
def get_leads_detail():
|
| 119 |
-
|
| 120 |
-
return df
|
| 121 |
|
| 122 |
def get_leads_summary():
|
| 123 |
df = get_leads_detail()
|
|
@@ -137,12 +131,11 @@ def compute_insights():
|
|
| 137 |
return vc.idxmax() if not vc.empty else "N/A"
|
| 138 |
return "N/A"
|
| 139 |
|
| 140 |
-
|
| 141 |
{"Metric": "Most Calls This Week", "Rep": top(calls, "Rep")},
|
| 142 |
{"Metric": "Most Appointments This Week", "Rep": top(appts, "Rep")},
|
| 143 |
{"Metric": "Most Leads Allocated", "Rep": top(leads, "Assigned Rep")},
|
| 144 |
-
]
|
| 145 |
-
return pd.DataFrame(data)
|
| 146 |
|
| 147 |
# -------------------- USER MANAGEMENT --------------------
|
| 148 |
def load_users():
|
|
@@ -166,221 +159,43 @@ def save_users(df):
|
|
| 166 |
set_with_dataframe(ws, df)
|
| 167 |
return "✅ Users saved!"
|
| 168 |
|
| 169 |
-
# --------------------
|
| 170 |
-
def get_quotes_df():
|
| 171 |
-
df = load_sheet_df("LiveQuotes")
|
| 172 |
-
df.columns = [c.strip() for c in df.columns]
|
| 173 |
-
return df
|
| 174 |
-
|
| 175 |
-
def rep_choices_quotes():
|
| 176 |
-
df = get_quotes_df()
|
| 177 |
-
return sorted(df["Rep"].dropna().unique().tolist()) if "Rep" in df else []
|
| 178 |
-
|
| 179 |
-
def quote_year_choices():
|
| 180 |
-
df = get_quotes_df()
|
| 181 |
-
if "Year" in df.columns:
|
| 182 |
-
years = sorted(df["Year"].dropna().unique().astype(str))
|
| 183 |
-
return years
|
| 184 |
-
if "Date" in df.columns:
|
| 185 |
-
years = pd.to_datetime(df["Date"], errors="coerce").dt.year.dropna().unique()
|
| 186 |
-
return sorted(years.astype(str))
|
| 187 |
-
return []
|
| 188 |
-
|
| 189 |
-
def quote_month_choices(year=None):
|
| 190 |
-
"""
|
| 191 |
-
Returns a sorted list of valid month strings for a given year, always at least [''].
|
| 192 |
-
Also prints debug output to help troubleshoot.
|
| 193 |
-
"""
|
| 194 |
-
df = get_quotes_df()
|
| 195 |
-
if (
|
| 196 |
-
year
|
| 197 |
-
and "Year" in df.columns
|
| 198 |
-
and "Month" in df.columns
|
| 199 |
-
and not df.empty
|
| 200 |
-
):
|
| 201 |
-
subset = df[df["Year"].astype(str) == str(year)]
|
| 202 |
-
if subset.empty:
|
| 203 |
-
print(f"[DEBUG] No quotes found for year {year}. Returning [''].")
|
| 204 |
-
return [""]
|
| 205 |
-
try:
|
| 206 |
-
months = pd.to_numeric(subset["Month"], errors="coerce").dropna().astype(int)
|
| 207 |
-
months = [str(m) for m in months if 1 <= m <= 12]
|
| 208 |
-
months = sorted(set(months))
|
| 209 |
-
result = [""] + months if months else [""]
|
| 210 |
-
print(f"[DEBUG] Year {year}: Months dropdown = {result}")
|
| 211 |
-
return result
|
| 212 |
-
except Exception as e:
|
| 213 |
-
print(f"[DEBUG] Exception in quote_month_choices for year {year}: {e}")
|
| 214 |
-
return [""]
|
| 215 |
-
print(f"[DEBUG] No valid year or columns missing. Returning [''].")
|
| 216 |
-
return [""]
|
| 217 |
-
|
| 218 |
-
def quotes_summary(year=None, month=None):
|
| 219 |
-
df = get_quotes_df()
|
| 220 |
-
if "Rep" not in df.columns or "Total" not in df.columns:
|
| 221 |
-
return pd.DataFrame([{"Error": "Missing Rep or Total column"}])
|
| 222 |
-
if year and "Year" in df.columns:
|
| 223 |
-
df = df[df["Year"].astype(str) == str(year)]
|
| 224 |
-
if month and "Month" in df.columns:
|
| 225 |
-
df = df[df["Month"].astype(str) == str(month)]
|
| 226 |
-
df["Total"] = pd.to_numeric(df["Total"].astype(str).str.replace(",", ""), errors="coerce")
|
| 227 |
-
summary = (
|
| 228 |
-
df.groupby("Rep")
|
| 229 |
-
.agg({"Document No.": "count", "Total": "sum"})
|
| 230 |
-
.rename(columns={"Document No.": "Total Quotes", "Total": "Total Value"})
|
| 231 |
-
.reset_index()
|
| 232 |
-
)
|
| 233 |
-
summary["Total Value"] = summary["Total Value"].fillna(0).round(2)
|
| 234 |
-
return summary
|
| 235 |
-
|
| 236 |
-
def get_rep_quotes_filtered(rep, year=None, month=None):
|
| 237 |
-
df = get_quotes_df()
|
| 238 |
-
if "Rep" not in df.columns:
|
| 239 |
-
return pd.DataFrame([{"Error": "Missing Rep column"}])
|
| 240 |
-
df = df[df["Rep"] == rep]
|
| 241 |
-
if year and "Year" in df.columns:
|
| 242 |
-
df = df[df["Year"].astype(str) == str(year)]
|
| 243 |
-
if month and "Month" in df.columns:
|
| 244 |
-
df = df[df["Month"].astype(str) == str(month)]
|
| 245 |
-
return df
|
| 246 |
-
|
| 247 |
-
def update_month_choices_summary(year):
|
| 248 |
-
months = quote_month_choices(year)
|
| 249 |
-
print(f"[DEBUG] update_month_choices_summary({year}) -> {months}")
|
| 250 |
-
return gr.Dropdown.update(choices=months, value="")
|
| 251 |
-
|
| 252 |
-
def update_month_choices(year):
|
| 253 |
-
months = quote_month_choices(year)
|
| 254 |
-
print(f"[DEBUG] update_month_choices({year}) -> {months}")
|
| 255 |
-
return gr.Dropdown.update(choices=months, value="")
|
| 256 |
-
|
| 257 |
-
# -------------------- UI LAYOUT --------------------
|
| 258 |
with gr.Blocks(title="Graffiti Admin Dashboard") as app:
|
| 259 |
-
gr.Markdown("#
|
| 260 |
|
| 261 |
-
# -- Calls Tab --
|
| 262 |
with gr.Tab("Calls Report"):
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
|
| 268 |
-
gr.Markdown("### Search Calls by Date")
|
| 269 |
-
y1, m1, d1 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
|
| 270 |
-
rep_c2 = gr.Dropdown(choices=rep_choices("Calls"), label="Filter by Rep", allow_custom_value=True)
|
| 271 |
-
btn_c2 = gr.Button("Search")
|
| 272 |
-
tbl_c2 = gr.Dataframe()
|
| 273 |
-
btn_c2.click(search_calls, [y1, m1, d1, rep_c2], tbl_c2)
|
| 274 |
-
|
| 275 |
-
# -- Appointments Tab --
|
| 276 |
with gr.Tab("Appointments Report"):
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
def _load_appts(r):
|
| 282 |
-
df = get_appointments(r)
|
| 283 |
-
return df.groupby("Rep").size().reset_index(name="Count"), df
|
| 284 |
-
btn_a.click(_load_appts, rep_a, [sum_a, tbl_a])
|
| 285 |
-
|
| 286 |
-
gr.Markdown("### Search Appts by Date")
|
| 287 |
-
y2, m2, d2 = gr.Textbox(label="Year"), gr.Textbox(label="Month"), gr.Textbox(label="Day")
|
| 288 |
-
rep_a2 = gr.Dropdown(choices=rep_choices("Appointments"), label="Filter by Rep", allow_custom_value=True)
|
| 289 |
-
btn_a2 = gr.Button("Search")
|
| 290 |
-
sum_a2 = gr.Dataframe(label="📊 Appts by Rep")
|
| 291 |
-
tbl_a2 = gr.Dataframe()
|
| 292 |
-
def _search_appts(y,m,d,r):
|
| 293 |
-
df = search_appointments(y,m,d,r)
|
| 294 |
-
return df.groupby("Rep").size().reset_index(name="Count"), df
|
| 295 |
-
btn_a2.click(_search_appts, [y2,m2,d2,rep_a2], [sum_a2, tbl_a2])
|
| 296 |
|
| 297 |
-
# -- Appointed Leads --
|
| 298 |
with gr.Tab("Allocated Leads"):
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
# -- Quotes Tab (NEW) --
|
| 305 |
-
with gr.Tab("Quotes"):
|
| 306 |
-
gr.Markdown("### 📈 Quotes Summary by Rep")
|
| 307 |
-
year_qs = gr.Dropdown(choices=[""] + quote_year_choices(), label="Year (optional)", value="")
|
| 308 |
-
month_qs = gr.Dropdown(choices=[""], label="Month (optional, needs year)", value="")
|
| 309 |
-
btn_qs = gr.Button("Show Quotes Summary")
|
| 310 |
-
sum_qs = gr.Dataframe(label="Summary by Rep")
|
| 311 |
-
|
| 312 |
-
# Dynamic month options for summary
|
| 313 |
-
year_qs.change(update_month_choices_summary, year_qs, month_qs)
|
| 314 |
-
|
| 315 |
-
def quotes_summary_wrapper(year, month):
|
| 316 |
-
return quotes_summary(year if year else None, month if month else None)
|
| 317 |
-
btn_qs.click(quotes_summary_wrapper, [year_qs, month_qs], sum_qs)
|
| 318 |
|
| 319 |
-
gr.Markdown("### 🔎 View All Quotes for a Rep, Year, and Month")
|
| 320 |
-
rep_q = gr.Dropdown(choices=rep_choices_quotes(), label="Select Rep")
|
| 321 |
-
year_q = gr.Dropdown(choices=[""] + quote_year_choices(), label="Year (optional)", value="")
|
| 322 |
-
month_q = gr.Dropdown(choices=[""], label="Month (optional, needs year)", value="")
|
| 323 |
-
btn_qr = gr.Button("Show Quotes")
|
| 324 |
-
tbl_qr = gr.Dataframe(label="Quotes for Selection")
|
| 325 |
-
|
| 326 |
-
# Dynamic month options for rep quotes
|
| 327 |
-
year_q.change(update_month_choices, year_q, month_q)
|
| 328 |
-
|
| 329 |
-
def get_rep_quotes_filtered_wrapper(rep, year, month):
|
| 330 |
-
return get_rep_quotes_filtered(rep, year if year else None, month if month else None)
|
| 331 |
-
btn_qr.click(get_rep_quotes_filtered_wrapper, [rep_q, year_q, month_q], tbl_qr)
|
| 332 |
-
|
| 333 |
-
# -- Insights --
|
| 334 |
with gr.Tab("Insights"):
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
|
| 339 |
-
# -- User Management --
|
| 340 |
with gr.Tab("User Management"):
|
| 341 |
-
gr.Markdown("## 👤 Manage Users\nEdit the grid below then click **Save Users** to push back to the sheet.")
|
| 342 |
users_tbl = gr.Dataframe(value=load_users(), interactive=True)
|
| 343 |
save_btn = gr.Button("Save Users")
|
| 344 |
-
|
| 345 |
-
save_btn.click(save_users, users_tbl,
|
| 346 |
-
# -------------------- Self Sourced Leads --------------------
|
| 347 |
-
SHEET_MAP = {
|
| 348 |
-
"Alice": "https://docs.google.com/spreadsheets/d/18qFpkbE2CwVOgiB6Xz4m2Ep7ZA29p5xV",
|
| 349 |
-
"Bob": "https://docs.google.com/spreadsheets/d/1EKngyAvq_3hzQMAOVame2nO9LKPJEV0d",
|
| 350 |
-
"Charlie": "https://docs.google.com/spreadsheets/d/164OTu1keBC12-5XFUDXMmLOPMkdAjBOM",
|
| 351 |
-
"Dave": "https://docs.google.com/spreadsheets/d/1m5e6YXxjK62vtBxYGkJSyHpHT7lnirg6"
|
| 352 |
-
}
|
| 353 |
-
|
| 354 |
-
def load_leads_data(rep_name):
|
| 355 |
-
if rep_name not in SHEET_MAP:
|
| 356 |
-
return pd.DataFrame([{"Error": f"No sheet available for '{rep_name}'"}])
|
| 357 |
-
|
| 358 |
-
sheet_url = SHEET_MAP[rep_name]
|
| 359 |
-
try:
|
| 360 |
-
sheet = client.open_by_url(sheet_url)
|
| 361 |
-
worksheet = sheet.get_worksheet(0)
|
| 362 |
-
data = worksheet.get_all_values()
|
| 363 |
-
if not data:
|
| 364 |
-
return pd.DataFrame([{"Info": "No data available"}])
|
| 365 |
-
return pd.DataFrame(data[1:], columns=data[0])
|
| 366 |
-
except Exception as e:
|
| 367 |
-
return pd.DataFrame([{"Error": str(e)}])
|
| 368 |
-
# -- Self Sourced Leads Tab (New) --
|
| 369 |
-
with gr.Tab("Self Sourced Leads"):
|
| 370 |
-
gr.Markdown("## 🔍 Self Sourced Leads by Rep")
|
| 371 |
-
|
| 372 |
-
rep_leads = gr.Dropdown(
|
| 373 |
-
label="Select Rep",
|
| 374 |
-
choices=list(SHEET_MAP.keys()),
|
| 375 |
-
allow_custom_value=True
|
| 376 |
-
)
|
| 377 |
-
btn_load_leads = gr.Button("Load Leads")
|
| 378 |
-
tbl_leads = gr.Dataframe(label="Leads Data")
|
| 379 |
-
|
| 380 |
-
btn_load_leads.click(load_leads_data, rep_leads, tbl_leads)
|
| 381 |
-
|
| 382 |
-
app.launch()
|
| 383 |
|
| 384 |
-
|
| 385 |
-
|
|
|
|
|
|
|
|
|
|
| 386 |
|
|
|
|
|
|
| 18 |
|
| 19 |
SHEET_URL = "https://docs.google.com/spreadsheets/d/1if4KoVQvw5ZbhknfdZbzMkcTiPfsD6bz9V3a1th-bwQ"
|
| 20 |
|
| 21 |
+
# -------------------- SELF SOURCED LEADS CONFIG --------------------
|
| 22 |
SHEET_MAP = {
|
| 23 |
"Alice": "https://docs.google.com/spreadsheets/d/18qFpkbE2CwVOgiB6Xz4m2Ep7ZA29p5xV",
|
| 24 |
"Bob": "https://docs.google.com/spreadsheets/d/1EKngyAvq_3hzQMAOVame2nO9LKPJEV0d",
|
|
|
|
| 26 |
"Dave": "https://docs.google.com/spreadsheets/d/1m5e6YXxjK62vtBxYGkJSyHpHT7lnirg6"
|
| 27 |
}
|
| 28 |
|
| 29 |
+
def load_self_sourced_leads(rep_name):
|
| 30 |
if rep_name not in SHEET_MAP:
|
| 31 |
return pd.DataFrame([{"Error": f"No sheet available for '{rep_name}'"}])
|
|
|
|
|
|
|
| 32 |
try:
|
| 33 |
+
sheet = client.open_by_url(SHEET_MAP[rep_name])
|
| 34 |
worksheet = sheet.get_worksheet(0)
|
| 35 |
data = worksheet.get_all_values()
|
| 36 |
if not data:
|
|
|
|
| 53 |
header = []
|
| 54 |
for col in raw_header:
|
| 55 |
counts[col] += 1
|
| 56 |
+
header.append(f"{col}_{counts[col]}" if counts[col] > 1 else col)
|
|
|
|
|
|
|
|
|
|
| 57 |
header = normalize_columns(header)
|
| 58 |
return pd.DataFrame(rows, columns=header)
|
| 59 |
|
|
|
|
| 111 |
|
| 112 |
# -------------------- LEADS --------------------
|
| 113 |
def get_leads_detail():
|
| 114 |
+
return load_sheet_df("AllocatedLeads")
|
|
|
|
| 115 |
|
| 116 |
def get_leads_summary():
|
| 117 |
df = get_leads_detail()
|
|
|
|
| 131 |
return vc.idxmax() if not vc.empty else "N/A"
|
| 132 |
return "N/A"
|
| 133 |
|
| 134 |
+
return pd.DataFrame([
|
| 135 |
{"Metric": "Most Calls This Week", "Rep": top(calls, "Rep")},
|
| 136 |
{"Metric": "Most Appointments This Week", "Rep": top(appts, "Rep")},
|
| 137 |
{"Metric": "Most Leads Allocated", "Rep": top(leads, "Assigned Rep")},
|
| 138 |
+
])
|
|
|
|
| 139 |
|
| 140 |
# -------------------- USER MANAGEMENT --------------------
|
| 141 |
def load_users():
|
|
|
|
| 159 |
set_with_dataframe(ws, df)
|
| 160 |
return "✅ Users saved!"
|
| 161 |
|
| 162 |
+
# -------------------- GRADIO APP --------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
with gr.Blocks(title="Graffiti Admin Dashboard") as app:
|
| 164 |
+
gr.Markdown("# 📊 Graffiti Admin Dashboard")
|
| 165 |
|
|
|
|
| 166 |
with gr.Tab("Calls Report"):
|
| 167 |
+
rep = gr.Dropdown(choices=rep_choices("Calls"), label="Rep")
|
| 168 |
+
btn = gr.Button("Load This Week")
|
| 169 |
+
out = gr.Dataframe()
|
| 170 |
+
btn.click(get_calls, rep, out)
|
| 171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
with gr.Tab("Appointments Report"):
|
| 173 |
+
rep2 = gr.Dropdown(choices=rep_choices("Appointments"), label="Rep")
|
| 174 |
+
btn2 = gr.Button("Load This Week")
|
| 175 |
+
out2 = gr.Dataframe()
|
| 176 |
+
btn2.click(get_appointments, rep2, out2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
|
|
|
|
| 178 |
with gr.Tab("Allocated Leads"):
|
| 179 |
+
btn3 = gr.Button("Show Leads")
|
| 180 |
+
summary = gr.Dataframe()
|
| 181 |
+
details = gr.Dataframe()
|
| 182 |
+
btn3.click(lambda: (get_leads_summary(), get_leads_detail()), None, [summary, details])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
with gr.Tab("Insights"):
|
| 185 |
+
btn4 = gr.Button("Generate Insights")
|
| 186 |
+
out4 = gr.Dataframe()
|
| 187 |
+
btn4.click(compute_insights, None, out4)
|
| 188 |
|
|
|
|
| 189 |
with gr.Tab("User Management"):
|
|
|
|
| 190 |
users_tbl = gr.Dataframe(value=load_users(), interactive=True)
|
| 191 |
save_btn = gr.Button("Save Users")
|
| 192 |
+
save_msg = gr.Textbox()
|
| 193 |
+
save_btn.click(save_users, users_tbl, save_msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
+
with gr.Tab("Self Sourced Leads"):
|
| 196 |
+
rep_s = gr.Dropdown(choices=list(SHEET_MAP.keys()), label="Rep")
|
| 197 |
+
btn_s = gr.Button("Load Leads")
|
| 198 |
+
tbl_s = gr.Dataframe()
|
| 199 |
+
btn_s.click(load_self_sourced_leads, rep_s, tbl_s)
|
| 200 |
|
| 201 |
+
app.launch()
|