1use crate::model::Task;
2
3/// Columns the task table can be sorted by.
4#[derive(Clone, Copy, PartialEq, Eq)]
5pub(in crate::cmd::webui) enum SortField {
6 Id,
7 Status,
8 Type,
9 Priority,
10 Effort,
11 Title,
12 Created,
13}
14
15impl SortField {
16 pub(in crate::cmd::webui) fn parse(s: &str) -> Option<Self> {
17 match s {
18 "id" => Some(Self::Id),
19 "status" => Some(Self::Status),
20 "type" => Some(Self::Type),
21 "priority" => Some(Self::Priority),
22 "effort" => Some(Self::Effort),
23 "title" => Some(Self::Title),
24 "created" => Some(Self::Created),
25 _ => None,
26 }
27 }
28
29 pub(in crate::cmd::webui) fn as_str(self) -> &'static str {
30 match self {
31 Self::Id => "id",
32 Self::Status => "status",
33 Self::Type => "type",
34 Self::Priority => "priority",
35 Self::Effort => "effort",
36 Self::Title => "title",
37 Self::Created => "created",
38 }
39 }
40
41 /// Sensible default direction when the user first clicks a column.
42 pub(in crate::cmd::webui) fn default_order(self) -> SortOrder {
43 match self {
44 // Newest first, alphabetical ascending for text fields.
45 Self::Created => SortOrder::Desc,
46 Self::Title | Self::Id | Self::Type => SortOrder::Asc,
47 // Highest priority/effort first; open before closed.
48 Self::Priority | Self::Effort | Self::Status => SortOrder::Asc,
49 }
50 }
51}
52
53#[derive(Clone, Copy, PartialEq, Eq)]
54pub(in crate::cmd::webui) enum SortOrder {
55 Asc,
56 Desc,
57}
58
59impl SortOrder {
60 pub(in crate::cmd::webui) fn parse(s: &str) -> Option<Self> {
61 match s {
62 "asc" => Some(Self::Asc),
63 "desc" => Some(Self::Desc),
64 _ => None,
65 }
66 }
67
68 pub(in crate::cmd::webui) fn as_str(self) -> &'static str {
69 match self {
70 Self::Asc => "asc",
71 Self::Desc => "desc",
72 }
73 }
74}
75
76/// Map a `Status` to a numeric value for semantic sorting.
77/// Lower values sort first in ascending order: open → in_progress → closed.
78fn status_sort_key(s: crate::model::Status) -> i32 {
79 match s {
80 crate::model::Status::Open => 1,
81 crate::model::Status::InProgress => 2,
82 crate::model::Status::Closed => 3,
83 }
84}
85
86/// Apply the chosen sort field and direction to a filtered task list.
87pub(in crate::cmd::webui) fn sort_tasks(tasks: &mut [&Task], field: SortField, order: SortOrder) {
88 tasks.sort_by(|a, b| {
89 let cmp = match field {
90 SortField::Id => a.id.as_str().cmp(b.id.as_str()),
91 SortField::Status => status_sort_key(a.status).cmp(&status_sort_key(b.status)),
92 SortField::Type => a.task_type.cmp(&b.task_type),
93 SortField::Priority => a.priority.score().cmp(&b.priority.score()),
94 SortField::Effort => a.effort.score().cmp(&b.effort.score()),
95 SortField::Title => a
96 .title
97 .to_ascii_lowercase()
98 .cmp(&b.title.to_ascii_lowercase()),
99 SortField::Created => a.created_at.cmp(&b.created_at),
100 };
101 match order {
102 SortOrder::Asc => cmp,
103 SortOrder::Desc => cmp.reverse(),
104 }
105 });
106}