ps1811 commited on
Commit
66e8e1d
Β·
1 Parent(s): be506d7

Added UI design.md. Modified landing page

Browse files
Files changed (2) hide show
  1. app.py +106 -83
  2. docs/superpowers/plans/UI.md +128 -0
app.py CHANGED
@@ -1,140 +1,163 @@
1
  import gradio as gr
2
  import os
3
-
4
- print("APP STARTED", flush=True)
5
 
6
  from app.db.repo import init_db
7
- from app.ui.dashboard import build_dashboard, get_dashboard_data
8
  from app.controller.campaign_controller import on_campaign_select
9
  from app.controller.session_loader import load_google_ads_data
10
  from app.ads1.ads_analyst import run_ads_analyst_card
11
  from app.ads1.budget_optimizer import run_budget_optimizer_card
12
- import spaces
13
-
14
- print("πŸ”₯ STEP 1: imports done", flush=True)
15
 
16
 
17
- @spaces.GPU(duration=120)
18
- def run_ads_card(state):
19
- print("\nπŸ”₯ [run_ads_card] ENTERED", flush=True)
 
20
  try:
21
- if not state:
22
- print("❌ [run_ads_card] state is EMPTY", flush=True)
23
- return "⚠️ Select a campaign first"
 
24
 
25
- print("πŸ“¦ [run_ads_card] state received", type(state), flush=True)
26
- dfs = state.get("full_dfs")
27
- campaign_name = state.get("campaign_name")
28
- print("πŸ“Š [run_ads_card] extracted dfs:", type(dfs), flush=True)
29
 
30
- if not dfs:
31
- return "⚠️ No campaign data β€” select a campaign on the Dashboard tab first."
 
 
 
32
 
33
- result = run_ads_analyst_card(dfs, campaign_name=campaign_name)
34
- print("βœ… [run_ads_card] returning result", flush=True)
35
- return result
36
- except Exception as e:
37
- print("❌ [run_ads_card] ERROR:", repr(e), flush=True)
38
- return f"⚠️ Analysis failed: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
 
 
 
 
41
  @spaces.GPU(duration=120)
42
- def run_budget_card(state):
43
- print("\nπŸ”₯ [run_budget_card] ENTERED", flush=True)
 
44
  try:
45
  if not state:
46
- print("❌ [run_budget_card] state is EMPTY", flush=True)
47
  return "⚠️ Select a campaign first"
48
 
49
  dfs = state.get("full_dfs")
50
  campaign_name = state.get("campaign_name")
51
- if not dfs:
52
- return "⚠️ No campaign data β€” select a campaign on the Dashboard tab first."
53
 
54
- result = run_budget_optimizer_card(dfs, campaign_name=campaign_name)
55
- print("βœ… [run_budget_card] returning result", flush=True)
56
- return result
57
- except Exception as e:
58
- print("❌ [run_budget_card] ERROR:", repr(e), flush=True)
59
- return f"⚠️ Budget optimization failed: {e}"
60
 
 
61
 
62
- def startup():
63
- try:
64
- init_db()
65
- print("βœ… DB initialized successfully", flush=True)
66
  except Exception as e:
67
- print("⚠️ DB failed:", e, flush=True)
 
68
 
69
 
70
- print("πŸ”₯ STEP 2: DB init done", flush=True)
 
 
 
71
 
72
- # UI Optimization: Fetch data AFTER UI elements are drawn
73
- def initial_data_load():
74
- print("πŸ”„ App loaded. Population of background states initiated...")
75
- dfs = load_google_ads_data()
76
- # Unpack dashboard metrics to fill the UI immediately
77
- spend, leads, cpl, count, formatted_df = get_dashboard_data()
78
- return dfs, formatted_df, spend, leads, cpl, count
79
 
 
80
 
81
- def campaign_row_selected(df, full_state, evt: gr.SelectData):
82
- row_index = evt.index[0]
83
- campaign_name = df.iloc[row_index]["Campaign"]
84
- campaign_state = on_campaign_select(full_state, campaign_name)
85
- return campaign_state, f"## πŸ“Š Selected Campaign: {campaign_name}"
86
 
 
 
 
87
 
88
- print("πŸ”₯ STEP 3: building UI", flush=True)
 
 
 
89
 
90
- with gr.Blocks() as demo:
91
- # Initialize components empty; populated safely via demo.load
92
- full_state = gr.State()
93
- campaign_state = gr.State()
94
- # df_state = gr.State()
95
 
96
- gr.Markdown("# 🎯 Ads Dashboard")
 
 
 
 
 
97
 
98
- with gr.Tab("Dashboard"):
99
- # We modify build_dashboard to expose metric components for automated hydration
100
- gr.Markdown("## πŸ“Š Campaign Dashboard")
101
- with gr.Row():
102
- total_spend = gr.Number(label="Total Spend")
103
- total_leads = gr.Number(label="Total Leads")
104
- average_cpl = gr.Number(label="Average CPL")
105
- active_campaigns = gr.Number(label="Active Campaigns")
106
 
107
- campaign_table = gr.Dataframe(label="Campaign Performance", interactive=True)
108
- refresh_btn = gr.Button("πŸ”„ Force Refresh Data")
 
109
 
110
- with gr.Tab("Analysis"):
111
- selected = gr.Markdown("πŸ‘ˆ Select a campaign from the Dashboard tab")
112
- output = gr.Markdown()
 
113
 
114
- with gr.Row():
115
- gr.Button("πŸš€ Run Ads Analysis").click(run_ads_card, campaign_state, output)
116
- gr.Button("πŸ’° Run Budget Optimization").click(run_budget_card, campaign_state, output)
 
 
 
 
 
 
 
 
 
 
117
 
118
  campaign_table.select(
119
  fn=campaign_row_selected,
120
  inputs=[campaign_table, full_state],
121
- outputs=[campaign_state, selected],
122
  )
123
 
124
- # Button manual refresh
125
- refresh_btn.click(
126
- fn=initial_data_load,
127
- outputs=[full_state, campaign_table, total_spend, total_leads, average_cpl, active_campaigns],
128
  )
129
 
130
- # ⚑ MAGIC FIX: App automatically loads data into UI components instantly on launch
 
 
 
131
  demo.load(
132
  fn=initial_data_load,
133
  outputs=[full_state, campaign_table, total_spend, total_leads, average_cpl, active_campaigns],
134
  )
 
135
  demo.load(fn=startup)
136
 
 
137
  demo.queue()
138
 
139
  if __name__ == "__main__":
140
- demo.launch()
 
1
  import gradio as gr
2
  import os
3
+ import spaces
 
4
 
5
  from app.db.repo import init_db
 
6
  from app.controller.campaign_controller import on_campaign_select
7
  from app.controller.session_loader import load_google_ads_data
8
  from app.ads1.ads_analyst import run_ads_analyst_card
9
  from app.ads1.budget_optimizer import run_budget_optimizer_card
 
 
 
10
 
11
 
12
+ # -----------------------------
13
+ # INIT
14
+ # -----------------------------
15
+ def startup():
16
  try:
17
+ init_db()
18
+ print("βœ… DB initialized successfully", flush=True)
19
+ except Exception as e:
20
+ print("⚠️ DB failed:", e, flush=True)
21
 
 
 
 
 
22
 
23
+ # -----------------------------
24
+ # DATA LOAD
25
+ # -----------------------------
26
+ def initial_data_load():
27
+ print("πŸ”„ Loading app data...")
28
 
29
+ dfs = load_google_ads_data()
30
+
31
+ from app.ui.dashboard import get_dashboard_data
32
+ spend, leads, cpl, count, formatted_df = get_dashboard_data()
33
+
34
+ return dfs, formatted_df, spend, leads, cpl, count
35
+
36
+
37
+ # -----------------------------
38
+ # CAMPAIGN SELECT
39
+ # -----------------------------
40
+ def campaign_row_selected(df, full_state, evt: gr.SelectData):
41
+ row_index = evt.index[0]
42
+ campaign_name = df.iloc[row_index]["Campaign"]
43
+
44
+ campaign_state = on_campaign_select(full_state, campaign_name)
45
+
46
+ return campaign_state, f"πŸ“Š {campaign_name}"
47
 
48
 
49
+ # -----------------------------
50
+ # ADS ANALYST CARD WRAPPER
51
+ # -----------------------------
52
  @spaces.GPU(duration=120)
53
+ def run_ads_card(state):
54
+ print("\nπŸ”₯ [run_ads_card] ENTERED", flush=True)
55
+
56
  try:
57
  if not state:
 
58
  return "⚠️ Select a campaign first"
59
 
60
  dfs = state.get("full_dfs")
61
  campaign_name = state.get("campaign_name")
 
 
62
 
63
+ if dfs is None:
64
+ return "⚠️ No campaign data available"
 
 
 
 
65
 
66
+ return run_ads_analyst_card(dfs, campaign_name=campaign_name)
67
 
 
 
 
 
68
  except Exception as e:
69
+ print("❌ ERROR:", repr(e), flush=True)
70
+ return f"⚠️ Analysis failed: {e}"
71
 
72
 
73
+ # -----------------------------
74
+ # UI
75
+ # -----------------------------
76
+ with gr.Blocks() as demo:
77
 
78
+ # STATE
79
+ full_state = gr.State()
80
+ campaign_state = gr.State()
 
 
 
 
81
 
82
+ gr.Markdown("# 🎯 Ads Intelligence Workspace")
83
 
84
+ # =========================
85
+ # MAIN LAYOUT
86
+ # =========================
87
+ with gr.Row():
 
88
 
89
+ # LEFT PANEL
90
+ with gr.Column(scale=1):
91
+ gr.Markdown("## πŸ“ Campaigns")
92
 
93
+ campaign_table = gr.Dataframe(
94
+ label="Campaigns",
95
+ interactive=True
96
+ )
97
 
98
+ # RIGHT PANEL
99
+ with gr.Column(scale=3):
 
 
 
100
 
101
+ # GLOBAL KPIs
102
+ with gr.Row():
103
+ total_spend = gr.Number(label="Spend")
104
+ total_leads = gr.Number(label="Leads")
105
+ average_cpl = gr.Number(label="CPL")
106
+ active_campaigns = gr.Number(label="Campaigns")
107
 
108
+ # CAMPAIGN BANNER
109
+ campaign_banner = gr.Markdown(
110
+ "πŸ‘‰ Select a campaign to begin analysis"
111
+ )
 
 
 
 
112
 
113
+ # =========================
114
+ # AI CARDS (CURSOR STYLE)
115
+ # =========================
116
 
117
+ with gr.Row():
118
+ ads_card = gr.Button("πŸ“Š Ads Analyst")
119
+ gr.Markdown("πŸ’° Budget Optimizer (inactive)")
120
+ gr.Markdown("🎯 Keyword Intelligence (inactive)")
121
 
122
+ with gr.Row():
123
+ gr.Markdown("⚠️ Risk Detector (inactive)")
124
+ gr.Markdown("πŸ“ˆ Growth Finder (inactive)")
125
+ gr.Markdown("πŸ§ͺ Experiment Ideas (inactive)")
126
+
127
+ # OUTPUT AREA (ONLY FOR ADS CARD NOW)
128
+ ads_output = gr.Markdown(
129
+ value="Click 'Ads Analyst' to analyze selected campaign"
130
+ )
131
+
132
+ # =========================
133
+ # EVENTS
134
+ # =========================
135
 
136
  campaign_table.select(
137
  fn=campaign_row_selected,
138
  inputs=[campaign_table, full_state],
139
+ outputs=[campaign_state, campaign_banner],
140
  )
141
 
142
+ ads_card.click(
143
+ fn=run_ads_card,
144
+ inputs=campaign_state,
145
+ outputs=ads_output
146
  )
147
 
148
+ # =========================
149
+ # DATA LOAD
150
+ # =========================
151
+
152
  demo.load(
153
  fn=initial_data_load,
154
  outputs=[full_state, campaign_table, total_spend, total_leads, average_cpl, active_campaigns],
155
  )
156
+
157
  demo.load(fn=startup)
158
 
159
+
160
  demo.queue()
161
 
162
  if __name__ == "__main__":
163
+ demo.launch()
docs/superpowers/plans/UI.md ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Final UI Architecture (clean version)
2
+ 🟦 LEFT PANEL (Navigation)
3
+
4
+ Campaign list:
5
+
6
+ πŸ“ Campaigns
7
+ - Preschool Search
8
+ - Brand Campaign
9
+ - Local Ads
10
+
11
+ Click β†’ updates state
12
+
13
+ 🟨 RIGHT PANEL (Workspace)
14
+ 🧱 A. Global Header (fixed)
15
+ 🎯 Ads Dashboard
16
+ Total Spend | Total Leads | Avg CPL | Active Campaigns
17
+ 🧱 B. Campaign Context Banner (ONLY when selected)
18
+ πŸ“Š Campaign Snapshot
19
+ Spend | Leads | CTR | CPL | Trend
20
+
21
+ If no campaign selected β†’ hidden or replaced with placeholder
22
+
23
+ 🧱 C. 6 AI INSIGHT CARDS (core system)
24
+
25
+ Layout:
26
+
27
+ Row 1:
28
+ [Ads Analyst] [Budget Optimizer] [Keyword Intelligence]
29
+
30
+ Row 2:
31
+ [Risk Detector] [Growth Finder] [Experiment Ideas]
32
+ 🟩 BEFORE CAMPAIGN SELECTION
33
+
34
+ Right panel shows:
35
+
36
+ πŸ‘‰ Select a campaign to begin analysis
37
+
38
+ AND:
39
+
40
+ 6 cards are visible BUT greyed out
41
+ disabled state
42
+ no click behavior
43
+ 🟦 AFTER CAMPAIGN SELECTION
44
+ banner appears
45
+ cards activate
46
+ clicking triggers expansion
47
+ 🧠 CARD BEHAVIOR (important consistency rule)
48
+
49
+ Each card:
50
+
51
+ Idle
52
+ Title only + 1-line hint
53
+ Clicked
54
+ Expands in place
55
+ β†’ loading
56
+ β†’ MiniCPM explanation
57
+ β†’ action buttons
58
+
59
+ No page change ever.
60
+
61
+ 🎯 3. Final Simple Design Doc (you can implement directly)
62
+ 🧩 Layout Structure
63
+ 1. App Layout
64
+ Left: Campaign Navigator (fixed width ~25%)
65
+ Right: Analysis Workspace (~75%)
66
+ 🧱 2. Right Panel Structure
67
+ A. Global Metrics Header (always visible)
68
+ Total Spend
69
+ Total Leads
70
+ Average CPL
71
+ Active Campaigns
72
+ B. Campaign Context Banner
73
+
74
+ Visible only when campaign selected:
75
+
76
+ Campaign name
77
+ Spend
78
+ CTR
79
+ CPL
80
+ Trend indicators
81
+
82
+ If no selection:
83
+
84
+ show placeholder message
85
+ C. AI Insight Grid (2x3 layout)
86
+
87
+ Cards:
88
+
89
+ Ads Performance Analyst
90
+ Budget Optimizer
91
+ Keyword Intelligence
92
+ Risk Detector
93
+ Growth Opportunities
94
+ Experiment Ideas
95
+ 🎨 3. Visual States
96
+ STATE 1: No campaign selected
97
+
98
+ Right panel:
99
+
100
+ message: β€œSelect a campaign to begin analysis”
101
+ 6 cards greyed out
102
+ STATE 2: Campaign selected
103
+ Context banner appears
104
+ Cards activate (clickable)
105
+ STATE 3: Card clicked
106
+
107
+ Inside same card:
108
+
109
+ loading spinner
110
+ MiniCPM explanation
111
+ recommendation
112
+
113
+ ⚑ 4. Key UX Principles
114
+ Context β‰  Action (banner vs cards)
115
+ No navigation changes (single workspace)
116
+ Progressive disclosure (click β†’ expand)
117
+ Grey-out inactive capability (not hide it)
118
+
119
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
120
+ β”‚ HEADER: Global KPIs (always visible) β”‚
121
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
122
+ β”‚ LEFT PANEL β”‚ RIGHT PANEL β”‚
123
+ β”‚ Campaign List β”‚ Workspace β”‚
124
+ β”‚ β”‚ β”‚
125
+ β”‚ β”‚ [Campaign Context Banner] β”‚
126
+ β”‚ β”‚ β”‚
127
+ β”‚ β”‚ [6 AI Cards Grid (2x3)] β”‚
128
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜