fix(wallet,ui): show only filterable text in the 模型/Key transaction cells - #231
Merged
Merged
Conversation
…ells The transactions table filters its 模型 and Key columns server-side: `tx_where` builds `model LIKE ?` and `COALESCE(...) LIKE ?` from the text the user types into the column's `.th-filter` input. Both cells, however, invented a fallback for rows that have no stored value: `txsToView` rendered `t.model || txType(t.type)` and `t.key_name || t.key_label || (consume||earn ? "—" : txType(t.type))` — a LOCALIZED type label (赠送 / 过期). The server has no language pack (there is no Rust-side label table at all), so that string can never match the SQL, and a user who filters by the text they just saw on screen gets 0 rows. Rows that do have a stored value were already fine, which is what makes the column look usable: it is a filter that lies, not a missing feature (the 类型 column and the top tabs cover type filtering correctly). The invariant is "every piece of text a cell can display must be matched by that column's filter". Since the offending fallback is localized, the fix belongs on the display side: no-value rows now show the language-neutral placeholder `—`, which the sibling 用户 column (`t.user_name || "—"`) and the Key column's own consume/earn branch already used. Both sides then share one definition: front `t.model || "—"` `t.key_name || t.key_label || "—"` SQL `COALESCE(NULLIF(model, ''), '—')` `COALESCE(NULLIF(ak.name,''), NULLIF(<key_label>, ''), '—')` The SQL needs the `NULLIF` layer-by-layer because JS `||` treats the empty string as missing while `COALESCE` only falls through on NULL — the same asymmetry C2101 fixed for an empty `api_keys.name`. The exported CSV needs no change: it prints `t.model` / `t.key` from the same view row, so it follows the cell (the same-口径 rule established for 点数 in C2054 and for 时间 in C2111). Tests: `cargo test` 235 -> 238 (three new tests: the placeholder must be findable through both column filters, the real-model-name path must keep working, and a negative control asserting the localized type label is NOT a filterable value — the guard for the rejected alternative of hardcoding Chinese labels into SQL). Verified with a jsdom probe (real ui/index.html + four real scripts, only `fetch` and the download stubbed) that reads the rendered cells, the wire parameter and the exported CSV bytes. A/B across both instruments, baseline pinned to 2be0d54 via `git show` (the driver aborts if the baseline equals the working tree) — red sets: v0_orig pre-change both sides B1 B2 B3 B4 R_main m_nosql display fixed, filters reverted R_main m_nodisp filters fixed, display kept B1 B2 B3 B4 m_label display kept + zh labels in the SQL B1 B2 B3 B4 R_main R_neg live the working tree none (14/14) m_nosql and m_nodisp are disjoint, so each half is pinned by its own instrument; the built-in control A0 and all five non-axis controls are green in every leg. Recorded, not claimed away: m_label's red set contains v0_orig's (the competing design fails the same front-end checks plus the negative control), and it is rejected on the argument that a localized label cannot live in SQL.
3 tasks done
argszero
added a commit
that referenced
this pull request
Sep 27, 2026
…315) Three sentences in ui/README.md named something a reader cannot find. Each is corrected in place, with the change that invalidated it named (the idiom the file already uses for its endpoint-card and login-page sections). 1. bump() wiring. The line claimed 4 balance-change sites including "raise-request approval" and an "isMe" guard. The code has 3 sites (4 bump() calls: two for the wallet top-up, one for the operator crediting themselves, one for the chat deduction). The approval site lived in the local mock branch that mutated D.USER.balance in place; 89963f3 (#94, zero-mock) deleted that branch and approveRaise() now only reloads the list, so the bump went with it. The same refactor inlined the self-top-up guard to `u.email === D.USER.email`. 2. The login "remember me" bullet still said a `demo-hint` element showed the demo account. 35291b3 (#90, v0.6.0 demo-seed removal) deleted that element; the class rule in ui/css/style.css is now orphaned and the `login.demo` pack key already sits in the unreachable sunset list. 3. The C2113 section cited `transactions_model_and_key_filters_match_the_displayed_placeholder`. No such test ever existed: dfa7318 (#231) added the README line and a test named `tx_model_and_key_filters_match_the_displayed_placeholder` in the same commit. The name now matches the file it cites. The corrections describe what was removed rather than re-printing the removed identifiers, so a grep for a stale name still lands nowhere (this is how the C2138 correction above already reads; the login-page section is the deliberate exception -- there the deleted selectors are the section's own subject). Docs only: no production code, no i18n key, no cache-bust token. The orphaned .demo-hint rule is left alone on purpose (removing CSS is a separate, cache-busting change). cargo test 413/0, fmt/clippy clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
交易表的「模型」列与「Key」列是服务端筛选(
tx_where的model LIKE ?/COALESCE(...) LIKE ?),但两列的单元格对「库内无值」的行会自己编一个兜底文案:txsToView渲染t.model || txType(t.type)与t.key_name || t.key_label || (consume||earn ? "—" : txType(t.type))—— 兜底是本地化类型名(「赠送」/「过期」)。服务端没有任何语言包(全仓没有 Rust 侧文案表),这个字符串在 SQL 里永远匹配不到 ⇒ 用户按屏幕上刚看到的文案筛选得 0 行。有模型名/Key 名的行一切正常 —— 这正是问题所在:列看起来是可筛选的,所以这是会说谎的筛选,不是缺功能(类型有专门的列与 tab,控件正确)。
不变量是「单元格能显示出来的每一段文字,都必须能被该列筛选命中」。既然惹事的兜底是本地化的,修法就落在显示侧:无值行改显示语言中性的占位符
—(同列族的「用户」列t.user_name || "—"与 Key 列自己的 consume/earn 分支早已如此),两侧共用同一条定义:txsToViewtx_wheret.model || "—"COALESCE(NULLIF(model, ''), '—')t.key_name || t.key_label || "—"COALESCE(NULLIF(ak.name,''), NULLIF(<key_label 表达式>, ''), '—')SQL 侧要逐层
NULLIF:JS||视空串为缺失,而COALESCE只对 NULL 回退 —— 与 C2101 修「空名api_keys.name」时同一个不对称。导出 CSV 无需改动:它打印同一份视图行的
t.model/t.key,自动跟随单元格(点数 C2054、时间 C2111 已立的「同口径」规则)。Related Issue
(无关联 issue)
Changes
—;服务端两列的 LIKE 表达式搜同一个文案TxColFilters字段文档与ui/README.md(新增「单元格文案 = 筛选口径」一节)ui/index.htmlcache-bustapp.js?v=20260914-7→-8Tests
cargo test全部通过(235 → 238,新增 3 个)cargo fmt --check通过cargo clippy --all-targets干净tx_model_and_key_filters_match_the_displayed_placeholder—— 按单元格显示的文案筛选必须命中该行(修前 0 行)tx_model_filter_still_matches_real_model_names—— 阳性对照:真实模型名照旧命中tx_model_filter_does_not_take_localized_type_labels—— 阴性对照:类型名(「赠送」)不属于该列口径A/B(两套仪器、五个腿,基线钉在不可变提交
2be0d54;git show取基线,基线==修复即中止)ui/index.html+ 四个真脚本(只 stubfetch与下载),读渲染后的单元格文本、模型筛选框发到线上的参数、以及导出的 CSV 字节;11 项检查cargo test tx_(3 个测试)v0_orig2be0d54)B1 B2 B3 B4 R_mainm_nosqlR_mainm_nodispB1 B2 B3 B4m_labelB1 B2 B3 B4 R_main R_neglivem_nosql与m_nodisp互不相交 ⇒ 每一半都被各自的仪器钉住;A0内建对照与全部 5 个非轴对照(A1 A2 A3 C1 D1 E1)每腿皆绿m_label的红集包含v0_orig(竞争方案连同样的前端检查一起挂,外加阴性对照);它被拒的理由是「本地化文案不能住进 SQL」(服务端无语言包,硬编码中文等于把 i18n 塞进 SQL)—— 该结论由R_neg这条阴性对照钉住Checklist
fix/)