use super::*; impl AppRoot { pub(super) fn render_approvals(&self, cx: &mut Context) -> impl IntoElement + '_ { let selected_pending_id = self .state .selected_approval_id .clone() .filter(|approval_id| { pending_approvals(&self.snapshot).any(|item| &item.id == approval_id) }); let selected_pending = selected_pending_id.as_ref().and_then(|approval_id| { self.snapshot .approvals .iter() .find(|item| &item.id == approval_id) }); div() .flex() .flex_col() .gap_10() .child( approvals_section( "Agent reviews in progress", self.snapshot.tasks.iter().filter(|task| task.status == "in_review"), ), ) .child( div() .gap_5() .child( div() .text_2xl() .text_color(theme::text_primary()) .child("Pending approvals"), ) .child(div().h(px(1.0)).bg(theme::panel_border())) .when(self.snapshot.pending_approvals() == 0, |container| { container.child( div() .min_h(px(280.0)) .flex() .items_center() .justify_center() .child( div() .w(px(520.0)) .text_center() .child( div() .text_xl() .text_color(theme::text_primary()) .child("No pending approvals"), ) .child( div() .mt(px(10.0)) .text_base() .text_color(theme::text_muted()) .child( "Approval requests from agents will appear here when they need your sign-off.", ), ), ), ) }) .when(self.snapshot.pending_approvals() > 0, |container| { container.child( div() .id("approvals-pending-list") .border_1() .rounded_lg() .border_color(theme::panel_border()) .bg(theme::panel()) .children(pending_approvals(&self.snapshot).map(|item| { let active = selected_pending_id.as_deref() == Some(item.id.as_str()); let approval_id = item.id.clone(); div() .id(SharedString::from(format!("approval-{}", item.id))) .px_5() .py_4() .border_b_1() .border_color(theme::panel_border()) .cursor_pointer() .bg(if active { theme::selection_fill() } else { theme::panel() }) .hover(|style| style.bg(theme::panel_hover())) .on_click(cx.listener(move |this, _, _, cx| { this.state.selected_approval_id = Some(approval_id.clone()); cx.notify(); })) .child( div() .text_base() .text_color(theme::text_primary()) .child(item.payload_summary.clone()), ) .child( div() .mt(px(6.0)) .text_sm() .text_color(theme::text_muted()) .child(format!( "{} · {}", item.approval_type.replace('_', " "), item.state )), ) })), ) }) .when_some(selected_pending, |container, approval| { container.child( div() .mt(px(8.0)) .p_5() .rounded_lg() .border_1() .border_color(theme::panel_border()) .bg(theme::panel()) .child( div() .text_xs() .text_color(theme::text_muted()) .child(approval.approval_type.replace('_', " ")), ) .child( div() .mt(px(10.0)) .text_lg() .text_color(theme::text_primary()) .child(approval.payload_summary.clone()), ) .child( div() .mt(px(10.0)) .text_base() .text_color(theme::text_secondary()) .child(approval.impact_summary.clone()), ) .child( div() .mt(px(18.0)) .flex() .gap_3() .child(primary_button("Approve", cx, |this, cx| { this.decide_selected_approval( ApprovalState::Approved, "Approved from native shell.", cx, ) })) .child(secondary_button("Reject", cx, |this, cx| { this.decide_selected_approval( ApprovalState::Rejected, "Rejected from native shell.", cx, ) })), ), ) }), ) } } fn approvals_section<'a>( title: &'static str, tasks: impl Iterator, ) -> impl IntoElement { let rows = tasks.collect::>(); div() .gap_5() .child( div() .text_2xl() .text_color(theme::text_primary()) .child(title), ) .child(div().h(px(1.0)).bg(theme::panel_border())) .when(rows.is_empty(), |container| { container.child( div() .py_8() .text_base() .text_color(theme::text_muted()) .child("No agent reviews are currently waiting on a human."), ) }) .when(!rows.is_empty(), |container| { container.child( div() .border_1() .rounded_lg() .border_color(theme::panel_border()) .bg(theme::panel()) .children(rows.into_iter().map(|task| { div() .px_5() .py_4() .border_b_1() .border_color(theme::panel_border()) .child( div() .text_base() .text_color(theme::text_primary()) .truncate() .child(task.title.clone()), ) .child( div() .mt(px(6.0)) .text_sm() .text_color(theme::text_muted()) .child(format!( "{} · awaiting review", task.assignee_agent_id .clone() .unwrap_or_else(|| "CEO".to_string()) )), ) })), ) }) } fn pending_approvals( snapshot: &domain::ProfileSnapshot, ) -> impl Iterator { snapshot .approvals .iter() .filter(|item| item.state == "pending" || item.state == "revision_requested") }