Browse Source

some crappy fixes incomming

UMTS at Teleco 1 month ago
parent
commit
49059fe5e3

+ 4 - 0
src/core/components/filter_builder.rs

@@ -102,6 +102,10 @@ impl FilterCondition {
     pub fn to_json(&self, table_prefix: &str) -> Value {
         let column_name = if self.column.contains('.') {
             self.column.clone()
+        } else if self.column == "zone_code" {
+            // Hack: zone_code is unique across joined tables (only in zones), so we can use it directly
+            // avoiding potential issues with qualified names in some backend paths
+            self.column.clone()
         } else {
             format!("{}.{}", table_prefix, self.column)
         };

+ 63 - 14
src/core/components/form_builder.rs

@@ -10,6 +10,7 @@ use egui_phosphor::regular as icons;
 #[derive(Clone)]
 pub enum FieldType {
     Text,
+    Number,
     #[allow(dead_code)]
     Dropdown(Vec<(String, String)>), // (value, label)
     MultilineText,
@@ -191,6 +192,19 @@ impl FormBuilder {
                                                 .interactive(!field.read_only),
                                         );
                                     }
+                                    FieldType::Number => {
+                                        let label_text = if field.required {
+                                            format!("{} *", field.label)
+                                        } else {
+                                            field.label.clone()
+                                        };
+                                        ui.label(&label_text);
+                                        ui.add(
+                                            egui::TextEdit::singleline(field_value)
+                                                .desired_width(f32::INFINITY)
+                                                .interactive(!field.read_only),
+                                        );
+                                    }
                                     FieldType::MultilineText => {
                                         let label_text = if field.required {
                                             format!("{} *", field.label)
@@ -309,21 +323,56 @@ impl FormBuilder {
                             // Convert string map back to JSON
                             let mut json_map = serde_json::Map::new();
                             for (k, v) in &self.data {
-                                // Try to preserve types
-                                let json_value = if v == "true" {
-                                    Value::Bool(true)
-                                } else if v == "false" {
-                                    Value::Bool(false)
-                                } else if let Ok(n) = v.parse::<i64>() {
-                                    Value::Number(n.into())
-                                } else if let Ok(n) = v.parse::<f64>() {
-                                    serde_json::Number::from_f64(n)
-                                        .map(Value::Number)
-                                        .unwrap_or_else(|| Value::String(v.clone()))
-                                } else if v.is_empty() {
-                                    Value::Null
+                                // Find field definition to determine expected type
+                                let field_def = self.fields.iter().find(|f| &f.name == k);
+
+                                let json_value = if let Some(field) = field_def {
+                                    match field.field_type {
+                                        FieldType::Text | FieldType::MultilineText | FieldType::Date | FieldType::Dropdown(_) => {
+                                            if v.is_empty() {
+                                                Value::Null
+                                            } else {
+                                                Value::String(v.clone())
+                                            }
+                                        }
+                                        FieldType::Number => {
+                                            if v.is_empty() {
+                                                Value::Null
+                                            } else if let Ok(n) = v.parse::<i64>() {
+                                                Value::Number(n.into())
+                                            } else if let Ok(n) = v.parse::<f64>() {
+                                                serde_json::Number::from_f64(n)
+                                                    .map(Value::Number)
+                                                    .unwrap_or_else(|| Value::String(v.clone()))
+                                            } else {
+                                                Value::String(v.clone())
+                                            }
+                                        }
+                                        FieldType::Checkbox => {
+                                            if v == "true" || v == "1" {
+                                                Value::Bool(true)
+                                            } else {
+                                                Value::Bool(false)
+                                            }
+                                        }
+                                    }
                                 } else {
-                                    Value::String(v.clone())
+                                    // Fallback to type inference if field definition not found
+                                    if v == "true" {
+                                        Value::Bool(true)
+                                    } else if v == "false" {
+                                        Value::Bool(false)
+                                    } else if let Ok(n) = v.parse::<i64>() {
+                                        Value::Number(n.into())
+                                    } else if let Ok(n) = v.parse::<f64>() {
+                                        serde_json::Number::from_f64(n)
+                                            .map(Value::Number)
+                                            .unwrap_or_else(|| Value::String(v.clone()))
+                                    } else if v.is_empty() {
+                                        Value::Null
+                                    } else {
+                                        Value::String(v.clone())
+                                    }
                                 };
                                 json_map.insert(k.clone(), json_value);
                             }

+ 14 - 24
src/core/data/data_loader.rs

@@ -69,31 +69,21 @@ impl DataLoader {
             filter
         );
 
-        // Use select_with_joins to load assets with zone and category data
-        let response = api_client
-            .select_with_joins(
-                "assets",
-                None, // columns (None = all)
-                where_clause,
-                filter,
-                None, // order_by
-                limit,
-                None, // joins (None = use default joins)
-            )
-            .map_err(|e| format!("Failed to load assets: {}", e))?;
-
-        if !response.success {
-            // Check if this is a database timeout error
-            if ApiClient::is_database_timeout_error(&response.error) {
-                log::warn!("Database timeout detected while loading assets");
+        // Use tables::get_all_assets to ensure we get all joined columns (category_name, zone_code, etc.)
+        match crate::core::tables::get_all_assets(api_client, limit, where_clause, filter) {
+            Ok(assets) => {
+                log::info!("Loaded {} assets successfully (with JOINs)", assets.len());
+                Ok(assets)
+            }
+            Err(e) => {
+                let error_msg = format!("Failed to load assets: {}", e);
+                // Check if this is a database timeout error
+                if ApiClient::is_database_timeout_error(&Some(error_msg.clone())) {
+                    log::warn!("Database timeout detected while loading assets");
+                }
+                log::error!("{}", error_msg);
+                Err(error_msg)
             }
-            let error_msg = format!("API error: {:?}", response.error);
-            log::error!("{}", error_msg);
-            return Err(error_msg);
         }
-
-        let assets = response.data.unwrap_or_default();
-        log::info!("Loaded {} assets successfully (with JOINs)", assets.len());
-        Ok(assets)
     }
 }

+ 9 - 9
src/core/tables.rs

@@ -416,10 +416,10 @@ pub fn get_all_assets(
         "assets.last_modified_by".to_string(),
         "assets.label_template_id".to_string(),
         // JOINed fields
-        "categories.category_name".to_string(),
+        "categories.category_name AS category_name".to_string(),
         "label_templates.template_name AS label_template_name".to_string(),
-        "zones.zone_name".to_string(),
-        "zones.zone_code".to_string(),
+        "zones.zone_name AS zone_name".to_string(),
+        "zones.zone_code AS zone_code".to_string(),
         "suppliers.name AS supplier_name".to_string(),
         // Borrower joined from asset field
         "current_borrower.name AS current_borrower_name".to_string(),
@@ -1128,8 +1128,8 @@ pub fn get_recent_audits(
         "physical_audits.notes".into(),
         "physical_audits.cancelled_reason".into(),
         // Joined labels
-        "zones.zone_code".into(),
-        "zones.zone_name".into(),
+        "zones.zone_code AS zone_code".into(),
+        "zones.zone_name AS zone_name".into(),
         "users.name as started_by_name".into(),
     ]);
     let joins = Some(vec![
@@ -1321,13 +1321,13 @@ pub fn get_templates(api_client: &ApiClient, limit: Option<u32>) -> Result<Vec<s
         "templates.asset_type".into(),
         "templates.name".into(),
         "templates.category_id".into(),
-        "categories.category_name".into(),
-        "categories.category_code".into(),
+        "categories.category_name AS category_name".into(),
+        "categories.category_code AS category_code".into(),
         "templates.manufacturer".into(),
         "templates.model".into(),
         "templates.zone_id".into(),
-        "zones.zone_code".into(),
-        "zones.zone_name".into(),
+        "zones.zone_code AS zone_code".into(),
+        "zones.zone_name AS zone_name".into(),
         "templates.zone_plus".into(),
         "templates.zone_note".into(),
         "templates.status".into(),

+ 5 - 5
src/ui/app.rs

@@ -1162,19 +1162,19 @@ impl eframe::App for BeepZoneApp {
                         self.zones.show(ui, self.api_client.as_ref(), ribbon, self.current_permissions.as_ref());
 
                         // Handle zone navigation request to inventory
-                        if let Some(zone_code) = self.zones.switch_to_inventory_with_zone.take() {
-                            log::info!("Switching to inventory with zone filter: {}", zone_code);
+                        if let Some((zone_code, zone_id)) = self.zones.switch_to_inventory_with_zone.take() {
+                            log::info!("Switching to inventory with zone filter: {} (ID: {})", zone_code, zone_id);
 
                             // Save current Zones filter state
                             let zones_filter_state = ribbon.filter_builder.filter_group.clone();
                             self.view_filter_states
                                 .insert(AppView::Zones, zones_filter_state);
 
-                            // Set zone filter using the correct column name from the JOIN
+                            // Set zone filter using the ID which is safer and faster
                             ribbon.filter_builder.set_single_filter(
-                                "zones.zone_code".to_string(),
+                                "assets.zone_id".to_string(),
                                 crate::core::components::filter_builder::FilterOperator::Is,
-                                zone_code,
+                                zone_id.to_string(),
                             );
 
                             // Switch to inventory view

+ 12 - 19
src/ui/zones.rs

@@ -29,7 +29,7 @@ pub struct ZonesView {
     pending_delete_id: Option<i32>,
     pending_parent_id: Option<i32>, // For "Add Child Zone"
     // Navigation request
-    pub switch_to_inventory_with_zone: Option<String>, // zone_code to filter by
+    pub switch_to_inventory_with_zone: Option<(String, i64)>, // (zone_code, zone_id) to filter by
     // Print dialog
     print_dialog: Option<crate::core::print::PrintDialog>,
     show_print_dialog: bool,
@@ -115,7 +115,7 @@ impl ZonesView {
                 EditorField {
                     name: "id".into(),
                     label: "ID".into(),
-                    field_type: FieldType::Text,
+                    field_type: FieldType::Number,
                     required: false,
                     read_only: true,
                 },
@@ -130,7 +130,7 @@ impl ZonesView {
                     name: "zone_code".into(),
                     label: "Full Zone Code".into(),
                     field_type: FieldType::Text,
-                    required: false,
+                    required: true,
                     read_only: false,
                 },
                 EditorField {
@@ -164,7 +164,7 @@ impl ZonesView {
                 EditorField {
                     name: "audit_timeout_minutes".into(),
                     label: "Audit Timeout (minutes)".into(),
-                    field_type: FieldType::Text,
+                    field_type: FieldType::Number,
                     required: false,
                     read_only: false,
                 },
@@ -202,7 +202,7 @@ impl ZonesView {
                     name: "zone_code".into(),
                     label: "Full Zone Code".into(),
                     field_type: FieldType::Text,
-                    required: false,
+                    required: true,
                     read_only: false,
                 },
                 EditorField {
@@ -236,7 +236,7 @@ impl ZonesView {
                 EditorField {
                     name: "audit_timeout_minutes".into(),
                     label: "Audit Timeout (minutes)".into(),
-                    field_type: FieldType::Text,
+                    field_type: FieldType::Number,
                     required: false,
                     read_only: false,
                 },
@@ -498,9 +498,9 @@ impl ZonesView {
         // Sort children lists by zone_code
         for list in children.values_mut() {
             list.sort_by(|a, b| {
-                a.get("zone_code")
-                    .and_then(|v| v.as_str())
-                    .cmp(&b.get("zone_code").and_then(|v| v.as_str()))
+                let code_a = a.get("zone_code").and_then(|v| v.as_str()).unwrap_or("");
+                let code_b = b.get("zone_code").and_then(|v| v.as_str()).unwrap_or("");
+                code_a.cmp(code_b)
             });
         }
 
@@ -730,7 +730,7 @@ impl ZonesView {
                 .clicked()
             {
                 // Set the flag to switch to inventory with this zone filter
-                self.switch_to_inventory_with_zone = Some(code.to_string());
+                self.switch_to_inventory_with_zone = Some((code.to_string(), id as i64));
                 ui.close();
             }
             if ui
@@ -777,15 +777,8 @@ impl ZonesView {
 
                     // Clear identifiers and codes that must be unique
                     clone_map.remove("id");
-                    clone_map.insert(
-                        "zone_code".to_string(),
-                        serde_json::Value::String(String::new()),
-                    );
-                    // Mini code is required but typically unique — leave empty to force user choice
-                    clone_map.insert(
-                        "mini_code".to_string(),
-                        serde_json::Value::String(String::new()),
-                    );
+                    // Keep zone_code and mini_code as requested, so user can easily modify them
+                    // for similar zones (e.g. multi-story buildings)
 
                     // Convert parent_id to string for dropdown if present
                     if let Some(p) = clone_map.get("parent_id").cloned() {