Skip to main content

web/pages/commodity/
list.rs

1use std::sync::Arc;
2
3use askama::Template;
4use axum::{
5    Extension, Json,
6    extract::State,
7    http::{HeaderMap, StatusCode, header},
8    response::IntoResponse,
9};
10use serde::Serialize;
11use server::command::{CmdResult, FinanceEntity, commodity::ListCommodities};
12use sqlx::types::Uuid;
13
14use crate::{AppState, jwt_auth::JWTAuthMiddleware, pages::HtmlTemplate};
15
16#[derive(Template)]
17#[template(path = "pages/commodity/list.html")]
18struct CommodityListPage;
19
20pub async fn commodity_list_page() -> impl IntoResponse {
21    let template = CommodityListPage {};
22    HtmlTemplate(template)
23}
24
25struct CommodityView {
26    id: Uuid,
27    symbol: String,
28    name: String,
29}
30
31#[derive(Serialize)]
32struct CommodityJson {
33    id: String,
34    symbol: String,
35    name: String,
36}
37
38#[derive(Template)]
39#[template(path = "components/commodity/table.html")]
40struct CommodityTableTemplate {
41    commodities: Vec<CommodityView>,
42}
43
44pub async fn commodity_table(
45    State(_data): State<Arc<AppState>>,
46    Extension(jwt_auth): Extension<JWTAuthMiddleware>,
47    headers: HeaderMap,
48) -> Result<impl IntoResponse, StatusCode> {
49    let result = ListCommodities::new()
50        .user_id(jwt_auth.user.id)
51        .run()
52        .await
53        .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}