diff --git a/src/modes/server/pages.rs b/src/modes/server/pages.rs index 4095ddb..5ede524 100644 --- a/src/modes/server/pages.rs +++ b/src/modes/server/pages.rs @@ -7,6 +7,8 @@ use axum::{ }; use rusqlite::Connection; use serde::Deserialize; +use crate::config::ColumnConfig; +use std::collections::HashMap; #[derive(Deserialize)] pub struct ListQueryParams { @@ -39,8 +41,9 @@ async fn list_items( Query(params): Query, ) -> Result, Html> { let conn = state.db.lock().await; + let settings = &state.settings; - let result = build_item_list(&conn, ¶ms); + let result = build_item_list(&conn, ¶ms, &settings.list_format); match result { Ok(html) => Ok(Html(html)), @@ -48,7 +51,7 @@ async fn list_items( } } -fn build_item_list(conn: &Connection, params: &ListQueryParams) -> Result { +fn build_item_list(conn: &Connection, params: &ListQueryParams, columns: &[ColumnConfig]) -> Result { let tags: Vec = params.tags .as_ref() .map(|t| t.split(',').map(|s| s.trim().to_string()).collect()) @@ -77,40 +80,94 @@ fn build_item_list(conn: &Connection, params: &ListQueryParams) -> Result = page_items.iter().filter_map(|item| item.id).collect(); let tags_map = db::get_tags_for_items(conn, &item_ids)?; + let meta_map = db::get_meta_for_items(conn, &item_ids)?; let mut html = String::new(); - html.push_str("Items"); + html.push_str("Items"); + html.push_str(""); + html.push_str(""); html.push_str("

Items

"); html.push_str("

API Documentation

"); - html.push_str("
    "); + // Start table + html.push_str(""); + + // Table headers + html.push_str(""); + for column in columns { + html.push_str(&format!("", column.label)); + } + html.push_str(""); + html.push_str(""); + + // Table rows for item in page_items { let item_id = item.id.unwrap_or(0); - let tags_html = if let Some(tags) = tags_map.get(&item_id) { - let tag_names: Vec = tags.iter().map(|t| t.name.clone()).collect(); - if tag_names.is_empty() { - String::new() - } else { - format!(" [{}]", tag_names.join(", ")) - } - } else { - String::new() - }; + let tags = tags_map.get(&item_id).cloned().unwrap_or_default(); + let meta: HashMap = meta_map.get(&item_id) + .map(|metas| metas.iter().map(|m| (m.name.clone(), m.value.clone())).collect()) + .unwrap_or_default(); + html.push_str(""); + for column in columns { + let value = match column.name.as_str() { + "id" => item.id.map(|id| id.to_string()).unwrap_or_default(), + "time" => item.ts.format("%Y-%m-%d %H:%M:%S").to_string(), + "size" => item.size.map(|s| s.to_string()).unwrap_or_default(), + "tags" => tags.iter().map(|t| t.name.clone()).collect::>().join(", "), + _ => { + if column.name.starts_with("meta:") { + let meta_key = &column.name[5..]; + meta.get(meta_key).cloned().unwrap_or_default() + } else { + String::new() + } + } + }; + + // Apply max_len if specified + let display_value = if let Some(max_len_str) = &column.max_len { + if let Ok(max_len) = max_len_str.parse::() { + if value.chars().count() > max_len { + let truncated: String = value.chars().take(max_len).collect(); + format!("{}...", truncated) + } else { + value + } + } else { + value + } + } else { + value + }; + + // Apply alignment + let align_style = match column.align { + crate::config::ColumnAlignment::Left => "text-align: left;", + crate::config::ColumnAlignment::Right => "text-align: right;", + }; + + html.push_str(&format!("", align_style, display_value)); + } + + // Actions column html.push_str(&format!( - "
  • Item #{} - {}{} - Download
  • ", - item_id, - item_id, - item.ts.format("%Y-%m-%d %H:%M:%S"), - tags_html, - item_id + "", + item_id, item_id )); + + html.push_str(""); } - html.push_str(""); + html.push_str("
    {}Actions
    {}View | Download
    "); // Add pagination info html.push_str(&format!("

    Showing {} to {} of {} items

    ",