feat(web): merge search filters into one row and tidy episode versions (POLISH-1)
- H&R becomes a quick-filter facet (yes/no/unknown, with counts) on search results and the site resources tab; drop the old free/H&R selects, keep size range and sort on the same row (mobile: drawer), single clear button - legacy free/hr URL params migrate into quick filters; stored selections keep the same localStorage key - search timing and query terms move into TechInfo - library episode versions: click the whole row to expand, versions listed as sibling rows, details only under the selected version, raw values in TechInfo - slot comparison gaps get reason codes (matchGapText in contracts reasons.ts): "可换成 2160p", "缺 HDR 版本" Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
12bc4664b6
commit
71b129d85a
19 files changed
+668
-290
No files matched your search
@@ -175,7 +175,9 @@ describe.skipIf(!hasPg)("LIBV-1 入库版本", () => {
|
||||
|
||||
const [e1, e2, e3] = s1.episodes;
|
||||
expect(e1.versions.map((x: { assetId: string }) => x.assetId).sort()).toEqual([ids.e1dv, ids.e1bd].sort());
|
||||
expect(e1.matches).toEqual([{ slotId: v.targets[0].slotId, state: "satisfied", assetId: ids.e1dv, reasons: [] }]);
|
||||
expect(e1.matches).toEqual([
|
||||
{ slotId: v.targets[0].slotId, state: "satisfied", assetId: ids.e1dv, reasons: [], codes: [] },
|
||||
]);
|
||||
const dv = e1.versions.find((x: { assetId: string }) => x.assetId === ids.e1dv);
|
||||
expect(dv).toMatchObject({
|
||||
resolution: "2160p",
|
||||
|
||||
@@ -355,6 +355,7 @@ export const searchFacetInput = (r: BaseRelease): FacetInput => ({
|
||||
uploadFactor: r.promotion.uploadFactor,
|
||||
expiresAt: r.promotion.expiresAt,
|
||||
},
|
||||
hr: r.hr,
|
||||
});
|
||||
|
||||
/** 快速筛选(SEARCH-3):先过旧筛选参数,再按 qf 多选筛选并统计分面 */
|
||||
|
||||
@@ -109,6 +109,8 @@ interface LightRow {
|
||||
parsed: unknown;
|
||||
promotion: unknown;
|
||||
promotion_expires_at: Date | null;
|
||||
/** raw._site.hr(列表快照解析的 H&R);没有为 null */
|
||||
hr: unknown;
|
||||
}
|
||||
|
||||
const num = (v: unknown): number | null => (typeof v === "number" && Number.isFinite(v) ? v : null);
|
||||
@@ -140,6 +142,7 @@ export function lightFacetInput(r: LightRow, site: Pick<SiteRow, "id" | "name">)
|
||||
uploadFactor: num(raw.uploadFactor),
|
||||
expiresAt: expires && !Number.isNaN(expires.getTime()) ? expires.toISOString() : null,
|
||||
},
|
||||
hr: r.hr === true || r.hr === false ? r.hr : "unknown",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -180,6 +183,7 @@ export async function listSiteReleases(
|
||||
sql<unknown>`r.parsed`.as("parsed"),
|
||||
sql<unknown>`r.promotion`.as("promotion"),
|
||||
"r.promotion_expires_at",
|
||||
sql<unknown>`r.raw->'_site'->'hr'`.as("hr"),
|
||||
])
|
||||
.where("r.site_id", "=", site.id);
|
||||
if (q.q) {
|
||||
|
||||
Reference in new issue
Block a user