@@ -1738,6 +1738,18 @@ finance/ age) use `ago`. No schema change, no new data source, no new network calls. Deployed to production on 2026-05-22 via `git push server master` (commit `39a863e`).- **2026-05-22: search "Add" affordance fixed for short tickers.** Bug: the Search page offered to add an untracked symbol only when the query returned zero results, but search matches `ticker LIKE '%q%'` (a substring), so a one or two letter ticker like `W` (Wayfair) partially matched a crowd of other tickers and the offer never showed, leaving `W` unaddable. Fix (`routes/search.rs`): the offer is now gated on an exact-ticker `EXISTS` check against `symbols`, not on `results.is_empty()`, so a query that is a valid, untracked ticker is offered even when the substring search found other symbols. The "do not nag a company-name search" intent is kept: the offer still shows only when the query matched nothing, or matched at least one result as a ticker substring (a name-only search such as `Inc` does not trigger it). The add panel can now render above a populated results grid.---
modified
src/routes/search.rs
@@ -115,12 +115,21 @@ async fn search_page(Query(sq): Query<SearchQuery>, State(state): State<AppState // fundamentals a standing is rolled from. attach_standings(&state, &mut results).await; // Offer "Add" only on a genuine miss: nothing matched, the query is a // plausible ticker (one `POST /api/symbols` would accept), and it is not // already a symbol the kind filter happened to hide. Requiring zero // results keeps the offer from nagging when a name search did find hits. let show_add = results.is_empty() && valid_ticker(&query).is_some() // Does the query land on at least one tracked ticker (as a substring)? If // so it reads as a ticker search: a bare ticker like "W" shares letters // with the tickers it returns. A pure company-name search ("Apple", // "bank") matches only names, with the query absent from every ticker. let ticker_hit = results.iter().any(|c| c.ticker.contains(query.as_str())); // Offer "Add" when the query names a ticker the universe does not hold. // The gate is an exact-ticker check (EXISTS on `symbols`), not "zero // results": searching "W" for Wayfair partially LIKE-matches a crowd of // other tickers, yet "W" itself is still untracked and addable. The query // must be a plausible ticker (one `POST /api/symbols` would accept). The // offer is kept off a pure company-name search, where it would be noise: // it shows only when the query matched nothing, or matched as a ticker. let show_add = valid_ticker(&query).is_some() && (results.is_empty() || ticker_hit) && !sqlx::query_scalar::<_, bool>("SELECT EXISTS(SELECT 1 FROM symbols WHERE ticker = ?)") .bind(&query) .fetch_one(&state.pool)