Sort user table by date

This commit is contained in:
euzu
2025-08-28 14:30:50 +02:00
parent b270f2253b
commit f30d2fa2e8
6 changed files with 120 additions and 16 deletions
+1
View File
@@ -259,6 +259,7 @@
"EPG": "Epg",
"PERSIST": "Persist",
"ALIASES": "Aliases",
"MAX_CON": "Max Con.",
"MAX_CONNECTIONS": "Max Connections",
"METHOD": "Method",
"EPG_TIMESHIFT": "Epg Timeshift",
+48
View File
@@ -0,0 +1,48 @@
use shared::model::{ProxyType, ProxyUserStatus};
pub enum CellValue<'a> {
Empty,
Bool(bool),
Status(ProxyUserStatus),
Proxy(ProxyType),
Text(&'a str),
Date(i64)
}
impl<'a> PartialOrd for CellValue<'a> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for CellValue<'a> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(CellValue::Empty, CellValue::Empty) => std::cmp::Ordering::Equal,
(CellValue::Bool(a), CellValue::Bool(b)) => a.cmp(b),
(CellValue::Status(a), CellValue::Status(b)) => a.cmp(b),
(CellValue::Proxy(a), CellValue::Proxy(b)) => a.cmp(b),
(CellValue::Text(a), CellValue::Text(b)) => a.cmp(b),
(CellValue::Date(a), CellValue::Date(b)) => a.cmp(b),
(CellValue::Empty, _) => std::cmp::Ordering::Less,
(CellValue::Bool(_), CellValue::Empty) => std::cmp::Ordering::Greater,
(CellValue::Bool(_), _) => std::cmp::Ordering::Less,
(CellValue::Status(_), CellValue::Empty | CellValue::Bool(_)) => std::cmp::Ordering::Greater,
(CellValue::Status(_), _) => std::cmp::Ordering::Less,
(CellValue::Proxy(_), CellValue::Empty | CellValue::Bool(_) | CellValue::Status(_)) => std::cmp::Ordering::Greater,
(CellValue::Proxy(_), _) => std::cmp::Ordering::Less,
(CellValue::Text(_), CellValue::Date(_)) => std::cmp::Ordering::Less,
(CellValue::Text(_), _) => std::cmp::Ordering::Greater,
(CellValue::Date(_), _) => std::cmp::Ordering::Greater,
}
}
}
impl<'a> PartialEq for CellValue<'a> {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == std::cmp::Ordering::Equal
}
}
impl<'a> Eq for CellValue<'a> {}
+2
View File
@@ -40,6 +40,7 @@ mod number_input;
mod date_input;
mod role_based_content;
mod api_user;
mod cell_value;
// pub use self::input::*;
// pub use self::menu_item::*;
// pub use self::popup_menu::*;
@@ -77,5 +78,6 @@ pub use self::accordion_panel::*;
pub use self::csv_table::*;
pub use self::tabset::*;
pub use self::role_based_content::*;
pub use self::cell_value::*;
//pub use self::number_input::*;
//pub use self::date_input::*;
+1 -1
View File
@@ -1,7 +1,7 @@
use std::rc::Rc;
use yew::prelude::*;
use crate::app::components::{NoContent, AppIcon};
use shared::model::SortOrder;
use shared::model::{SortOrder};
#[derive(Properties, PartialEq)]
pub struct TableDefinition<T: PartialEq + Clone> {
@@ -1,14 +1,11 @@
use std::borrow::Cow;
use crate::app::components::menu_item::MenuItem;
use crate::app::components::popup_menu::PopupMenu;
use crate::app::components::{convert_bool_to_chip_style, AppIcon, Chip, HideContent, MaxConnections,
ProxyTypeView, RevealContent, Table, TableDefinition, UserStatus,
UserlistContext, UserlistPage};
use crate::app::components::{convert_bool_to_chip_style, AppIcon, CellValue, Chip, HideContent, MaxConnections, ProxyTypeView, RevealContent, Table, TableDefinition, UserStatus, UserlistContext, UserlistPage};
use crate::app::context::TargetUser;
use crate::model::DialogResult;
use crate::services::DialogService;
use shared::error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind};
use shared::model::SortOrder;
use shared::model::{SortOrder};
use shared::utils::{unix_ts_to_str, Substring};
use std::fmt::Display;
use std::rc::Rc;
@@ -29,7 +26,7 @@ const HEADERS: [&str; 15] = [
"LABEL.TOKEN",
"LABEL.PROXY",
"LABEL.SERVER",
"LABEL.MAX_CONNECTIONS",
"LABEL.MAX_CON",
"LABEL.UI_ENABLED",
"LABEL.EPG_TIMESHIFT",
"LABEL.CREATED_AT",
@@ -37,20 +34,22 @@ const HEADERS: [&str; 15] = [
"LABEL.COMMENT",
];
fn get_cell_value(user: &TargetUser, col: usize) -> Cow<'_, str> {
fn get_cell_value(user: &TargetUser, col: usize) -> CellValue<'_> {
match col {
1 => Cow::Owned(user.credentials.is_active().to_string()),
2 => Cow::Owned(user.credentials.status.as_ref().map_or_else(String::new, ToString::to_string)),
3 => Cow::Borrowed(user.target.as_str()),
4 => Cow::Borrowed(user.credentials.username.as_str()),
7 => Cow::Owned(user.credentials.proxy.to_string()),
8 => Cow::Owned(user.credentials.server.as_ref().map_or_else(String::new, Clone::clone)),
_ => Cow::Owned(String::new())
1 => CellValue::Bool(user.credentials.is_active()),
2 => user.credentials.status.as_ref().map_or(CellValue::Empty, |s| CellValue::Status(*s)),
3 => CellValue::Text(user.target.as_str()),
4 => CellValue::Text(user.credentials.username.as_str()),
7 => CellValue::Proxy(user.credentials.proxy),
8 => user.credentials.server.as_ref().map_or(CellValue::Empty, |s|CellValue::Text(s)),
12 => user.credentials.created_at.as_ref().map_or(CellValue::Empty, |d| CellValue::Date(*d)),
13 => user.credentials.exp_date.as_ref().map_or(CellValue::Empty, |d| CellValue::Date(*d)),
_ => CellValue::Empty,
}
}
fn is_col_sortable(col: usize) -> bool {
matches!(col, 1 | 2 | 3 | 4 | 7 | 8)
matches!(col, 1 | 2 | 3 | 4 | 7 | 8 | 12 | 13)
}
+54
View File
@@ -1,3 +1,4 @@
use std::cmp::Ordering;
use crate::utils::default_as_true;
use std::fmt::Display;
use std::str::FromStr;
@@ -37,6 +38,28 @@ impl PartialEq for ProxyType {
impl Eq for ProxyType {}
impl Ord for ProxyType {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl PartialOrd for ProxyType {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
// Some(match (self, other) {
// (ProxyType::Redirect, ProxyType::Redirect) => std::cmp::Ordering::Equal,
// (ProxyType::Reverse(a), ProxyType::Reverse(b)) => {
// let a_flags = a.map_or(0u16, |f| if f.has_full_flags() { 0u16 } else { f.bits() });
// let b_flags = b.map_or(0u16, |f| if f.has_full_flags() { 0u16 } else { f.bits() });
// a_flags.cmp(&b_flags)
// }
// (ProxyType::Redirect, _) => std::cmp::Ordering::Less,
// (ProxyType::Reverse(_), _) => std::cmp::Ordering::Greater,
// })
}
}
impl Hash for ProxyType {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
@@ -207,6 +230,37 @@ impl FromStr for ProxyUserStatus {
}
}
impl Ord for ProxyUserStatus {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// Definiere numerische Werte für die Sortierreihenfolge
let self_value = match self {
ProxyUserStatus::Active => 0,
ProxyUserStatus::Trial => 1,
ProxyUserStatus::Pending => 2,
ProxyUserStatus::Disabled => 3,
ProxyUserStatus::Expired => 4,
ProxyUserStatus::Banned => 5,
};
let other_value = match other {
ProxyUserStatus::Active => 0,
ProxyUserStatus::Trial => 1,
ProxyUserStatus::Pending => 2,
ProxyUserStatus::Disabled => 3,
ProxyUserStatus::Expired => 4,
ProxyUserStatus::Banned => 5,
};
self_value.cmp(&other_value)
}
}
impl PartialOrd for ProxyUserStatus {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Default)]
#[serde(deny_unknown_fields)]
pub struct ProxyUserCredentialsDto {