fix: clarify panel ticket row hierarchy
This commit is contained in:
parent
79dda10da3
commit
f3b435e724
|
|
@ -5221,7 +5221,7 @@ const POD_STATUS_COLUMN_WIDTH: usize = 18;
|
|||
|
||||
fn panel_row_lines(row: &PanelRow, selected: bool, width: u16) -> Vec<Line<'static>> {
|
||||
if row.kind == PanelRowKind::TicketIntakePod {
|
||||
vec![panel_row_title_line(row, selected, width)]
|
||||
vec![panel_intake_child_line(row, selected, width)]
|
||||
} else {
|
||||
vec![
|
||||
panel_row_title_line(row, selected, width),
|
||||
|
|
@ -5241,7 +5241,7 @@ fn panel_row_title_line(row: &PanelRow, selected: bool, width: u16) -> Line<'sta
|
|||
let mut spans = Vec::new();
|
||||
let mut remaining = width as usize;
|
||||
|
||||
push_ticket_marker_span(&mut spans, selected, &mut remaining);
|
||||
push_ticket_primary_marker_span(&mut spans, selected, &mut remaining);
|
||||
push_column_span(
|
||||
&mut spans,
|
||||
&row.status,
|
||||
|
|
@ -5254,11 +5254,41 @@ fn panel_row_title_line(row: &PanelRow, selected: bool, width: u16) -> Line<'sta
|
|||
Line::from(spans)
|
||||
}
|
||||
|
||||
fn panel_intake_child_line(row: &PanelRow, selected: bool, width: u16) -> Line<'static> {
|
||||
let title_style = if selected {
|
||||
Style::default()
|
||||
.fg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default().fg(Color::Cyan)
|
||||
};
|
||||
let mut spans = Vec::new();
|
||||
let mut remaining = width as usize;
|
||||
|
||||
push_intake_child_marker_span(&mut spans, selected, &mut remaining);
|
||||
push_column_span(
|
||||
&mut spans,
|
||||
&row.status,
|
||||
TICKET_STATE_COLUMN_WIDTH,
|
||||
intake_status_style(&row.status),
|
||||
&mut remaining,
|
||||
);
|
||||
push_bounded_span(&mut spans, row.title.as_str(), title_style, &mut remaining);
|
||||
|
||||
Line::from(spans)
|
||||
}
|
||||
|
||||
fn panel_row_detail_line(row: &PanelRow, selected: bool, width: u16) -> Line<'static> {
|
||||
let mut spans = Vec::new();
|
||||
let mut remaining = width as usize;
|
||||
|
||||
push_ticket_marker_span(&mut spans, selected, &mut remaining);
|
||||
push_ticket_detail_marker_span(&mut spans, selected, &mut remaining);
|
||||
push_bounded_span(
|
||||
&mut spans,
|
||||
"meta ",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
&mut remaining,
|
||||
);
|
||||
push_bounded_span(
|
||||
&mut spans,
|
||||
&panel_ticket_detail(row),
|
||||
|
|
@ -5269,10 +5299,14 @@ fn panel_row_detail_line(row: &PanelRow, selected: bool, width: u16) -> Line<'st
|
|||
Line::from(spans)
|
||||
}
|
||||
|
||||
fn push_ticket_marker_span(spans: &mut Vec<Span<'static>>, selected: bool, remaining: &mut usize) {
|
||||
fn push_ticket_primary_marker_span(
|
||||
spans: &mut Vec<Span<'static>>,
|
||||
selected: bool,
|
||||
remaining: &mut usize,
|
||||
) {
|
||||
let (marker, style) = if selected {
|
||||
(
|
||||
"| ",
|
||||
"▶ ",
|
||||
Style::default()
|
||||
.fg(Color::Magenta)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
|
|
@ -5283,6 +5317,42 @@ fn push_ticket_marker_span(spans: &mut Vec<Span<'static>>, selected: bool, remai
|
|||
push_bounded_span(spans, marker, style, remaining);
|
||||
}
|
||||
|
||||
fn push_ticket_detail_marker_span(
|
||||
spans: &mut Vec<Span<'static>>,
|
||||
selected: bool,
|
||||
remaining: &mut usize,
|
||||
) {
|
||||
let (marker, style) = if selected {
|
||||
(
|
||||
"│ ",
|
||||
Style::default()
|
||||
.fg(Color::Magenta)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)
|
||||
} else {
|
||||
(" ", Style::default().fg(Color::DarkGray))
|
||||
};
|
||||
push_bounded_span(spans, marker, style, remaining);
|
||||
}
|
||||
|
||||
fn push_intake_child_marker_span(
|
||||
spans: &mut Vec<Span<'static>>,
|
||||
selected: bool,
|
||||
remaining: &mut usize,
|
||||
) {
|
||||
let (marker, style) = if selected {
|
||||
(
|
||||
" ▶ ",
|
||||
Style::default()
|
||||
.fg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)
|
||||
} else {
|
||||
(" └ ", Style::default().fg(Color::DarkGray))
|
||||
};
|
||||
push_bounded_span(spans, marker, style, remaining);
|
||||
}
|
||||
|
||||
fn panel_ticket_detail(row: &PanelRow) -> String {
|
||||
if row.kind == PanelRowKind::InvalidTicket {
|
||||
let mut parts = vec![panel_ticket_reference(row), "Gate: unavailable".to_string()];
|
||||
|
|
@ -5420,6 +5490,15 @@ fn panel_priority_style(priority: ActionPriority) -> Style {
|
|||
}
|
||||
}
|
||||
|
||||
fn intake_status_style(status: &str) -> Style {
|
||||
match status {
|
||||
"live" => Style::default().fg(Color::Green),
|
||||
"restorable" => Style::default().fg(Color::Yellow),
|
||||
"stale" => Style::default().fg(Color::DarkGray),
|
||||
_ => Style::default().fg(Color::Cyan),
|
||||
}
|
||||
}
|
||||
|
||||
fn section_rows(
|
||||
list: &PodList,
|
||||
section: &MultiPodSection,
|
||||
|
|
@ -5553,6 +5632,34 @@ fn target_status_line(app: &MultiPodApp) -> Line<'static> {
|
|||
));
|
||||
}
|
||||
Line::from(spans)
|
||||
} else if let Some(row) = app
|
||||
.selected_panel_row()
|
||||
.filter(|row| row.kind == PanelRowKind::TicketIntakePod)
|
||||
{
|
||||
let ticket_id = panel_ticket_reference(row);
|
||||
let action = if row.next_action == Some(NextUserAction::OpenPod) {
|
||||
"open/attach"
|
||||
} else {
|
||||
"unavailable"
|
||||
};
|
||||
Line::from(vec![
|
||||
Span::styled("composer target ", Style::default().fg(Color::DarkGray)),
|
||||
Span::styled(
|
||||
app.composer_target().label(),
|
||||
Style::default()
|
||||
.fg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::styled(
|
||||
" · selected Intake Pod ",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
),
|
||||
Span::styled(row.status.clone(), intake_status_style(&row.status)),
|
||||
Span::styled(
|
||||
format!(" · Ticket {ticket_id} · blank Enter {action}"),
|
||||
Style::default().fg(Color::DarkGray),
|
||||
),
|
||||
])
|
||||
} else if let Some(entry) = app.selected_pod_entry() {
|
||||
let (status, status_style) = row_status_label(entry);
|
||||
Line::from(vec![
|
||||
|
|
@ -7398,9 +7505,8 @@ branch = "orchestration/custom-panel"
|
|||
let title_start = state_start + TICKET_STATE_COLUMN_WIDTH + 1;
|
||||
let row_id = row.ticket.as_ref().unwrap().id.as_str();
|
||||
|
||||
assert!(title_line.starts_with("| "));
|
||||
assert!(detail_line.starts_with("| "));
|
||||
assert!(!title_line.starts_with("▶"));
|
||||
assert!(title_line.starts_with("▶ "));
|
||||
assert!(detail_line.starts_with("│ meta "));
|
||||
assert!(!title_line.contains(row_id));
|
||||
assert_eq!(display_column(&title_line, "inprogress"), state_start);
|
||||
assert_eq!(
|
||||
|
|
@ -7430,7 +7536,7 @@ branch = "orchestration/custom-panel"
|
|||
let title_start = state_start + TICKET_STATE_COLUMN_WIDTH + 1;
|
||||
|
||||
assert!(title_line.starts_with(" ready"));
|
||||
assert!(detail_line.starts_with(" 00001KTTB479X"));
|
||||
assert!(detail_line.starts_with(" meta 00001KTTB479X"));
|
||||
assert_eq!(display_column(&title_line, "ready"), state_start);
|
||||
assert_eq!(
|
||||
display_column(&title_line, "Long Ticket title"),
|
||||
|
|
@ -7458,7 +7564,7 @@ branch = "orchestration/custom-panel"
|
|||
assert_eq!(display_column(&title_line, "Very long Ticket"), title_start);
|
||||
assert!(title_line.ends_with('…'));
|
||||
assert_eq!(detail_line.width(), 42);
|
||||
assert!(detail_line.starts_with(" 00001KTTB479X · Gate: clear"));
|
||||
assert!(detail_line.starts_with(" meta 00001KTTB479X · Gate: clear"));
|
||||
assert!(detail_line.ends_with('…'));
|
||||
}
|
||||
|
||||
|
|
@ -7483,6 +7589,67 @@ branch = "orchestration/custom-panel"
|
|||
assert!(detail_line.contains("Reason: Queue disabled: waiting for BLOCKER-1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panel_ticket_intake_child_rows_render_as_indented_single_line() {
|
||||
let row = panel_test_intake_child_row(
|
||||
"00001TICKET",
|
||||
"intake-live",
|
||||
TicketLocalClaimStatus::Live,
|
||||
Some(NextUserAction::OpenPod),
|
||||
);
|
||||
|
||||
let lines = panel_row_lines(&row, false, 160);
|
||||
assert_eq!(lines.len(), 1);
|
||||
let line = plain_line(&lines[0]);
|
||||
let status_start = 4;
|
||||
let title_start = status_start + TICKET_STATE_COLUMN_WIDTH + 1;
|
||||
|
||||
assert!(line.starts_with(" └ live"));
|
||||
assert_eq!(display_column(&line, "live"), status_start);
|
||||
assert_eq!(
|
||||
display_column(&line, "Intake Pod: intake-live"),
|
||||
title_start
|
||||
);
|
||||
assert!(!line.starts_with(" live"));
|
||||
|
||||
let selected_line = plain_line(&panel_row_lines(&row, true, 160)[0]);
|
||||
assert!(selected_line.starts_with(" ▶ live"));
|
||||
assert!(selected_line.contains("Intake Pod: intake-live"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selected_ticket_intake_child_status_is_not_rendered_as_generic_ticket_or_pod() {
|
||||
let ticket_id = "00001TICKET";
|
||||
let pod_name = "intake-live";
|
||||
let mut panel = WorkspacePanelViewModel::empty(Path::new("test"));
|
||||
panel.rows.push(panel_test_intake_child_row(
|
||||
ticket_id,
|
||||
pod_name,
|
||||
TicketLocalClaimStatus::Live,
|
||||
Some(NextUserAction::OpenPod),
|
||||
));
|
||||
let list = PodList::from_sources(
|
||||
PodVisibilitySource::ResumePicker,
|
||||
vec![],
|
||||
vec![live_info(pod_name, PodStatus::Idle)],
|
||||
None,
|
||||
10,
|
||||
);
|
||||
let mut app = app_with_panel(list, panel);
|
||||
app.select_panel_key(PanelRowKey::TicketIntakePod {
|
||||
ticket_id: ticket_id.to_string(),
|
||||
pod_name: pod_name.to_string(),
|
||||
});
|
||||
|
||||
let status = plain_line(&target_status_line(&app));
|
||||
|
||||
assert!(status.contains("selected Intake Pod live"));
|
||||
assert!(status.contains("Ticket 00001TICKET"));
|
||||
assert!(status.contains("blank Enter open/attach"));
|
||||
assert!(!status.contains("selected Ticket"));
|
||||
assert!(!status.contains("selected Pod live"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panel_pod_rows_use_aligned_columns_before_pod_name() {
|
||||
let app = test_app(vec![
|
||||
|
|
@ -8750,6 +8917,36 @@ branch = "orchestration/custom-panel"
|
|||
}
|
||||
}
|
||||
|
||||
fn panel_test_intake_child_row(
|
||||
ticket_id: &str,
|
||||
pod_name: &str,
|
||||
status: TicketLocalClaimStatus,
|
||||
next_action: Option<NextUserAction>,
|
||||
) -> PanelRow {
|
||||
PanelRow {
|
||||
key: PanelRowKey::TicketIntakePod {
|
||||
ticket_id: ticket_id.to_string(),
|
||||
pod_name: pod_name.to_string(),
|
||||
},
|
||||
kind: PanelRowKind::TicketIntakePod,
|
||||
title: format!("Intake Pod: {pod_name}"),
|
||||
subtitle: Some(format!("Intake claim for Ticket {ticket_id}")),
|
||||
status: status.label().to_string(),
|
||||
priority: match status {
|
||||
TicketLocalClaimStatus::Live | TicketLocalClaimStatus::Restorable => {
|
||||
ActionPriority::ActiveWork
|
||||
}
|
||||
TicketLocalClaimStatus::Stale => ActionPriority::Background,
|
||||
},
|
||||
next_action,
|
||||
ticket: None,
|
||||
related_pods: vec![pod_name.to_string()],
|
||||
disabled_reason: (status == TicketLocalClaimStatus::Stale)
|
||||
.then(|| "claim metadata is stale".to_string()),
|
||||
key_hint: Some(format!("Ticket {ticket_id} Intake Pod {pod_name}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn closed_list(count: usize, selected: Option<&str>) -> PodList {
|
||||
PodList::from_sources(
|
||||
PodVisibilitySource::ResumePicker,
|
||||
|
|
|
|||
|
|
@ -1039,7 +1039,7 @@ fn ticket_intake_pod_row(intake: &TicketAssociatedIntakeEntry) -> PanelRow {
|
|||
pod_name: intake.pod_name.clone(),
|
||||
},
|
||||
kind: PanelRowKind::TicketIntakePod,
|
||||
title: format!("↳ Intake Pod: {}", intake.pod_name),
|
||||
title: format!("Intake Pod: {}", intake.pod_name),
|
||||
subtitle: Some(format!(
|
||||
"Ticket {} · {} · {}",
|
||||
intake.ticket_id,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user