"""Page 4: Current Admin Headcount (from scraper).""" import duckdb from dash import html, dcc, dash_table import plotly.express as px import plotly.graph_objects as go from admin_analytics.dashboard.queries import ( query_admin_headcount, query_headcount_summary, ) _NO_DATA = html.Div( "No headcount data loaded. Run: admin-analytics ingest scrape", style={"textAlign": "center", "padding": "40px", "color": "#888"}, ) def _kpi_card(title: str, value: str) -> html.Div: return html.Div( [ html.H4(title, style={"margin": "0", "color": "#666", "fontSize": "14px"}), html.H2(value, style={"margin": "5px 0", "color": "#00539F"}), ], style={ "flex": "1", "padding": "20px", "backgroundColor": "#f8f9fa", "borderRadius": "8px", "textAlign": "center", "margin": "0 8px", }, ) def layout(conn: duckdb.DuckDBPyConnection): detail_df = query_admin_headcount(conn) if detail_df.height == 0: return _NO_DATA summary_df = query_headcount_summary(conn) detail_pd = detail_df.to_pandas() summary_pd = summary_df.to_pandas() total = len(detail_pd) overhead_count = int(detail_pd["is_overhead"].sum()) if "is_overhead" in detail_pd.columns else 0 overhead_pct = round(overhead_count * 100 / total, 1) if total > 0 else 0 # KPI cards kpi_row = html.Div( [ _kpi_card("Total Staff Scraped", str(total)), _kpi_card("Overhead Staff", str(overhead_count)), _kpi_card("Overhead %", f"{overhead_pct}%"), ], style={"display": "flex", "marginBottom": "24px"}, ) # Staff by unit bar chart unit_counts = summary_pd.groupby("unit")["count"].sum().reset_index().sort_values("count") unit_fig = px.bar( unit_counts, x="count", y="unit", orientation="h", title="Staff Count by Unit", labels={"count": "Staff", "unit": ""}, color_discrete_sequence=["#00539F"], ) unit_fig.update_layout(template="plotly_white", height=max(300, len(unit_counts) * 30 + 100)) # Overhead pie oh_data = detail_pd["is_overhead"].value_counts() oh_labels = {True: "Overhead", False: "Non-Overhead"} pie_fig = px.pie( names=[oh_labels.get(k, "Debatable") for k in oh_data.index], values=oh_data.values, title="Overhead vs Non-Overhead", color_discrete_sequence=["#E07A5F", "#7FB069", "#999"], ) pie_fig.update_layout(template="plotly_white", height=350) # Category distribution per unit cat_fig = px.bar( summary_pd, x="count", y="unit", color="category", orientation="h", title="Category Distribution by Unit", labels={"count": "Staff", "unit": "", "category": "Category"}, ) cat_fig.update_layout(template="plotly_white", height=max(300, len(unit_counts) * 30 + 100)) # Detail table table = dash_table.DataTable( columns=[ {"name": "Unit", "id": "unit"}, {"name": "Name", "id": "person_name"}, {"name": "Title", "id": "title"}, {"name": "Category", "id": "category"}, {"name": "Overhead", "id": "is_overhead"}, ], data=detail_pd.to_dict("records"), page_size=20, sort_action="native", filter_action="native", style_table={"overflowX": "auto"}, style_cell={"textAlign": "left", "padding": "8px", "fontSize": "13px"}, style_header={"fontWeight": "bold", "backgroundColor": "#f0f0f0"}, ) return html.Div([ kpi_row, html.Div( [ html.Div(dcc.Graph(figure=unit_fig), style={"flex": "1"}), html.Div(dcc.Graph(figure=pie_fig), style={"flex": "1"}), ], style={"display": "flex", "gap": "16px"}, ), dcc.Graph(figure=cat_fig), ])