NujacsaintS commited on
Commit
9f5e052
Β·
verified Β·
1 Parent(s): 5a9e13c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +241 -9
app.py CHANGED
@@ -1,19 +1,251 @@
1
- from shiny import App, ui, render
 
 
 
 
 
 
2
 
3
- # Simple UI
4
  app_ui = ui.page_fluid(
5
  ui.h2("Tessai Link: Phase 0"),
6
- ui.p("If you can see this, your custom app.py is working."),
7
- ui.input_text("company_name", "Company name"),
8
- ui.input_text_area("notes", "Initial notes"),
9
- ui.output_text_verbatim("debug")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  )
11
 
12
- # Server logic
 
 
 
 
13
  def server(input, output, session):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  @output
15
  @render.text
16
- def debug():
17
- return f"Company: {input.company_name()}\nNotes:\n{input.notes()}"
 
 
18
 
19
  app = App(app_ui, server)
 
1
+ from shiny import App, ui, render, reactive
2
+ import json
3
+ import datetime as dt
4
+
5
+ # -----------------------------
6
+ # UI
7
+ # -----------------------------
8
 
 
9
  app_ui = ui.page_fluid(
10
  ui.h2("Tessai Link: Phase 0"),
11
+ ui.p(
12
+ "This form helps us understand your current environment and what you "
13
+ "are hoping to change. Fill out what you can -- we will review the rest together."
14
+ ),
15
+
16
+ ui.layout_columns(
17
+ # Left column: basic company + contacts
18
+ ui.card(
19
+ ui.card_header("1. Company & Contact"),
20
+ ui.input_text("company_name", "Company name"),
21
+ ui.input_text("contact_name", "Primary contact name"),
22
+ ui.input_text("contact_role", "Contact role / title"),
23
+ ui.input_text("contact_email", "Contact email"),
24
+ ui.input_text("contact_phone", "Contact phone (optional)"),
25
+ ),
26
+
27
+ # Right column: project trigger + goals
28
+ ui.card(
29
+ ui.card_header("2. Why now?"),
30
+ ui.input_text_area(
31
+ "project_trigger",
32
+ "What triggered this project now?",
33
+ placeholder="Example: Our current system is slowing us down, we are growing, a new compliance requirement, etc.",
34
+ rows=4,
35
+ ),
36
+ ui.input_text_area(
37
+ "primary_outcomes",
38
+ "What outcomes would feel like a clear win?",
39
+ placeholder="Example: Fewer dropped handoffs, clearer ownership, faster quoting, better visibility across teams...",
40
+ rows=4,
41
+ ),
42
+ ),
43
+ ),
44
+
45
+ ui.layout_columns(
46
+ # Current tools / systems / data
47
+ ui.card(
48
+ ui.card_header("3. Current tools & data"),
49
+ ui.input_text_area(
50
+ "current_tools",
51
+ "Current tools / systems in use",
52
+ placeholder="Example: Excel, email, QuickBooks, Odoo, custom access DB, paper forms...",
53
+ rows=4,
54
+ ),
55
+ ui.input_text_area(
56
+ "data_sources",
57
+ "Key data sources we should know about",
58
+ placeholder="Example: Shared drives, vendor portals, ERP, CRM, PDF drawings, email inboxes...",
59
+ rows=4,
60
+ ),
61
+ ),
62
+
63
+ # Scope, change profile, constraints
64
+ ui.card(
65
+ ui.card_header("4. Scope & constraints"),
66
+ ui.input_selectize(
67
+ "areas_of_interest",
68
+ "Primary areas you want to improve (choose all that apply)",
69
+ choices=[
70
+ "Lead / opportunity tracking",
71
+ "Quoting / estimating",
72
+ "Operations / workflows",
73
+ "Vendor integration",
74
+ "Reporting / dashboards",
75
+ "Document / email intake",
76
+ "Change management / approvals",
77
+ "Other",
78
+ ],
79
+ multiple=True,
80
+ ),
81
+ ui.input_radio_buttons(
82
+ "change_profile",
83
+ "How would you describe this change?",
84
+ choices=[
85
+ "Exploring options -- nothing urgent yet",
86
+ "We have a clearly broken process to fix",
87
+ "We are replacing or consolidating existing systems",
88
+ "We have a hard deadline / compliance date",
89
+ ],
90
+ ),
91
+ ui.input_select(
92
+ "team_size",
93
+ "Roughly how many people are affected?",
94
+ choices=[
95
+ "1–5",
96
+ "6–15",
97
+ "16–50",
98
+ "51–150",
99
+ "150+",
100
+ ],
101
+ ),
102
+ ui.input_select(
103
+ "timeline",
104
+ "Ideal timeline to see first meaningful results",
105
+ choices=[
106
+ "0–6 weeks",
107
+ "6–12 weeks",
108
+ "3–6 months",
109
+ "6–12 months",
110
+ "Just exploring for now",
111
+ ],
112
+ ),
113
+ ),
114
+ ),
115
+
116
+ ui.layout_columns(
117
+ ui.card(
118
+ ui.card_header("5. Risks, blockers, and notes"),
119
+ ui.input_text_area(
120
+ "risks_blockers",
121
+ "Known risks or blockers",
122
+ placeholder="Example: Limited internal capacity, key person risk, uncertain budget, complex approvals...",
123
+ rows=3,
124
+ ),
125
+ ui.input_text_area(
126
+ "other_notes",
127
+ "Anything else we should know?",
128
+ placeholder="Free space for context, history, politics, or 'things that have been tried before'.",
129
+ rows=3,
130
+ ),
131
+ ),
132
+ ),
133
+
134
+ ui.hr(),
135
+ ui.input_action_button("submit", "Submit intake", class_="btn-primary"),
136
+
137
+ ui.hr(),
138
+ ui.layout_columns(
139
+ ui.card(
140
+ ui.card_header("Intake summary (human-readable)"),
141
+ ui.output_text_verbatim("intake_summary"),
142
+ ),
143
+ ui.card(
144
+ ui.card_header("Raw payload (for systems)"),
145
+ ui.output_text_verbatim("intake_payload"),
146
+ ),
147
+ ),
148
  )
149
 
150
+ # -----------------------------
151
+ # Server
152
+ # -----------------------------
153
+
154
+
155
  def server(input, output, session):
156
+ # Build a structured dict from the current inputs
157
+ def build_payload():
158
+ return {
159
+ "submitted_at": dt.datetime.now().isoformat(timespec="seconds"),
160
+ "company": {
161
+ "name": input.company_name(),
162
+ "contact_name": input.contact_name(),
163
+ "contact_role": input.contact_role(),
164
+ "contact_email": input.contact_email(),
165
+ "contact_phone": input.contact_phone(),
166
+ },
167
+ "project": {
168
+ "trigger": input.project_trigger(),
169
+ "primary_outcomes": input.primary_outcomes(),
170
+ },
171
+ "environment": {
172
+ "current_tools": input.current_tools(),
173
+ "data_sources": input.data_sources(),
174
+ },
175
+ "scope": {
176
+ "areas_of_interest": input.areas_of_interest(),
177
+ "change_profile": input.change_profile(),
178
+ "team_size": input.team_size(),
179
+ "timeline": input.timeline(),
180
+ },
181
+ "risks_and_notes": {
182
+ "risks_blockers": input.risks_blockers(),
183
+ "other_notes": input.other_notes(),
184
+ },
185
+ }
186
+
187
+ # Reactive value that updates only when "Submit intake" is clicked
188
+ @reactive.calc
189
+ def submitted_payload():
190
+ # Wait for the button click event
191
+ input.submit()
192
+ return build_payload()
193
+
194
+ @output
195
+ @render.text
196
+ def intake_summary():
197
+ d = submitted_payload()
198
+
199
+ company = d["company"]
200
+ project = d["project"]
201
+ env = d["environment"]
202
+ scope = d["scope"]
203
+ risks = d["risks_and_notes"]
204
+
205
+ areas = scope["areas_of_interest"] or []
206
+ areas_str = ", ".join(areas) if areas else "Not specified"
207
+
208
+ lines = [
209
+ f"Submitted at: {d['submitted_at']}",
210
+ "",
211
+ f"Company: {company['name'] or 'Unknown'}",
212
+ f"Contact: {company['contact_name'] or 'Unknown'} "
213
+ f"({company['contact_role'] or 'role not specified'})",
214
+ f"Email: {company['contact_email'] or 'not provided'}",
215
+ f"Phone: {company['contact_phone'] or 'not provided'}",
216
+ "",
217
+ "Why now:",
218
+ project["trigger"] or "(no description provided)",
219
+ "",
220
+ "Primary outcomes:",
221
+ project["primary_outcomes"] or "(no outcomes described yet)",
222
+ "",
223
+ "Current tools / systems:",
224
+ env["current_tools"] or "(not listed)",
225
+ "",
226
+ "Key data sources:",
227
+ env["data_sources"] or "(not listed)",
228
+ "",
229
+ "Scope snapshot:",
230
+ f" Areas of interest: {areas_str}",
231
+ f" Change profile: {scope['change_profile'] or 'Not specified'}",
232
+ f" Team size affected: {scope['team_size'] or 'Not specified'}",
233
+ f" Timeline: {scope['timeline'] or 'Not specified'}",
234
+ "",
235
+ "Risks / blockers:",
236
+ risks["risks_blockers"] or "(none listed)",
237
+ "",
238
+ "Other notes:",
239
+ risks["other_notes"] or "(none)",
240
+ ]
241
+
242
+ return "\n".join(lines)
243
+
244
  @output
245
  @render.text
246
+ def intake_payload():
247
+ d = submitted_payload()
248
+ return json.dumps(d, indent=2)
249
+
250
 
251
  app = App(app_ui, server)