Compare commits
2 commits
102f69fd29
...
486bf9cbad
Author | SHA1 | Date | |
---|---|---|---|
486bf9cbad | |||
b017df9b35 |
9 changed files with 95 additions and 48 deletions
|
@ -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:3000) where the server serves the content. Use it in your server setup.
|
# The IP and port (ex: 127.0.0.1:3004) where the server serves the content. Use it in your server setup.
|
||||||
site-addr = "127.0.0.1:3000"
|
site-addr = "127.0.0.1:3004"
|
||||||
# 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.
|
||||||
|
|
|
@ -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:3000](http://localhost:3000)
|
3. Open your browser at [localhost:3004](http://localhost:3004)
|
||||||
|
|
||||||
## **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:3000](http://localhost:3000)
|
3. Access the application at: [http://localhost:3004](http://localhost:3004)
|
||||||
|
|
||||||
### **Collaboration**
|
### **Collaboration**
|
||||||
We welcome contributions! Here’s how you can help:
|
We welcome contributions! Here’s how you can help:
|
||||||
|
|
|
@ -2,7 +2,7 @@ services:
|
||||||
app:
|
app:
|
||||||
build: .
|
build: .
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3004:3004"
|
||||||
volumes:
|
volumes:
|
||||||
- ./compareware.db:/app/compareware.db
|
- ./compareware.db:/app/compareware.db
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -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 3000
|
EXPOSE 3004
|
||||||
ENV LEPTOS_SITE_ADDR=0.0.0.0:3000
|
ENV LEPTOS_SITE_ADDR=0.0.0.0:3004
|
||||||
ENV LEPTOS_SITE_ROOT="site"
|
ENV LEPTOS_SITE_ROOT="site"
|
||||||
CMD ["./compareware"]
|
CMD ["./compareware"]
|
|
@ -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:3000',
|
// baseURL: 'http://localhost:3004',
|
||||||
|
|
||||||
/* 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: 3000,
|
// port: 3004,
|
||||||
// },
|
// },
|
||||||
});
|
});
|
||||||
|
|
|
@ -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:3000/");
|
await page.goto("http://localhost:3004/");
|
||||||
|
|
||||||
await expect(page).toHaveTitle("Welcome to Leptos");
|
await expect(page).toHaveTitle("Welcome to Leptos");
|
||||||
|
|
||||||
|
|
|
@ -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.label.clone();
|
item.name = suggestion.display.label.value.clone();
|
||||||
item.description = suggestion.description.clone().unwrap_or_default();
|
item.description = suggestion.display.description.value.clone();
|
||||||
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
|
||||||
|
|
|
@ -144,26 +144,29 @@ 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();
|
||||||
|
|
||||||
// Create nested display structure matching API response
|
// Set flattened structure for Typeahead compatibility
|
||||||
let display = Object::new();
|
Reflect::set(&obj, &"id".into(), &suggestion.id.clone().into()).unwrap();
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
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);
|
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)>);
|
||||||
|
|
||||||
|
@ -259,6 +262,7 @@ 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);
|
||||||
|
@ -267,7 +271,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(
|
||||||
|
@ -277,7 +281,7 @@ fn initialize_typeahead(
|
||||||
).unwrap();
|
).unwrap();
|
||||||
closure.forget();
|
closure.forget();
|
||||||
|
|
||||||
// Initialization script
|
// Initialization script with enhanced logging
|
||||||
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}');
|
||||||
|
@ -291,39 +295,52 @@ fn initialize_typeahead(
|
||||||
}},
|
}},
|
||||||
{{
|
{{
|
||||||
name: 'suggestions',
|
name: 'suggestions',
|
||||||
display: 'label',
|
display: function(data) {{
|
||||||
source: bloodhound.ttAdapter(),
|
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: {{
|
templates: {{
|
||||||
suggestion: function(data) {{
|
suggestion: function(data) {{
|
||||||
// Handle nested Wikidata structure
|
console.log('[JS] Rendering suggestion:', data);
|
||||||
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(label))
|
.append($('<div>').addClass('label').text(data.displayLabel || data.label))
|
||||||
.append($('<div>').addClass('description').text(description));
|
.append($('<div>').addClass('description').text(data.displayDescription || data.description));
|
||||||
}},
|
}},
|
||||||
empty: $('<div>').addClass('empty-suggestion').text('No matches found')
|
empty: function() {{
|
||||||
|
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:', suggestions);
|
console.log('[JS] Received suggestions in typeahead:asyncreceive:', 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 data:', JSON.stringify(suggestion, null, 2));
|
console.log('[JS] Selection event received with suggestion:', suggestion);
|
||||||
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);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -11,9 +11,39 @@ pub struct Item {
|
||||||
pub custom_properties: HashMap<String, String>,
|
pub custom_properties: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct WikidataSuggestion {
|
pub struct WikidataSuggestion {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
#[serde(default)]
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub description: Option<String>,
|
#[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,
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue