use crate::api::ApiClient; use crate::core::components::form_builder::FormBuilder; use crate::core::{EditorField, FieldType}; use serde_json::Value; /// Struct to hold commonly used dropdown options for assets pub struct AssetDropdownOptions { pub asset_types: Vec<(String, String)>, pub status_options: Vec<(String, String)>, pub lending_status_options: Vec<(String, String)>, pub no_scan_options: Vec<(String, String)>, pub zone_plus_options: Vec<(String, String)>, pub category_options: Vec<(String, String)>, pub zone_options: Vec<(String, String)>, pub supplier_options: Vec<(String, String)>, pub label_template_options: Vec<(String, String)>, pub audit_task_options: Vec<(String, String)>, } impl AssetDropdownOptions { /// Create dropdown options by fetching from API pub fn new(api_client: &ApiClient) -> Self { // Static options let asset_types = vec![ ("N".to_string(), "Normal".to_string()), ("B".to_string(), "Basic".to_string()), ("L".to_string(), "License".to_string()), ("C".to_string(), "Consumable".to_string()), ]; // Status options: include full set supported by schema. Some installations may use "Retired" while others use "Scrapped". // We include both to allow selection wherever the backend enum allows it. let status_options = vec![ ("Good".to_string(), "Good".to_string()), ("Attention".to_string(), "Attention".to_string()), ("Faulty".to_string(), "Faulty".to_string()), ("Missing".to_string(), "Missing".to_string()), ("In Repair".to_string(), "In Repair".to_string()), ("In Transit".to_string(), "In Transit".to_string()), ("Expired".to_string(), "Expired".to_string()), ("Unmanaged".to_string(), "Unmanaged".to_string()), ("Retired".to_string(), "Retired".to_string()), ("Scrapped".to_string(), "Scrapped".to_string()), ]; let lending_status_options = vec![ ("Available".to_string(), "Available".to_string()), ("Borrowed".to_string(), "Borrowed".to_string()), ("Overdue".to_string(), "Overdue".to_string()), ("Deployed".to_string(), "Deployed".to_string()), ( "Illegally Handed Out".to_string(), "Illegally Handed Out".to_string(), ), ("Stolen".to_string(), "Stolen".to_string()), ]; let no_scan_options = vec![ ("No".to_string(), "No".to_string()), ("Ask".to_string(), "Ask".to_string()), ("Yes".to_string(), "Yes".to_string()), ]; let zone_plus_options = vec![ ("".into(), "".into()), ("Floating Local".into(), "Floating Local".into()), ("Floating Global".into(), "Floating Global".into()), ("Clarify".into(), "Clarify".into()), ]; // Fetch categories from API let mut category_options: Vec<(String, String)> = Vec::new(); if let Ok(resp) = api_client.select( "categories", Some(vec!["id".into(), "category_name".into()]), None, Some(vec![crate::models::OrderBy { column: "category_name".into(), direction: "ASC".into(), }]), None, ) { if resp.success { if let Some(data) = resp.data { for row in data { let id = row .get("id") .and_then(|v| v.as_i64()) .map(|n| n.to_string()) .unwrap_or_default(); let name = row .get("category_name") .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); category_options.push((id, name)); } } } } // Fetch zones from API let mut zone_options: Vec<(String, String)> = Vec::new(); if let Ok(resp) = api_client.select( "zones", Some(vec!["id".into(), "zone_code".into(), "zone_name".into()]), None, Some(vec![crate::models::OrderBy { column: "zone_code".into(), direction: "ASC".into(), }]), None, ) { if resp.success { if let Some(data) = resp.data { for row in data { let id = row .get("id") .and_then(|v| v.as_i64()) .map(|n| n.to_string()) .unwrap_or_default(); let code = row .get("zone_code") .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); let name = row .get("zone_name") .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); zone_options.push((id, format!("{} - {}", code, name))); } } } } // Fetch suppliers from API let mut supplier_options: Vec<(String, String)> = Vec::new(); if let Ok(resp) = api_client.select( "suppliers", Some(vec!["id".into(), "name".into()]), None, Some(vec![crate::models::OrderBy { column: "name".into(), direction: "ASC".into(), }]), None, ) { if resp.success { if let Some(data) = resp.data { for row in data { let id = row .get("id") .and_then(|v| v.as_i64()) .map(|n| n.to_string()) .unwrap_or_default(); let name = row .get("name") .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); supplier_options.push((id, name)); } } } } // Fetch label templates for dropdown let mut label_template_options: Vec<(String, String)> = Vec::new(); if let Ok(resp) = api_client.select( "label_templates", Some(vec!["id".into(), "template_name".into()]), None, Some(vec![crate::models::OrderBy { column: "template_name".into(), direction: "ASC".into(), }]), None, ) { if resp.success { if let Some(data) = resp.data { for row in data { let id = row .get("id") .and_then(|v| v.as_i64()) .map(|n| n.to_string()) .unwrap_or_default(); let name = row .get("template_name") .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); label_template_options.push((id, name)); } } } } // Fetch audit tasks and add default "None" option let mut audit_task_options: Vec<(String, String)> = vec![(String::new(), "-- None --".to_string())]; if let Ok(resp) = api_client.select( "audit_tasks", Some(vec!["id".into(), "task_name".into()]), None, Some(vec![crate::models::OrderBy { column: "task_name".into(), direction: "ASC".into(), }]), None, ) { if resp.success { if let Some(data) = resp.data { for row in data { if let Some(id) = row.get("id").and_then(|v| v.as_i64()) { let name = row .get("task_name") .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); audit_task_options.push((id.to_string(), name)); } } } } } Self { asset_types, status_options, lending_status_options, no_scan_options, zone_plus_options, category_options, zone_options, supplier_options, label_template_options, audit_task_options, } } } /// Asset field configuration builder - provides standardized field definitions for asset forms pub struct AssetFieldBuilder; impl AssetFieldBuilder { /// Create a Full Add dialog that shows (nearly) all asset fields similar to Advanced Edit, /// but configured for inserting a new asset (no ID fields, audit fields stay read-only). pub fn create_full_add_dialog(api_client: &ApiClient) -> FormBuilder { let options = AssetDropdownOptions::new(api_client); let fields: Vec = vec![ // Core identifiers for new record EditorField { name: "asset_tag".into(), label: "Asset Tag".into(), field_type: FieldType::Text, required: true, read_only: false, }, EditorField { name: "asset_type".into(), label: "Type".into(), field_type: FieldType::Dropdown(options.asset_types.clone()), required: true, read_only: false, }, EditorField { name: "name".into(), label: "Name".into(), field_type: FieldType::Text, required: true, read_only: false, }, // Classification EditorField { name: "category_id".into(), label: "Category".into(), field_type: FieldType::Dropdown(options.category_options.clone()), required: false, read_only: false, }, // Quick add category EditorField { name: "new_category_name".into(), label: "Add Category - Name".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "new_category_code".into(), label: "Add Category - Code".into(), field_type: FieldType::Text, required: false, read_only: false, }, // Location EditorField { name: "zone_id".into(), label: "Zone".into(), field_type: FieldType::Dropdown(options.zone_options.clone()), required: false, read_only: false, }, // Quick add zone EditorField { name: "new_zone_parent_id".into(), label: "Add Zone - Parent".into(), field_type: FieldType::Dropdown(options.zone_options.clone()), required: false, read_only: false, }, EditorField { name: "new_zone_mini_code".into(), label: "Add Zone - Mini Code".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "new_zone_name".into(), label: "Add Zone - Name".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "zone_plus".into(), label: "Zone Plus".into(), field_type: FieldType::Dropdown(options.zone_plus_options.clone()), required: false, read_only: false, }, EditorField { name: "no_scan".into(), label: "No Scan".into(), field_type: FieldType::Dropdown(options.no_scan_options.clone()), required: false, read_only: false, }, // Make/model EditorField { name: "manufacturer".into(), label: "Manufacturer".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "model".into(), label: "Model".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "serial_number".into(), label: "Serial Number".into(), field_type: FieldType::Text, required: false, read_only: false, }, // Status EditorField { name: "status".into(), label: "Status".into(), field_type: FieldType::Dropdown(options.status_options.clone()), required: true, read_only: false, }, // Financial / dates EditorField { name: "price".into(), label: "Price".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "purchase_date".into(), label: "Purchase Date".into(), field_type: FieldType::Date, required: false, read_only: false, }, EditorField { name: "warranty_until".into(), label: "Warranty Until".into(), field_type: FieldType::Date, required: false, read_only: false, }, EditorField { name: "expiry_date".into(), label: "Expiry Date".into(), field_type: FieldType::Date, required: false, read_only: false, }, // Lendable EditorField { name: "lendable".into(), label: "Lendable".into(), field_type: FieldType::Checkbox, required: false, read_only: false, }, EditorField { name: "lending_status".into(), label: "Lending Status".into(), field_type: FieldType::Dropdown(options.lending_status_options.clone()), required: false, read_only: false, }, EditorField { name: "due_date".into(), label: "Due Date".into(), field_type: FieldType::Date, required: false, read_only: false, }, // Supplier EditorField { name: "supplier_id".into(), label: "Supplier".into(), field_type: FieldType::Dropdown(options.supplier_options.clone()), required: false, read_only: false, }, // Quick add supplier EditorField { name: "new_supplier_name".into(), label: "Add Supplier - Name".into(), field_type: FieldType::Text, required: false, read_only: false, }, // Label template EditorField { name: "label_template_id".into(), label: "Label Template".into(), field_type: FieldType::Dropdown(options.label_template_options.clone()), required: false, read_only: false, }, // Notes EditorField { name: "notes".into(), label: "Notes".into(), field_type: FieldType::MultilineText, required: false, read_only: false, }, // Optional: print on add toggle EditorField { name: "print_label".into(), label: "Print Label".into(), field_type: FieldType::Checkbox, required: false, read_only: false, }, // Audit/meta (read-only informational) EditorField { name: "created_date".into(), label: "Created Date".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "created_by_username".into(), label: "Created By".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "last_modified_date".into(), label: "Last Modified".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "last_modified_by_username".into(), label: "Modified By".into(), field_type: FieldType::Text, required: false, read_only: true, }, ]; let mut dialog = FormBuilder::new("Add Asset (Full)", fields); // Prefill sensible defaults let mut preset = serde_json::Map::new(); preset.insert("asset_type".to_string(), Value::String("N".to_string())); preset.insert("status".to_string(), Value::String("Good".to_string())); preset.insert("lendable".to_string(), Value::Bool(true)); preset.insert("print_label".to_string(), Value::Bool(true)); dialog.open_new(Some(&preset)); dialog } /// Create a comprehensive Advanced Edit dialog with all asset fields pub fn create_advanced_edit_dialog(api_client: &ApiClient) -> FormBuilder { let options = AssetDropdownOptions::new(api_client); let fields: Vec = vec![ // Identifiers (read-only) EditorField { name: "id".into(), label: "ID".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "asset_numeric_id".into(), label: "Numeric ID".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "asset_tag".into(), label: "Asset Tag".into(), field_type: FieldType::Text, required: false, read_only: false, }, // Core fields EditorField { name: "asset_type".into(), label: "Type".into(), field_type: FieldType::Dropdown(options.asset_types), required: true, read_only: false, }, EditorField { name: "name".into(), label: "Name".into(), field_type: FieldType::Text, required: true, read_only: false, }, EditorField { name: "description".into(), label: "Description".into(), field_type: FieldType::MultilineText, required: false, read_only: false, }, // Classification EditorField { name: "category_id".into(), label: "Category".into(), field_type: FieldType::Dropdown(options.category_options.clone()), required: false, read_only: false, }, // Quick add category EditorField { name: "new_category_name".into(), label: "Add Category - Name".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "new_category_code".into(), label: "Add Category - Code".into(), field_type: FieldType::Text, required: false, read_only: false, }, // Location EditorField { name: "zone_id".into(), label: "Zone".into(), field_type: FieldType::Dropdown(options.zone_options.clone()), required: false, read_only: false, }, // Quick add zone EditorField { name: "new_zone_parent_id".into(), label: "Add Zone - Parent".into(), field_type: FieldType::Dropdown(options.zone_options.clone()), required: false, read_only: false, }, EditorField { name: "new_zone_mini_code".into(), label: "Add Zone - Mini Code".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "new_zone_name".into(), label: "Add Zone - Name".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "zone_plus".into(), label: "Zone Plus".into(), field_type: FieldType::Dropdown(options.zone_plus_options), required: false, read_only: false, }, EditorField { name: "no_scan".into(), label: "No Scan".into(), field_type: FieldType::Dropdown(options.no_scan_options), required: false, read_only: false, }, EditorField { name: "audit_task_id".into(), label: "Audit Task".into(), field_type: FieldType::Dropdown(options.audit_task_options.clone()), required: false, read_only: false, }, // Make/model EditorField { name: "manufacturer".into(), label: "Manufacturer".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "model".into(), label: "Model".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "serial_number".into(), label: "Serial Number".into(), field_type: FieldType::Text, required: false, read_only: false, }, // Status EditorField { name: "status".into(), label: "Status".into(), field_type: FieldType::Dropdown(options.status_options), required: true, read_only: false, }, // Financial / dates EditorField { name: "price".into(), label: "Price".into(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "purchase_date".into(), label: "Purchase Date".into(), field_type: FieldType::Date, required: false, read_only: false, }, EditorField { name: "warranty_until".into(), label: "Warranty Until".into(), field_type: FieldType::Date, required: false, read_only: false, }, EditorField { name: "expiry_date".into(), label: "Expiry Date".into(), field_type: FieldType::Date, required: false, read_only: false, }, // Lendable EditorField { name: "lendable".into(), label: "Lendable".into(), field_type: FieldType::Checkbox, required: false, read_only: false, }, EditorField { name: "lending_status".into(), label: "Lending Status".into(), field_type: FieldType::Dropdown(options.lending_status_options), required: false, read_only: false, }, EditorField { name: "due_date".into(), label: "Due Date".into(), field_type: FieldType::Date, required: false, read_only: false, }, EditorField { name: "supplier_id".into(), label: "Supplier".into(), field_type: FieldType::Dropdown(options.supplier_options), required: false, read_only: false, }, // Quick add supplier EditorField { name: "new_supplier_name".into(), label: "Add Supplier - Name".into(), field_type: FieldType::Text, required: false, read_only: false, }, // Borrowers (read-only historical) EditorField { name: "previous_borrower_id".into(), label: "Prev Borrower".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "current_borrower_id".into(), label: "Current Borrower".into(), field_type: FieldType::Text, required: false, read_only: true, }, // Label template selection EditorField { name: "label_template_id".into(), label: "Label Template".into(), field_type: FieldType::Dropdown(options.label_template_options.clone()), required: false, read_only: false, }, // Notes / images EditorField { name: "notes".into(), label: "Notes".into(), field_type: FieldType::MultilineText, required: false, read_only: false, }, // Audit/meta (read-only) EditorField { name: "created_date".into(), label: "Created Date".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "created_by_username".into(), label: "Created By".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "last_modified_date".into(), label: "Last Modified".into(), field_type: FieldType::Text, required: false, read_only: true, }, EditorField { name: "last_modified_by_username".into(), label: "Modified By".into(), field_type: FieldType::Text, required: false, read_only: true, }, ]; FormBuilder::new("Advanced Edit Asset", fields) } /// Create an Easy Edit dialog with essential asset fields only pub fn create_easy_edit_dialog(api_client: &ApiClient) -> FormBuilder { let options = AssetDropdownOptions::new(api_client); let fields = vec![ EditorField { name: "asset_tag".to_string(), label: "Asset Tag".to_string(), field_type: FieldType::Text, required: true, read_only: false, }, EditorField { name: "asset_type".to_string(), label: "Type".to_string(), field_type: FieldType::Dropdown(options.asset_types), required: true, read_only: false, }, EditorField { name: "name".to_string(), label: "Name".to_string(), field_type: FieldType::Text, required: true, read_only: false, }, EditorField { name: "category_id".to_string(), label: "Category".to_string(), field_type: FieldType::Dropdown(options.category_options), required: false, read_only: false, }, EditorField { name: "manufacturer".to_string(), label: "Manufacturer".to_string(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "model".to_string(), label: "Model".to_string(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "zone_id".to_string(), label: "Zone".to_string(), field_type: FieldType::Dropdown(options.zone_options), required: false, read_only: false, }, EditorField { name: "status".to_string(), label: "Status".to_string(), field_type: FieldType::Dropdown(options.status_options), required: true, read_only: false, }, EditorField { name: "lendable".to_string(), label: "Lendable".to_string(), field_type: FieldType::Checkbox, required: false, read_only: false, }, ]; FormBuilder::new("Easy Edit Asset", fields) } /// Create an Add Asset dialog with quick-add functionality for categories/zones/suppliers pub fn create_add_dialog_with_preset(api_client: &ApiClient) -> FormBuilder { let options = AssetDropdownOptions::new(api_client); let fields = vec![ EditorField { name: "asset_tag".to_string(), label: "Asset Tag".to_string(), field_type: FieldType::Text, required: true, read_only: false, }, EditorField { name: "asset_type".to_string(), label: "Type".to_string(), field_type: FieldType::Dropdown(options.asset_types), required: true, read_only: false, }, EditorField { name: "name".to_string(), label: "Name".to_string(), field_type: FieldType::Text, required: true, read_only: false, }, // Category dropdown + add-new placeholders EditorField { name: "category_id".to_string(), label: "Category".to_string(), field_type: FieldType::Dropdown(options.category_options), required: false, read_only: false, }, // Label template selection EditorField { name: "label_template_id".to_string(), label: "Label Template".to_string(), field_type: FieldType::Dropdown(options.label_template_options.clone()), required: false, read_only: false, }, // Print label option EditorField { name: "print_label".to_string(), label: "Print Label".to_string(), field_type: FieldType::Checkbox, required: false, read_only: false, }, // Add new category name/code as text fields EditorField { name: "new_category_name".to_string(), label: "Add New Category - Name".to_string(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "new_category_code".to_string(), label: "Add New Category - Code".to_string(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "manufacturer".to_string(), label: "Manufacturer".to_string(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "model".to_string(), label: "Model".to_string(), field_type: FieldType::Text, required: false, read_only: false, }, EditorField { name: "zone_id".to_string(), label: "Zone".to_string(), field_type: FieldType::Dropdown(options.zone_options), required: false, read_only: false, }, EditorField { name: "status".to_string(), label: "Status".to_string(), field_type: FieldType::Dropdown(options.status_options), required: true, read_only: false, }, EditorField { name: "lendable".to_string(), label: "Lendable".to_string(), field_type: FieldType::Checkbox, required: false, read_only: false, }, ]; let mut dialog = FormBuilder::new("Add Asset", fields); // Open with sensible defaults let mut preset = serde_json::Map::new(); preset.insert("asset_type".to_string(), Value::String("N".to_string())); preset.insert("status".to_string(), Value::String("Good".to_string())); preset.insert("lendable".to_string(), Value::Bool(true)); // Default to printing a label after adding when possible preset.insert("print_label".to_string(), Value::Bool(true)); dialog.open_new(Some(&preset)); dialog } /// Get the list of fields that are allowed to be updated via API #[allow(dead_code)] pub fn get_allowed_update_fields() -> Vec { vec![ "asset_tag".to_string(), "asset_type".to_string(), "name".to_string(), "description".to_string(), "category_id".to_string(), "zone_id".to_string(), "zone_plus".to_string(), "no_scan".to_string(), "manufacturer".to_string(), "model".to_string(), "serial_number".to_string(), "status".to_string(), "price".to_string(), "purchase_date".to_string(), "warranty_until".to_string(), "expiry_date".to_string(), "lendable".to_string(), "lending_status".to_string(), "due_date".to_string(), "supplier_id".to_string(), "notes".to_string(), "label_template_id".to_string(), ] } }