From 486bf9cbada8466b338326b970f2f640a062c7f5 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 14 Apr 2025 17:42:04 +0300 Subject: [PATCH] feat(typeahead): update structure and enhance Typeahead integration and logging: - Bloodhound Initialization: - Changed the suggestion object structure to a flattened format for Typeahead compatibility. - Removed nested `display` structure in favor of direct `displayLabel` and `displayDescription` fields. - Simplified the construction of suggestion objects by directly setting flattened fields. - Typeahead Initialization: - Enhanced logging in the JavaScript initialization script for better debugging. - Updated the `display` function in Typeahead to use flattened fields (`displayLabel` and `displayDescription`) instead of nested `display` objects. - Improved error handling and logging for cases where suggestions are not arrays or are empty. - Selection Handling: - Added detailed logging for selected suggestions in both Rust and JavaScript. - Ensured the input field is updated with the selected suggestion's label. - Code Cleanup: - Removed redundant nested object creation for `display` fields in Bloodhound initialization. - Streamlined the JavaScript initialization script for better readability and maintainability. - Consolidated logging statements for consistent debugging output. --- src/components/items_list.rs | 4 +- src/components/typeahead_input.rs | 85 ++++++++++++++++++------------- src/models/item.rs | 34 ++++++++++++- 3 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/components/items_list.rs b/src/components/items_list.rs index 8e582a7..d0eeec5 100644 --- a/src/components/items_list.rs +++ b/src/components/items_list.rs @@ -828,8 +828,8 @@ pub fn ItemsList( on_select=Callback::new(move |suggestion: WikidataSuggestion| { set_items.update(|items| { if let Some(item) = items.get_mut(index) { - item.name = suggestion.label.clone(); - item.description = suggestion.description.clone().unwrap_or_default(); + item.name = suggestion.display.label.value.clone(); + item.description = suggestion.display.description.value.clone(); item.wikidata_id = Some(suggestion.id.clone()); // Automatically fetch properties when Wikidata ID is set diff --git a/src/components/typeahead_input.rs b/src/components/typeahead_input.rs index 4d3f954..7003f62 100644 --- a/src/components/typeahead_input.rs +++ b/src/components/typeahead_input.rs @@ -144,26 +144,29 @@ fn initialize_bloodhound(fetch: Callback>) -> Js for suggestion in &suggestions { let obj = Object::new(); - // Create nested display structure matching API response - let display = Object::new(); - - let label_obj = Object::new(); - Reflect::set(&label_obj, &"value".into(), &suggestion.label.clone().into()).unwrap(); - Reflect::set(&display, &"label".into(), &label_obj).unwrap(); - - let desc_obj = Object::new(); - Reflect::set(&desc_obj, &"value".into(), &suggestion.description.clone().into()).unwrap(); - Reflect::set(&display, &"description".into(), &desc_obj).unwrap(); - - Reflect::set(&obj, &"display".into(), &display).unwrap(); - - // Add flat fields as fallback + // Set flattened structure for Typeahead compatibility + Reflect::set(&obj, &"id".into(), &suggestion.id.clone().into()).unwrap(); Reflect::set(&obj, &"label".into(), &suggestion.label.clone().into()).unwrap(); Reflect::set(&obj, &"description".into(), &suggestion.description.clone().into()).unwrap(); - log!("[BLOODHOUND] Constructed suggestion object: {:?}", obj); + // Flatten display values for direct access + Reflect::set( + &obj, + &"displayLabel".into(), + &suggestion.display.label.value.clone().into() + ).unwrap(); + + Reflect::set( + &obj, + &"displayDescription".into(), + &suggestion.display.description.value.clone().into() + ).unwrap(); + array.push(&obj); } + + log!("[BLOODHOUND] suggestions: {:?}", array); + let _ = sync.call1(&JsValue::NULL, &array); }) as Box); @@ -259,6 +262,7 @@ fn initialize_typeahead( let closure = Closure::wrap(Box::new(move |_event: web_sys::Event, suggestion: JsValue| { log!("[TYPEAHEAD] Selection made"); if let Ok(data) = suggestion.into_serde::() { + log!("[TYPEAHEAD] Selected suggestion: {:?}", data); on_select.call(data.clone()); if let Some(input) = node_ref.get() { input.set_value(&data.label); @@ -267,7 +271,7 @@ fn initialize_typeahead( log!("[ERROR] Failed to deserialize suggestion"); } }) as Box); - + // Register global handler let handler_name = format!("handler_{}", input_id); js_sys::Reflect::set( @@ -277,7 +281,7 @@ fn initialize_typeahead( ).unwrap(); closure.forget(); - // Initialization script + // Initialization script with enhanced logging let init_script = format!( r#" console.log('[JS] Starting Typeahead init for #{id}'); @@ -291,39 +295,52 @@ fn initialize_typeahead( }}, {{ name: 'suggestions', - display: 'label', - source: bloodhound.ttAdapter(), + display: function(data) {{ + console.log('[JS] Display function called with data:', data); + return data.display?.label?.value || data.label || ''; + }}, + source: function(query, syncResults) {{ + console.log('[JS] Bloodhound source called with query:', query); + var bloodhound = window.bloodhoundInstance; + bloodhound.ttAdapter()(query, function(suggestions) {{ + console.log('[JS] Suggestions from Bloodhound before syncResults:', suggestions); + if (Array.isArray(suggestions)) {{ + console.log('[JS] Passing suggestions to syncResults:', suggestions); + syncResults(suggestions); + }} else {{ + console.warn('[JS] Suggestions are not an array:', suggestions); + }} + }}); + }}, templates: {{ suggestion: function(data) {{ - // Handle nested Wikidata structure - var label = data.label || ''; - var description = data.description || ''; - - // If nested display exists, use those values - if (data.display) {{ - label = data.display.label?.value || label; - description = data.display.description?.value || description; - }} - + console.log('[JS] Rendering suggestion:', data); return $('
') .addClass('suggestion-item') - .append($('
').addClass('label').text(label)) - .append($('
').addClass('description').text(description)); + .append($('
').addClass('label').text(data.displayLabel || data.label)) + .append($('
').addClass('description').text(data.displayDescription || data.description)); }}, - empty: $('
').addClass('empty-suggestion').text('No matches found') + empty: function() {{ + console.log('[JS] No suggestions found'); + return $('
').addClass('empty-suggestion').text('No matches found'); + }} }} }} ) .on('typeahead:asyncreceive', function(ev, dataset, suggestions) {{ - console.log('[JS] Received suggestions:', suggestions); + console.log('[JS] Received suggestions in typeahead:asyncreceive:', suggestions); if (suggestions && suggestions.length > 0) {{ + console.log('[JS] Suggestions passed to dropdown:', suggestions); $(this).data('ttTypeahead').dropdown.open(); + }} else {{ + console.warn('[JS] No suggestions received or suggestions are empty.'); }} }}) .on('typeahead:select', function(ev, suggestion) {{ - console.log('[JS] Selection data:', JSON.stringify(suggestion, null, 2)); + console.log('[JS] Selection event received with suggestion:', suggestion); window['{handler}'](ev, suggestion); }}); + console.log('[JS] Typeahead initialized successfully'); }} catch (e) {{ console.error('[JS] Typeahead init error:', e); }} diff --git a/src/models/item.rs b/src/models/item.rs index 154b44d..ea3171b 100644 --- a/src/models/item.rs +++ b/src/models/item.rs @@ -11,9 +11,39 @@ pub struct Item { pub custom_properties: HashMap, } -#[derive(Deserialize, Clone, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct WikidataSuggestion { pub id: String, + #[serde(default)] pub label: String, - pub description: Option, + #[serde(default)] + pub description: String, + #[serde(default)] + pub title: String, + #[serde(default, rename = "display")] + pub display: DisplayInfo, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +pub struct DisplayInfo { + #[serde(default, rename = "label")] + pub label: LabelInfo, + #[serde(default, rename = "description")] + pub description: DescriptionInfo, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +pub struct LabelInfo { + #[serde(default, rename = "value")] + pub value: String, + #[serde(default, rename = "language")] + pub language: String, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +pub struct DescriptionInfo { + #[serde(default, rename = "value")] + pub value: String, + #[serde(default, rename = "language")] + pub language: String, } \ No newline at end of file