JaydeepR Claude Sonnet 4.6 commited on
Commit
8a64f5c
·
1 Parent(s): ed344be

Two UI changes: criteria delete button and bidder toggle

Browse files

tab_tender.py: add × button per criterion (columns layout, one per row).
Clicking removes that criterion from session_state and reruns — evaluation
will only run against the remaining criteria.

tab_bidders.py: wrap all criteria rows in st.expander("View all N criteria").
Bidder card header (name, overall verdict, pass/total) always visible when
collapsed; all criterion rows appear on expand.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (2) hide show
  1. ui/tab_bidders.py +82 -80
  2. ui/tab_tender.py +23 -19
ui/tab_bidders.py CHANGED
@@ -118,87 +118,89 @@ def render() -> None:
118
  unsafe_allow_html=True,
119
  )
120
 
121
- # Column headers
122
- st.markdown(
123
- '<div style="display:grid;grid-template-columns:2.5fr 1.6fr 1.8fr 2.2fr 1.4fr;'
124
- 'gap:8px;padding:6px 2px;border-top:1px solid rgba(128,128,128,0.15);'
125
- 'border-bottom:1px solid rgba(128,128,128,0.15);margin-bottom:4px;">'
126
- + "".join(
127
- f'<div style="font-size:0.68rem;font-weight:700;'
128
- f'color:var(--text-color);opacity:0.4;'
129
- f'text-transform:uppercase;letter-spacing:0.07em;">{h}</div>'
130
- for h in ["Criterion", "Verdict", "Extracted Value", "Source & Tier", "Category"]
131
- )
132
- + "</div>",
133
- unsafe_allow_html=True,
134
- )
135
-
136
- for v in verdicts:
137
- crit = crit_map.get(v["criterion_id"])
138
- title = crit.title if crit else v["criterion_id"]
139
- mb = mandatory_badge(crit.mandatory if crit else True)
140
- cat = category_badge(crit.category if crit else "compliance")
141
- extracted = v.get("extracted_value") or ""
142
- src = v.get("source") or {}
143
-
144
- src_html = '<span style="color:var(--text-color);opacity:0.3;">—</span>'
145
- if src:
146
- tier = ocr_tier_badge(src.get("source_type", "text_pdf"))
147
- src_html = (
148
- f'<span style="font-family:monospace;font-size:0.78rem;'
149
- f'background:rgba(128,128,128,0.1);padding:2px 5px;border-radius:4px;'
150
- f'border:1px solid rgba(128,128,128,0.2);'
151
- f'color:var(--text-color);">{src.get("doc_name","")}</span>'
152
- f' <span style="font-size:0.75rem;color:var(--text-color);opacity:0.5;">'
153
- f'p{src.get("page","")}</span>'
154
- f'<br><div style="margin-top:4px;">{tier}</div>'
155
- )
156
-
157
- extracted_cell = (
158
- f'<span style="font-size:0.84rem;color:var(--text-color);">{extracted}</span>'
159
- if extracted else
160
- '<span style="color:var(--text-color);opacity:0.3;">—</span>'
161
- )
162
-
163
  st.markdown(
164
- f'<div style="display:grid;grid-template-columns:2.5fr 1.6fr 1.8fr 2.2fr 1.4fr;'
165
- f'gap:8px;padding:10px 2px;align-items:start;">'
166
- f'<div>{mb}<div style="font-size:0.85rem;font-weight:600;'
167
- f'color:var(--text-color);margin-top:5px;">'
168
- f'{v["criterion_id"]}: {title}</div></div>'
169
- f'<div style="padding-top:2px;">{verdict_pill(v["verdict"])}</div>'
170
- f'<div style="padding-top:4px;">{extracted_cell}</div>'
171
- f'<div style="font-size:0.82rem;">{src_html}</div>'
172
- f'<div style="padding-top:2px;">{cat}</div>'
173
- f'</div>',
174
  unsafe_allow_html=True,
175
  )
176
- confidence_bar(v.get("combined_confidence", 0.0))
177
-
178
- reason = v.get("reason", "")
179
- snippet = (v.get("source") or {}).get("snippet", "")
180
- if reason or snippet:
181
- with st.expander("View reasoning & evidence", expanded=False):
182
- if reason:
183
- st.markdown(
184
- f'<div style="background:rgba(37,99,235,0.08);'
185
- f'border-left:3px solid #3B82F6;padding:10px 14px;'
186
- f'border-radius:0 8px 8px 0;font-size:0.875rem;'
187
- f'color:var(--text-color);"><strong>Reason:</strong> {reason}</div>',
188
- unsafe_allow_html=True,
189
- )
190
- if snippet:
191
- st.markdown(
192
- f'<div style="background:rgba(245,158,11,0.08);'
193
- f'border-left:3px solid #F59E0B;padding:10px 14px;'
194
- f'border-radius:0 8px 8px 0;margin-top:8px;font-size:0.84rem;'
195
- f'color:var(--text-color);font-style:italic;">'
196
- f'&ldquo;{snippet}&rdquo;</div>',
197
- unsafe_allow_html=True,
198
- )
199
 
200
- st.markdown(
201
- '<hr style="margin:2px 0;border:none;'
202
- 'border-top:1px solid rgba(128,128,128,0.1);">',
203
- unsafe_allow_html=True,
204
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  unsafe_allow_html=True,
119
  )
120
 
121
+ with st.expander(f"View all {len(verdicts)} criteria", expanded=False):
122
+ # Column headers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  st.markdown(
124
+ '<div style="display:grid;grid-template-columns:2.5fr 1.6fr 1.8fr 2.2fr 1.4fr;'
125
+ 'gap:8px;padding:6px 2px;border-top:1px solid rgba(128,128,128,0.15);'
126
+ 'border-bottom:1px solid rgba(128,128,128,0.15);margin-bottom:4px;">'
127
+ + "".join(
128
+ f'<div style="font-size:0.68rem;font-weight:700;color:var(--text-color);'
129
+ f'opacity:0.4;text-transform:uppercase;letter-spacing:0.07em;">{h}</div>'
130
+ for h in ["Criterion", "Verdict", "Extracted Value", "Source & Tier", "Category"]
131
+ )
132
+ + "</div>",
 
133
  unsafe_allow_html=True,
134
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
+ for v in verdicts:
137
+ crit = crit_map.get(v["criterion_id"])
138
+ title = crit.title if crit else v["criterion_id"]
139
+ mb = mandatory_badge(crit.mandatory if crit else True)
140
+ cat = category_badge(crit.category if crit else "compliance")
141
+ extracted = v.get("extracted_value") or ""
142
+ src = v.get("source") or {}
143
+
144
+ src_html = '<span style="color:var(--text-color);opacity:0.3;">—</span>'
145
+ if src:
146
+ tier = ocr_tier_badge(src.get("source_type", "text_pdf"))
147
+ src_html = (
148
+ f'<span style="font-family:monospace;font-size:0.78rem;'
149
+ f'background:rgba(128,128,128,0.1);padding:2px 5px;border-radius:4px;'
150
+ f'border:1px solid rgba(128,128,128,0.2);color:var(--text-color);">'
151
+ f'{src.get("doc_name","")}</span>'
152
+ f' <span style="font-size:0.75rem;color:var(--text-color);opacity:0.5;">'
153
+ f'p{src.get("page","")}</span>'
154
+ f'<br><div style="margin-top:4px;">{tier}</div>'
155
+ )
156
+
157
+ extracted_cell = (
158
+ f'<span style="font-size:0.84rem;color:var(--text-color);">{extracted}</span>'
159
+ if extracted else
160
+ '<span style="color:var(--text-color);opacity:0.3;">—</span>'
161
+ )
162
+
163
+ st.markdown(
164
+ f'<div style="display:grid;'
165
+ f'grid-template-columns:2.5fr 1.6fr 1.8fr 2.2fr 1.4fr;'
166
+ f'gap:8px;padding:10px 2px;align-items:start;">'
167
+ f'<div>{mb}<div style="font-size:0.85rem;font-weight:600;'
168
+ f'color:var(--text-color);margin-top:5px;">'
169
+ f'{v["criterion_id"]}: {title}</div></div>'
170
+ f'<div style="padding-top:2px;">{verdict_pill(v["verdict"])}</div>'
171
+ f'<div style="padding-top:4px;">{extracted_cell}</div>'
172
+ f'<div style="font-size:0.82rem;">{src_html}</div>'
173
+ f'<div style="padding-top:2px;">{cat}</div>'
174
+ f'</div>',
175
+ unsafe_allow_html=True,
176
+ )
177
+ confidence_bar(v.get("combined_confidence", 0.0))
178
+
179
+ reason = v.get("reason", "")
180
+ snippet = (v.get("source") or {}).get("snippet", "")
181
+ if reason or snippet:
182
+ with st.expander("View reasoning & evidence", expanded=False):
183
+ if reason:
184
+ st.markdown(
185
+ f'<div style="background:rgba(37,99,235,0.08);'
186
+ f'border-left:3px solid #3B82F6;padding:10px 14px;'
187
+ f'border-radius:0 8px 8px 0;font-size:0.875rem;'
188
+ f'color:var(--text-color);">'
189
+ f'<strong>Reason:</strong> {reason}</div>',
190
+ unsafe_allow_html=True,
191
+ )
192
+ if snippet:
193
+ st.markdown(
194
+ f'<div style="background:rgba(245,158,11,0.08);'
195
+ f'border-left:3px solid #F59E0B;padding:10px 14px;'
196
+ f'border-radius:0 8px 8px 0;margin-top:8px;'
197
+ f'font-size:0.84rem;color:var(--text-color);font-style:italic;">'
198
+ f'&ldquo;{snippet}&rdquo;</div>',
199
+ unsafe_allow_html=True,
200
+ )
201
+
202
+ st.markdown(
203
+ '<hr style="margin:2px 0;border:none;'
204
+ 'border-top:1px solid rgba(128,128,128,0.1);">',
205
+ unsafe_allow_html=True,
206
+ )
ui/tab_tender.py CHANGED
@@ -78,11 +78,7 @@ def render() -> None:
78
  if st.session_state.get("fallback_active"):
79
  st.warning("⚠ Live API unavailable — showing pre-computed criteria.")
80
 
81
- st.markdown(
82
- f'<div style="font-size:0.9rem;font-weight:700;color:#0D1B2A;margin:1rem 0 0.5rem;">'
83
- f'Extracted {len(criteria_data)} criteria</div>',
84
- unsafe_allow_html=True,
85
- )
86
 
87
  for c in criteria_data:
88
  icon = _CAT_ICONS.get(c["category"], "⚪")
@@ -95,17 +91,25 @@ def render() -> None:
95
  rule_str += f' {rule["unit"]}'
96
  rule_str += "`"
97
 
98
- with st.expander(
99
- f'{icon} **{c["id"]}** — {c["title"]} · {mand_lbl}',
100
- expanded=False,
101
- ):
102
- col1, col2 = st.columns([2, 1])
103
- with col1:
104
- st.markdown(f"**Description:** {c['description']}")
105
- st.markdown(f"**Rule:** {rule_str}")
106
- if c.get("query_hints"):
107
- hints = " · ".join(f"`{h}`" for h in c["query_hints"])
108
- st.markdown(f"**Query hints:** {hints}")
109
- with col2:
110
- st.markdown(f"**Category:** {c['category'].capitalize()}")
111
- st.markdown(f"**Source:** Page {c['source_page']}, Clause `{c['source_clause']}`")
 
 
 
 
 
 
 
 
 
78
  if st.session_state.get("fallback_active"):
79
  st.warning("⚠ Live API unavailable — showing pre-computed criteria.")
80
 
81
+ st.caption(f"{len(criteria_data)} criteria — click × to remove any you don't want evaluated.")
 
 
 
 
82
 
83
  for c in criteria_data:
84
  icon = _CAT_ICONS.get(c["category"], "⚪")
 
91
  rule_str += f' {rule["unit"]}'
92
  rule_str += "`"
93
 
94
+ btn_col, exp_col = st.columns([0.04, 0.96])
95
+ with btn_col:
96
+ if st.button("×", key=f"rm_{c['id']}", help=f"Remove {c['id']}"):
97
+ st.session_state["criteria"] = [
98
+ x for x in criteria_data if x["id"] != c["id"]
99
+ ]
100
+ st.rerun()
101
+ with exp_col:
102
+ with st.expander(
103
+ f'{icon} **{c["id"]}** {c["title"]} · {mand_lbl}',
104
+ expanded=False,
105
+ ):
106
+ col1, col2 = st.columns([2, 1])
107
+ with col1:
108
+ st.markdown(f"**Description:** {c['description']}")
109
+ st.markdown(f"**Rule:** {rule_str}")
110
+ if c.get("query_hints"):
111
+ hints = " · ".join(f"`{h}`" for h in c["query_hints"])
112
+ st.markdown(f"**Query hints:** {hints}")
113
+ with col2:
114
+ st.markdown(f"**Category:** {c['category'].capitalize()}")
115
+ st.markdown(f"**Source:** Page {c['source_page']}, Clause `{c['source_clause']}`")