add FTS5 full-text product search

Adds SQLite FTS5 search index with BM25 ranking across product title,
category, variant attributes, and description. Search modal now has
live results with thumbnails, prices, and click-to-navigate. Index
rebuilds automatically after each provider sync.

Also fixes Access syntax on Product/ProductImage structs (Map.get
instead of bracket notation) which was causing crashes when real
products were loaded from the database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-13 07:29:19 +00:00
parent 35e0386abb
commit 037cd168cd
19 changed files with 603 additions and 34 deletions

View File

@@ -9,6 +9,8 @@
cart_drawer_open={assigns[:cart_drawer_open] || false}
cart_status={assigns[:cart_status]}
active_page="cart"
search_query={assigns[:search_query] || ""}
search_results={assigns[:search_results] || []}
>
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<.page_title text="Your basket" />

View File

@@ -9,6 +9,8 @@
cart_drawer_open={assigns[:cart_drawer_open] || false}
cart_status={assigns[:cart_status]}
active_page="checkout"
search_query={assigns[:search_query] || ""}
search_results={assigns[:search_results] || []}
>
<main id="main-content" class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
<%= if @order && @order.payment_status == "paid" do %>

View File

@@ -9,6 +9,8 @@
cart_drawer_open={assigns[:cart_drawer_open] || false}
cart_status={assigns[:cart_status]}
active_page="collection"
search_query={assigns[:search_query] || ""}
search_results={assigns[:search_results] || []}
>
<main id="main-content">
<.collection_header title="All Products" product_count={length(@preview_data.products)} />

View File

@@ -9,6 +9,8 @@
cart_drawer_open={assigns[:cart_drawer_open] || false}
cart_status={assigns[:cart_status]}
active_page="contact"
search_query={assigns[:search_query] || ""}
search_results={assigns[:search_results] || []}
>
<main id="main-content" class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-16">
<.hero_section

View File

@@ -9,6 +9,8 @@
cart_drawer_open={assigns[:cart_drawer_open] || false}
cart_status={assigns[:cart_status]}
active_page={@active_page}
search_query={assigns[:search_query] || ""}
search_results={assigns[:search_results] || []}
>
<main id="main-content" class="content-page" style="background-color: var(--t-surface-base);">
<%= if assigns[:hero_background] do %>

View File

@@ -10,6 +10,8 @@
cart_status={assigns[:cart_status]}
active_page="error"
error_page
search_query={assigns[:search_query] || ""}
search_results={assigns[:search_results] || []}
>
<main
id="main-content"

View File

@@ -9,6 +9,8 @@
cart_drawer_open={assigns[:cart_drawer_open] || false}
cart_status={assigns[:cart_status]}
active_page="home"
search_query={assigns[:search_query] || ""}
search_results={assigns[:search_results] || []}
>
<main id="main-content">
<.hero_section

View File

@@ -9,6 +9,8 @@
cart_drawer_open={assigns[:cart_drawer_open] || false}
cart_status={assigns[:cart_status]}
active_page="pdp"
search_query={assigns[:search_query] || ""}
search_results={assigns[:search_results] || []}
>
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<.breadcrumb