Compare commits

..

No commits in common. "486bf9cbada8466b338326b970f2f640a062c7f5" and "102f69fd2932f3a37f0d272cd3b34f4a32bb5fdd" have entirely different histories.

9 changed files with 48 additions and 95 deletions

View file

@ -79,8 +79,8 @@ style-file = "style/main.scss"
# #
# Optional. Env: LEPTOS_ASSETS_DIR. # Optional. Env: LEPTOS_ASSETS_DIR.
assets-dir = "assets" assets-dir = "assets"
# The IP and port (ex: 127.0.0.1:3004) where the server serves the content. Use it in your server setup. # The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-addr = "127.0.0.1:3004" site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring # The port to use for automatic reload monitoring
reload-port = 3001 reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir. # [Optional] Command to use when running end2end tests. It will run in the end2end dir.

View file

@ -26,7 +26,7 @@ It combines Rust's **Leptos** for a modern, reactive frontend and **Nostr** for
```bash ```bash
cargo leptos serve cargo leptos serve
``` ```
3. Open your browser at [localhost:3004](http://localhost:3004) 3. Open your browser at [localhost:3000](http://localhost:3000)
## **Database Schema** ## **Database Schema**
### Key Concepts ### Key Concepts
@ -97,7 +97,7 @@ sequenceDiagram
```bash ```bash
docker-compose up -d docker-compose up -d
``` ```
3. Access the application at: [http://localhost:3004](http://localhost:3004) 3. Access the application at: [http://localhost:3000](http://localhost:3000)
### **Collaboration** ### **Collaboration**
We welcome contributions! Heres how you can help: We welcome contributions! Heres how you can help:

View file

@ -2,7 +2,7 @@ services:
app: app:
build: . build: .
ports: ports:
- "3004:3004" - "3000:3000"
volumes: volumes:
- ./compareware.db:/app/compareware.db - ./compareware.db:/app/compareware.db
environment: environment:

View file

@ -50,7 +50,7 @@ COPY assets /app/assets
# Configure container, expose port and set entrypoint # Configure container, expose port and set entrypoint
WORKDIR /app WORKDIR /app
EXPOSE 3004 EXPOSE 3000
ENV LEPTOS_SITE_ADDR=0.0.0.0:3004 ENV LEPTOS_SITE_ADDR=0.0.0.0:3000
ENV LEPTOS_SITE_ROOT="site" ENV LEPTOS_SITE_ROOT="site"
CMD ["./compareware"] CMD ["./compareware"]

View file

@ -35,7 +35,7 @@ export default defineConfig({
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0, actionTimeout: 0,
/* Base URL to use in actions like `await page.goto('/')`. */ /* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://localhost:3004', // baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry", trace: "on-first-retry",
@ -99,6 +99,6 @@ export default defineConfig({
/* Run your local dev server before starting the tests */ /* Run your local dev server before starting the tests */
// webServer: { // webServer: {
// command: 'npm run start', // command: 'npm run start',
// port: 3004, // port: 3000,
// }, // },
}); });

View file

@ -1,7 +1,7 @@
import { test, expect } from "@playwright/test"; import { test, expect } from "@playwright/test";
test("homepage has title and links to intro page", async ({ page }) => { test("homepage has title and links to intro page", async ({ page }) => {
await page.goto("http://localhost:3004/"); await page.goto("http://localhost:3000/");
await expect(page).toHaveTitle("Welcome to Leptos"); await expect(page).toHaveTitle("Welcome to Leptos");

View file

@ -828,8 +828,8 @@ pub fn ItemsList(
on_select=Callback::new(move |suggestion: WikidataSuggestion| { on_select=Callback::new(move |suggestion: WikidataSuggestion| {
set_items.update(|items| { set_items.update(|items| {
if let Some(item) = items.get_mut(index) { if let Some(item) = items.get_mut(index) {
item.name = suggestion.display.label.value.clone(); item.name = suggestion.label.clone();
item.description = suggestion.display.description.value.clone(); item.description = suggestion.description.clone().unwrap_or_default();
item.wikidata_id = Some(suggestion.id.clone()); item.wikidata_id = Some(suggestion.id.clone());
// Automatically fetch properties when Wikidata ID is set // Automatically fetch properties when Wikidata ID is set

View file

@ -144,29 +144,26 @@ fn initialize_bloodhound(fetch: Callback<String, Vec<WikidataSuggestion>>) -> Js
for suggestion in &suggestions { for suggestion in &suggestions {
let obj = Object::new(); let obj = Object::new();
// Set flattened structure for Typeahead compatibility // Create nested display structure matching API response
Reflect::set(&obj, &"id".into(), &suggestion.id.clone().into()).unwrap(); 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
Reflect::set(&obj, &"label".into(), &suggestion.label.clone().into()).unwrap(); Reflect::set(&obj, &"label".into(), &suggestion.label.clone().into()).unwrap();
Reflect::set(&obj, &"description".into(), &suggestion.description.clone().into()).unwrap(); Reflect::set(&obj, &"description".into(), &suggestion.description.clone().into()).unwrap();
// Flatten display values for direct access log!("[BLOODHOUND] Constructed suggestion object: {:?}", obj);
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); array.push(&obj);
} }
log!("[BLOODHOUND] suggestions: {:?}", array);
let _ = sync.call1(&JsValue::NULL, &array); let _ = sync.call1(&JsValue::NULL, &array);
}) as Box<dyn Fn(JsValue, Function)>); }) as Box<dyn Fn(JsValue, Function)>);
@ -262,7 +259,6 @@ fn initialize_typeahead(
let closure = Closure::wrap(Box::new(move |_event: web_sys::Event, suggestion: JsValue| { let closure = Closure::wrap(Box::new(move |_event: web_sys::Event, suggestion: JsValue| {
log!("[TYPEAHEAD] Selection made"); log!("[TYPEAHEAD] Selection made");
if let Ok(data) = suggestion.into_serde::<WikidataSuggestion>() { if let Ok(data) = suggestion.into_serde::<WikidataSuggestion>() {
log!("[TYPEAHEAD] Selected suggestion: {:?}", data);
on_select.call(data.clone()); on_select.call(data.clone());
if let Some(input) = node_ref.get() { if let Some(input) = node_ref.get() {
input.set_value(&data.label); input.set_value(&data.label);
@ -271,7 +267,7 @@ fn initialize_typeahead(
log!("[ERROR] Failed to deserialize suggestion"); log!("[ERROR] Failed to deserialize suggestion");
} }
}) as Box<dyn FnMut(web_sys::Event, JsValue)>); }) as Box<dyn FnMut(web_sys::Event, JsValue)>);
// Register global handler // Register global handler
let handler_name = format!("handler_{}", input_id); let handler_name = format!("handler_{}", input_id);
js_sys::Reflect::set( js_sys::Reflect::set(
@ -281,7 +277,7 @@ fn initialize_typeahead(
).unwrap(); ).unwrap();
closure.forget(); closure.forget();
// Initialization script with enhanced logging // Initialization script
let init_script = format!( let init_script = format!(
r#" r#"
console.log('[JS] Starting Typeahead init for #{id}'); console.log('[JS] Starting Typeahead init for #{id}');
@ -295,52 +291,39 @@ fn initialize_typeahead(
}}, }},
{{ {{
name: 'suggestions', name: 'suggestions',
display: function(data) {{ display: 'label',
console.log('[JS] Display function called with data:', data); source: bloodhound.ttAdapter(),
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: {{ templates: {{
suggestion: function(data) {{ suggestion: function(data) {{
console.log('[JS] Rendering suggestion:', 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;
}}
return $('<div>') return $('<div>')
.addClass('suggestion-item') .addClass('suggestion-item')
.append($('<div>').addClass('label').text(data.displayLabel || data.label)) .append($('<div>').addClass('label').text(label))
.append($('<div>').addClass('description').text(data.displayDescription || data.description)); .append($('<div>').addClass('description').text(description));
}}, }},
empty: function() {{ empty: $('<div>').addClass('empty-suggestion').text('No matches found')
console.log('[JS] No suggestions found');
return $('<div>').addClass('empty-suggestion').text('No matches found');
}}
}} }}
}} }}
) )
.on('typeahead:asyncreceive', function(ev, dataset, suggestions) {{ .on('typeahead:asyncreceive', function(ev, dataset, suggestions) {{
console.log('[JS] Received suggestions in typeahead:asyncreceive:', suggestions); console.log('[JS] Received suggestions:', suggestions);
if (suggestions && suggestions.length > 0) {{ if (suggestions && suggestions.length > 0) {{
console.log('[JS] Suggestions passed to dropdown:', suggestions);
$(this).data('ttTypeahead').dropdown.open(); $(this).data('ttTypeahead').dropdown.open();
}} else {{
console.warn('[JS] No suggestions received or suggestions are empty.');
}} }}
}}) }})
.on('typeahead:select', function(ev, suggestion) {{ .on('typeahead:select', function(ev, suggestion) {{
console.log('[JS] Selection event received with suggestion:', suggestion); console.log('[JS] Selection data:', JSON.stringify(suggestion, null, 2));
window['{handler}'](ev, suggestion); window['{handler}'](ev, suggestion);
}}); }});
console.log('[JS] Typeahead initialized successfully');
}} catch (e) {{ }} catch (e) {{
console.error('[JS] Typeahead init error:', e); console.error('[JS] Typeahead init error:', e);
}} }}

View file

@ -11,39 +11,9 @@ pub struct Item {
pub custom_properties: HashMap<String, String>, pub custom_properties: HashMap<String, String>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Deserialize, Clone, Debug)]
pub struct WikidataSuggestion { pub struct WikidataSuggestion {
pub id: String, pub id: String,
#[serde(default)]
pub label: String, pub label: String,
#[serde(default)] pub description: Option<String>,
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,
} }