From 4a5483442418d1074a97e1d2f12b2d2c01a2ebc2 Mon Sep 17 00:00:00 2001 From: Jon Seager Date: Thu, 5 Feb 2026 17:23:50 +0000 Subject: [PATCH] refactor(ui): extract reusable tab bar component - Create shared tab_bar.html partial with desktop + mobile layouts - Add CSS component classes (.tab, .tab-active, .tab-mobile variants) - Replace divergent implementations in data.html and add.html - Rename DataTab to Tab for shared use across templates - Improve readability: darker inactive text, more spacing, hover states --- src/application/routes/add.rs | 45 ++++++++++++++++++++++++++++- src/application/routes/data.rs | 29 +++++++++++-------- src/presentation/web/templates.rs | 15 ++++++++-- templates/add.html | 22 ++------------- templates/data.html | 47 ++----------------------------- templates/input.css | 46 ++++++++++++++++++++++++++++++ templates/partials/tab_bar.html | 43 ++++++++++++++++++++++++++++ 7 files changed, 168 insertions(+), 79 deletions(-) create mode 100644 templates/partials/tab_bar.html diff --git a/src/application/routes/add.rs b/src/application/routes/add.rs index 1e80758..2121579 100644 --- a/src/application/routes/add.rs +++ b/src/application/routes/add.rs @@ -9,10 +9,41 @@ use crate::application::routes::support::{ load_cafe_options, load_roast_options, load_roaster_options, }; use crate::application::server::AppState; -use crate::presentation::web::templates::AddTemplate; +use crate::presentation::web::templates::{AddTemplate, Tab}; use super::brews::load_brew_form_data; +const ADD_TABS: &[Tab] = &[ + Tab { + key: "roaster", + label: "Roaster", + }, + Tab { + key: "roast", + label: "Roast", + }, + Tab { + key: "bag", + label: "Bag", + }, + Tab { + key: "brew", + label: "Brew", + }, + Tab { + key: "gear", + label: "Gear", + }, + Tab { + key: "cafe", + label: "Cafe", + }, + Tab { + key: "cup", + label: "Cup", + }, +]; + #[derive(Debug, Deserialize)] pub(crate) struct AddQuery { #[serde(rename = "type", default = "default_type")] @@ -48,6 +79,18 @@ pub(crate) async fn add_page( is_authenticated, version_info: &crate::VERSION_INFO, active_type: query.entity_type, + tabs: ADD_TABS + .iter() + .map(|t| Tab { + key: t.key, + label: t.label, + }) + .collect(), + tab_signal: "_add-type", + tab_signal_js: "$_addType", + tab_base_url: "", + tab_fetch_target: "", + tab_fetch_mode: "", roaster_options, roast_options, bag_options: brew_form.bag_options, diff --git a/src/application/routes/data.rs b/src/application/routes/data.rs index 4d93668..d03646c 100644 --- a/src/application/routes/data.rs +++ b/src/application/routes/data.rs @@ -8,36 +8,36 @@ use crate::application::routes::render_html; use crate::application::routes::support::{ListQuery, is_datastar_request}; use crate::application::server::AppState; use crate::presentation::web::templates::{ - BagListTemplate, BrewListTemplate, CafeListTemplate, CupListTemplate, DataTab, DataTemplate, - GearListTemplate, RoastListTemplate, RoasterListTemplate, render_template, + BagListTemplate, BrewListTemplate, CafeListTemplate, CupListTemplate, DataTemplate, + GearListTemplate, RoastListTemplate, RoasterListTemplate, Tab, render_template, }; -const TABS: &[DataTab] = &[ - DataTab { +const TABS: &[Tab] = &[ + Tab { key: "brews", label: "Brews", }, - DataTab { + Tab { key: "roasters", label: "Roasters", }, - DataTab { + Tab { key: "roasts", label: "Roasts", }, - DataTab { + Tab { key: "bags", label: "Bags", }, - DataTab { + Tab { key: "gear", label: "Gear", }, - DataTab { + Tab { key: "cafes", label: "Cafes", }, - DataTab { + Tab { key: "cups", label: "Cups", }, @@ -84,9 +84,9 @@ pub(crate) async fn data_page( return Ok(response); } - let tabs: Vec = TABS + let tabs: Vec = TABS .iter() - .map(|t| DataTab { + .map(|t| Tab { key: t.key, label: t.label, }) @@ -98,6 +98,11 @@ pub(crate) async fn data_page( version_info: &crate::VERSION_INFO, active_type: entity_type, tabs, + tab_signal: "_active-tab", + tab_signal_js: "$_activeTab", + tab_base_url: "/data?type=", + tab_fetch_target: "#data-content", + tab_fetch_mode: "inner", content, search_value, }; diff --git a/src/presentation/web/templates.rs b/src/presentation/web/templates.rs index d0464b8..79daf9e 100644 --- a/src/presentation/web/templates.rs +++ b/src/presentation/web/templates.rs @@ -134,12 +134,17 @@ pub struct DataTemplate { pub is_authenticated: bool, pub version_info: &'static crate::VersionInfo, pub active_type: String, - pub tabs: Vec, + pub tabs: Vec, + pub tab_signal: &'static str, + pub tab_signal_js: &'static str, + pub tab_base_url: &'static str, + pub tab_fetch_target: &'static str, + pub tab_fetch_mode: &'static str, pub content: String, pub search_value: String, } -pub struct DataTab { +pub struct Tab { pub key: &'static str, pub label: &'static str, } @@ -151,6 +156,12 @@ pub struct AddTemplate { pub is_authenticated: bool, pub version_info: &'static crate::VersionInfo, pub active_type: String, + pub tabs: Vec, + pub tab_signal: &'static str, + pub tab_signal_js: &'static str, + pub tab_base_url: &'static str, + pub tab_fetch_target: &'static str, + pub tab_fetch_mode: &'static str, pub roaster_options: Vec, pub roast_options: Vec, pub bag_options: Vec, diff --git a/templates/add.html b/templates/add.html index a10cb8d..7b31ea3 100644 --- a/templates/add.html +++ b/templates/add.html @@ -134,7 +134,6 @@

Manual Add

- -
- {% for (key, label) in [("roaster", "Roaster"), ("roast", "Roast"), ("bag", "Bag"), ("brew", "Brew"), ("gear", "Gear"), ("cafe", "Cafe"), ("cup", "Cup")] %} - - {% endfor %} + +
+ {% include "partials/tab_bar.html" %}
diff --git a/templates/data.html b/templates/data.html index 12ca529..3ea8ea9 100644 --- a/templates/data.html +++ b/templates/data.html @@ -16,51 +16,8 @@ {% endif %} -
- - - -
- - -
+
+ {% include "partials/tab_bar.html" %}
+ + + +
+ + +
+