diff --git a/TODO.md b/TODO.md index a62125a5..658ab92f 100644 --- a/TODO.md +++ b/TODO.md @@ -13,10 +13,11 @@ - server-wide stats page has no PMC/Scav raid breakdown (per-user profile already tracks both) - restarting a headless client from the main headless page causes you to end up at that headless' info page - headless client start/stop/restart buttons on `/quma/headless` go past the card length -- currency items (USD, EUR) displayed as roubles instead of as currency balances (Stash) - typo in update changelog: versions url should use `/#versions` hash anchor, not `/versions` path segment (current URL 404s on Forge) - no auto-refresh when scaling/converging clients - typo in update changelog: versions url should be like this `https://forge.sp-tarkov.com/mod/2310/wtt-commonlib/#versions` (`#version` is the important part) +- mod queue allows duplicate pending operations (operations on mods should be exlcusive in the queue) +- spt server profile creation is broken on user signup ## Convoy - user config file sync @@ -27,6 +28,7 @@ ## Core Architecture - `WebError` always returns HTML even for API endpoints (`error.rs`) - blocking filesystem reads on async runtime (partially fixed — `svm::save_section` uses `web::block`, many others don't) +- standardize all overlays ontop of host overlayfs instead of podman managed ## Headless Client - too-many-arguments on convergence functions (clippy lint suppressed) @@ -40,6 +42,7 @@ - headless client actions - container cpu stats don't work (`cpu_percent` is never populated — always `None`; memory stats work) - `ensure_fika_headless` writes to base headless dir — should move to `mod_overlay()` for proper layering +- headless client numa scheduling section has shitty formatting ## Robustness - no mutual exclusion on server start/stop/restart (`server.rs`) diff --git a/src/assets/style.css b/src/assets/style.css index f9346b98..66798ffc 100644 --- a/src/assets/style.css +++ b/src/assets/style.css @@ -355,7 +355,7 @@ button.htmx-request .icon { animation: spin 0.8s linear infinite; } .tab-bar .tab:hover { color: var(--text); } .profile-card { line-height: 1.5; } -.action-buttons { display: flex; gap: 0.25rem; flex-wrap: wrap; } +.action-buttons { display: flex; gap: 0.25rem; flex-wrap: wrap; min-width: 10rem; } .reset-link-display { display: flex; gap: 0.25rem; align-items: center; } .reset-link-display input { font-family: monospace; font-size: 0.85rem; } diff --git a/src/web/handlers/clients.rs b/src/web/handlers/clients.rs index b6f5b9d4..1b67d159 100644 --- a/src/web/handlers/clients.rs +++ b/src/web/handlers/clients.rs @@ -380,7 +380,7 @@ pub async fn client_restart( return Ok(HttpResponse::NoContent().finish()); } Ok(HttpResponse::SeeOther() - .insert_header(("Location", format!("/quma/headless/{index}"))) + .insert_header(("Location", "/quma/headless")) .finish()) } diff --git a/src/web/handlers/profiles.rs b/src/web/handlers/profiles.rs index 6ad7d95e..096d622d 100644 --- a/src/web/handlers/profiles.rs +++ b/src/web/handlers/profiles.rs @@ -47,6 +47,7 @@ struct StashItemDisplay { short_name: String, count: i64, total_value: i64, + currency_type: Option, // "RUB", "USD", or "EUR" for currency items } struct StashCategoryDisplay { @@ -54,6 +55,10 @@ struct StashCategoryDisplay { items: Vec, total_value: i64, item_count: usize, + // Per-currency subtotals (only populated for Money category) + rub_total: i64, + usd_total: i64, + eur_total: i64, } #[derive(Template)] @@ -478,6 +483,14 @@ pub async fn stash_partial( let unit_price = state.game_data.item_price(tpl).unwrap_or(0); let total_value = unit_price.saturating_mul(*count); + // ponytail: currency type detection by template ID + let currency_type = match tpl.as_str() { + "5449016a4bdc2d6f08b456f6" => Some("RUB".to_string()), + "5696686a4bdc2da3298b456a" => Some("USD".to_string()), + "569668774bdc2da2298b4568" => Some("EUR".to_string()), + _ => None, + }; + all_categories_set.insert(category.clone()); if !search_lower.is_empty() @@ -499,6 +512,7 @@ pub async fn stash_partial( short_name, count: *count, total_value, + currency_type, }); } @@ -535,17 +549,41 @@ pub async fn stash_partial( } } - // Build sorted category list + // Build sorted category list with per-currency subtotals let mut categories: Vec = items_by_category .into_iter() .map(|(name, items)| { let total_value = items.iter().map(|i| i.total_value).sum(); let item_count = items.len(); + // ponytail: only calculate currency subtotals for Money category + let (rub_total, usd_total, eur_total) = if name == "Money" { + let rub = items + .iter() + .filter(|i| i.currency_type.as_deref() == Some("RUB")) + .map(|i| i.count) + .sum(); + let usd = items + .iter() + .filter(|i| i.currency_type.as_deref() == Some("USD")) + .map(|i| i.count) + .sum(); + let eur = items + .iter() + .filter(|i| i.currency_type.as_deref() == Some("EUR")) + .map(|i| i.count) + .sum(); + (rub, usd, eur) + } else { + (0, 0, 0) + }; StashCategoryDisplay { name, items, total_value, item_count, + rub_total, + usd_total, + eur_total, } }) .collect(); diff --git a/src/web/template_filters.rs b/src/web/template_filters.rs index acf4d59e..2a83b0d5 100644 --- a/src/web/template_filters.rs +++ b/src/web/template_filters.rs @@ -130,6 +130,11 @@ pub fn format_roubles_i64(value: &i64, _env: &dyn askama::Values) -> askama::Res Ok(format!("₽{}", format_roubles_value(*value))) } +#[askama::filter_fn] +pub fn format_number(value: &i64, _env: &dyn askama::Values) -> askama::Result { + Ok(format_roubles_value(*value)) +} + /// Truncate a datetime string to just the date+time portion (first 19 chars: `YYYY-MM-DD HH:MM:SS`). #[askama::filter_fn] pub fn format_datetime(s: &str, _env: &dyn askama::Values) -> askama::Result { diff --git a/templates/mods/partials/updates_carousel.html b/templates/mods/partials/updates_carousel.html index 79e149d5..72537aa5 100644 --- a/templates/mods/partials/updates_carousel.html +++ b/templates/mods/partials/updates_carousel.html @@ -30,7 +30,7 @@

Updates Available ({{ total }})

{{ e.current_version }} → {{ e.new_version }} {% if let Some(slug) = &e.slug %} - changelog ↗ + changelog ↗ {% endif %} diff --git a/templates/profiles/partials/stash.html b/templates/profiles/partials/stash.html index 079cc2f3..d56129dc 100644 --- a/templates/profiles/partials/stash.html +++ b/templates/profiles/partials/stash.html @@ -40,7 +40,7 @@ {% for cat in categories %}
- {{ cat.name }} ({{ cat.item_count }} items · {{ cat.total_value|format_roubles_i64 }}) + {{ cat.name }} ({{ cat.item_count }} items{% if cat.name == "Money" %}{% if cat.rub_total > 0 %} · ₽ {{ cat.rub_total|format_number }}{% endif %}{% if cat.usd_total > 0 %} · $ {{ cat.usd_total|format_number }}{% endif %}{% if cat.eur_total > 0 %} · € {{ cat.eur_total|format_number }}{% endif %}{% else %} · {{ cat.total_value|format_roubles_i64 }}{% endif %}) @@ -64,7 +64,7 @@ - + {% endfor %}
{{ item.short_name }} {% if item.count > 1 %}{{ item.count }}{% else %}1{% endif %}{{ item.total_value|format_roubles_i64 }}{% match item.currency_type %}{% when Some with (c) %}{% if c == "USD" %}${% else if c == "EUR" %}€{% else %}₽{% endif %} {{ item.count|format_number }}{% when None %}{{ item.total_value|format_roubles_i64 }}{% endmatch %}