taotanapol commited on
Commit
799be66
·
verified ·
1 Parent(s): 31d39f4

Add filter mechanic to Inventory tab

Browse files
Files changed (1) hide show
  1. streamlit_app.py +62 -28
streamlit_app.py CHANGED
@@ -127,6 +127,8 @@ LANG = {
127
  "inv_kpi_value_used": "Total Value Used",
128
  "inv_kpi_value_per_cust": "Value Used / Customer",
129
  "inv_chart_vpc_trend": "Value Used / Customer — monthly trend",
 
 
130
  "inv_store_filter": "Store",
131
  # Sign-in screen
132
  "auth_title": "Copper Group Dashboard",
@@ -242,6 +244,8 @@ LANG = {
242
  "inv_kpi_value_used": "มูลค่าที่ใช้ทั้งหมด",
243
  "inv_kpi_value_per_cust": "มูลค่าที่ใช้ต่อลูกค้า",
244
  "inv_chart_vpc_trend": "มูลค่าที่ใช้ต่อลูกค้า — แนวโน้มรายเดือน",
 
 
245
  "inv_store_filter": "สโตร์",
246
  # Sign-in screen
247
  "auth_title": "แดชบอร์ดคอปเปอร์กรุ๊ป",
@@ -2405,14 +2409,62 @@ with tab_inv:
2405
  fmt_money(_value_per_cust) if _value_per_cust is not None else "—",
2406
  )
2407
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2408
  # ── Monthly trend: Value Used / Customer ─────────────────────
2409
- # Aggregates over ALL months in the sidebar date range (not just
2410
- # the picked month). Numerator = sum(Value_Used) per month from
2411
- # fact_inventory; denominator = sum(Customers) per month from
2412
- # kpi_daily under the same Restaurant/Branch filter.
2413
  _inv_for_trend = inv_filt.copy()
2414
  _inv_for_trend["Date"] = pd.to_datetime(_inv_for_trend["Date"], errors="coerce")
2415
  _inv_for_trend = _inv_for_trend.dropna(subset=["Date"])
 
 
 
2416
  if not _inv_for_trend.empty and "Value_Used" in _inv_for_trend.columns:
2417
  _inv_for_trend["YearMonth"] = _inv_for_trend["Date"].dt.to_period("M").astype(str)
2418
  _inv_for_trend["Year"] = _inv_for_trend["Date"].dt.year
@@ -2471,34 +2523,16 @@ with tab_inv:
2471
  textfont=dict(size=9),
2472
  )
2473
  fig.update_yaxes(tickformat=",.0f")
 
 
 
 
 
2474
  fig.update_layout(
2475
- title=t("inv_chart_vpc_trend"),
2476
  xaxis_title=None, yaxis_title=None,
2477
  showlegend=False,
2478
  )
2479
  st.plotly_chart(style_plotly(fig, height=320), use_container_width=True)
2480
 
2481
- ranked = latest_rows[latest_rows[sort_by] > 0].sort_values(sort_by, ascending=False).head(100)
2482
-
2483
- cols_to_show = [c for c in
2484
- ["Item", "Restaurant", "Branch", "Store Name",
2485
- "Unit", "Qty_Closing", "Value_Closing",
2486
- "Qty_Used", "Value_Used"]
2487
- if c in ranked.columns]
2488
- disp = ranked[cols_to_show].copy()
2489
- for c in ("Qty_Closing", "Qty_Used"):
2490
- if c in disp.columns:
2491
- disp[c] = disp[c].map(fmt_qty)
2492
- for c in ("Value_Closing", "Value_Used"):
2493
- if c in disp.columns:
2494
- disp[c] = disp[c].map(fmt_money)
2495
- disp = disp.rename(columns={
2496
- "Value_Closing": "Value Closing (THB)",
2497
- "Value_Used": "Value Used (THB)",
2498
- })
2499
- st.dataframe(
2500
- disp,
2501
- use_container_width=True, hide_index=True,
2502
- )
2503
-
2504
 
 
127
  "inv_kpi_value_used": "Total Value Used",
128
  "inv_kpi_value_per_cust": "Value Used / Customer",
129
  "inv_chart_vpc_trend": "Value Used / Customer — monthly trend",
130
+ "inv_chart_vpc_item": "Value Used / Customer — {item} (monthly)",
131
+ "inv_table_hint": "Click any row to filter the chart below to that item. Click the same row again to clear.",
132
  "inv_store_filter": "Store",
133
  # Sign-in screen
134
  "auth_title": "Copper Group Dashboard",
 
244
  "inv_kpi_value_used": "มูลค่าที่ใช้ทั้งหมด",
245
  "inv_kpi_value_per_cust": "มูลค่าที่ใช้ต่อลูกค้า",
246
  "inv_chart_vpc_trend": "มูลค่าที่ใช้ต่อลูกค้า — แนวโน้มรายเดือน",
247
+ "inv_chart_vpc_item": "มูลค่าที่ใช้ต่อลูกค้า — {item} (รายเดือน)",
248
+ "inv_table_hint": "คลิกแถวใดก็ได้เพื่อกรองกราฟด้านล่างเฉพาะรายการนั้น คลิกแถวเดิมอีกครั้งเพื่อล้าง",
249
  "inv_store_filter": "สโตร์",
250
  # Sign-in screen
251
  "auth_title": "แดชบอร์ดคอปเปอร์กรุ๊ป",
 
2409
  fmt_money(_value_per_cust) if _value_per_cust is not None else "—",
2410
  )
2411
 
2412
+ # ── Snapshot table (with row selection) ──────────────────────
2413
+ # Render the table FIRST. A single-row selection here drives the
2414
+ # chart below — clicking an item filters its monthly Value Used
2415
+ # / Customer trend; clicking again clears.
2416
+ ranked = latest_rows[latest_rows[sort_by] > 0].sort_values(sort_by, ascending=False).head(100)
2417
+
2418
+ cols_to_show = [c for c in
2419
+ ["Item", "Restaurant", "Branch", "Store Name",
2420
+ "Unit", "Qty_Closing", "Value_Closing",
2421
+ "Qty_Used", "Value_Used"]
2422
+ if c in ranked.columns]
2423
+ disp = ranked[cols_to_show].copy()
2424
+ for c in ("Qty_Closing", "Qty_Used"):
2425
+ if c in disp.columns:
2426
+ disp[c] = disp[c].map(fmt_qty)
2427
+ for c in ("Value_Closing", "Value_Used"):
2428
+ if c in disp.columns:
2429
+ disp[c] = disp[c].map(fmt_money)
2430
+ disp = disp.rename(columns={
2431
+ "Value_Closing": "Value Closing (THB)",
2432
+ "Value_Used": "Value Used (THB)",
2433
+ })
2434
+ st.caption(t("inv_table_hint"))
2435
+ _table_event = st.dataframe(
2436
+ disp,
2437
+ use_container_width=True, hide_index=True,
2438
+ on_select="rerun",
2439
+ selection_mode="single-row",
2440
+ key="inv_table",
2441
+ )
2442
+
2443
+ # Translate the picked row index back to the underlying Item name.
2444
+ # The picker indexes into the displayed (formatted) DataFrame,
2445
+ # which has the same row order as `ranked`, so we can look it up
2446
+ # there to recover the original (unformatted) Item value.
2447
+ _selected_item: "str | None" = None
2448
+ try:
2449
+ _sel_rows = _table_event.selection.rows # list[int]
2450
+ if _sel_rows and "Item" in ranked.columns:
2451
+ _idx = _sel_rows[0]
2452
+ if 0 <= _idx < len(ranked):
2453
+ _selected_item = str(ranked.iloc[_idx]["Item"])
2454
+ except Exception:
2455
+ _selected_item = None
2456
+
2457
  # ── Monthly trend: Value Used / Customer ─────────────────────
2458
+ # If a row was selected above, filter the numerator (Value_Used)
2459
+ # to that item only the denominator (Customers) stays as the
2460
+ # period total because we're asking "for this item, how much
2461
+ # value per customer did we burn each month?".
2462
  _inv_for_trend = inv_filt.copy()
2463
  _inv_for_trend["Date"] = pd.to_datetime(_inv_for_trend["Date"], errors="coerce")
2464
  _inv_for_trend = _inv_for_trend.dropna(subset=["Date"])
2465
+ if _selected_item is not None and "Item" in _inv_for_trend.columns:
2466
+ _inv_for_trend = _inv_for_trend[_inv_for_trend["Item"] == _selected_item]
2467
+
2468
  if not _inv_for_trend.empty and "Value_Used" in _inv_for_trend.columns:
2469
  _inv_for_trend["YearMonth"] = _inv_for_trend["Date"].dt.to_period("M").astype(str)
2470
  _inv_for_trend["Year"] = _inv_for_trend["Date"].dt.year
 
2523
  textfont=dict(size=9),
2524
  )
2525
  fig.update_yaxes(tickformat=",.0f")
2526
+ _chart_title = (
2527
+ t("inv_chart_vpc_item", item=_selected_item)
2528
+ if _selected_item is not None
2529
+ else t("inv_chart_vpc_trend")
2530
+ )
2531
  fig.update_layout(
2532
+ title=_chart_title,
2533
  xaxis_title=None, yaxis_title=None,
2534
  showlegend=False,
2535
  )
2536
  st.plotly_chart(style_plotly(fig, height=320), use_container_width=True)
2537
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2538