1
use std::sync::Arc;
2

            
3
use askama::Template;
4
use axum::{
5
    Extension, Json,
6
    extract::State,
7
    http::{HeaderMap, StatusCode, header},
8
    response::IntoResponse,
9
};
10
use serde::Serialize;
11
use server::command::{CmdResult, FinanceEntity, commodity::ListCommodities};
12
use sqlx::types::Uuid;
13

            
14
use crate::{AppState, jwt_auth::JWTAuthMiddleware, pages::HtmlTemplate};
15

            
16
#[derive(Template)]
17
#[template(path = "pages/commodity/list.html")]
18
struct CommodityListPage;
19

            
20
pub async fn commodity_list_page() -> impl IntoResponse {
21
    let template = CommodityListPage {};
22
    HtmlTemplate(template)
23
}
24

            
25
struct CommodityView {
26
    id: Uuid,
27
    symbol: String,
28
    name: String,
29
}
30

            
31
#[derive(Serialize)]
32
struct CommodityJson {
33
    id: String,
34
    symbol: String,
35
    name: String,
36
}
37

            
38
#[derive(Template)]
39
#[template(path = "components/commodity/table.html")]
40
struct CommodityTableTemplate {
41
    commodities: Vec<CommodityView>,
42
}
43

            
44
2
pub async fn commodity_table(
45
2
    State(_data): State<Arc<AppState>>,
46
2
    Extension(jwt_auth): Extension<JWTAuthMiddleware>,
47
2
    headers: HeaderMap,
48
2
) -> Result<impl IntoResponse, StatusCode> {
49
2
    let result = ListCommodities::new()
50
2
        .user_id(jwt_auth.user.id)
51
2
        .run()
52
2
        .await
53
2
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
54
    let mut commodities = Vec::new();
55
    if let Some(CmdResult::TaggedEntities { entities, .. }) = result {
56
        for (entity, tags) in entities {
57
            if let FinanceEntity::Commodity(commodity) = entity {
58
                // Find symbol and name tags
59
                let (symbol, name) = if let (FinanceEntity::Tag(s), FinanceEntity::Tag(n)) =
60
                    (&tags["symbol"], &tags["name"])
61
                {
62
                    (s.tag_value.clone(), n.tag_value.clone())
63
                } else {
64
                    return Err(StatusCode::INTERNAL_SERVER_ERROR);
65
                };
66

            
67
                commodities.push(CommodityView {
68
                    id: commodity.id,
69
                    symbol,
70
                    name,
71
                });
72
            }
73
        }
74
    }
75

            
76
    let wants_json = headers
77
        .get(header::ACCEPT)
78
        .and_then(|value| value.to_str().ok())
79
        .is_some_and(|value| value.contains("application/json"));
80

            
81
    if wants_json {
82
        let commodities_json: Vec<CommodityJson> = commodities
83
            .iter()
84
            .map(|c| CommodityJson {
85
                id: c.id.to_string(),
86
                symbol: c.symbol.clone(),
87
                name: c.name.clone(),
88
            })
89
            .collect();
90
        return Ok(Json(commodities_json).into_response());
91
    }
92

            
93
    Ok(HtmlTemplate(CommodityTableTemplate { commodities }).into_response())
94
2
}