mirror of
https://github.com/samsonjs/spirit-tracker.git
synced 2026-04-27 15:07:43 +00:00
feat: Linker page fixes
This commit is contained in:
parent
d47f2fe9ee
commit
028f0d741a
1 changed files with 54 additions and 12 deletions
|
|
@ -184,14 +184,24 @@ function storesOverlap(aItem, bItem) {
|
||||||
|
|
||||||
/* ---------------- Mapping helpers ---------------- */
|
/* ---------------- Mapping helpers ---------------- */
|
||||||
|
|
||||||
function buildMappedSkuSet(links) {
|
function buildMappedSkuSet(links, rules) {
|
||||||
const s = new Set();
|
const s = new Set();
|
||||||
for (const x of Array.isArray(links) ? links : []) {
|
|
||||||
const a = String(x?.fromSku || "").trim();
|
function add(k) {
|
||||||
const b = String(x?.toSku || "").trim();
|
const x = String(k || "").trim();
|
||||||
if (a) s.add(a);
|
if (!x) return;
|
||||||
if (b) s.add(b);
|
s.add(x);
|
||||||
|
if (rules && typeof rules.canonicalSku === "function") {
|
||||||
|
const c = String(rules.canonicalSku(x) || "").trim();
|
||||||
|
if (c) s.add(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const x of Array.isArray(links) ? links : []) {
|
||||||
|
add(x?.fromSku);
|
||||||
|
add(x?.toSku);
|
||||||
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -651,8 +661,23 @@ export async function renderSkuLinker($app) {
|
||||||
const allRows = Array.isArray(idx.items) ? idx.items : [];
|
const allRows = Array.isArray(idx.items) ? idx.items : [];
|
||||||
|
|
||||||
const URL_BY_SKU_STORE = new Map();
|
const URL_BY_SKU_STORE = new Map();
|
||||||
|
|
||||||
|
function urlQuality(r) {
|
||||||
|
// Prefer “better” URLs if dupes exist.
|
||||||
|
// Heuristics: longer path > shorter, avoid obvious legacy/short generic slugs when possible.
|
||||||
|
const u = String(r?.url || "").trim();
|
||||||
|
if (!u) return -1;
|
||||||
|
let s = 0;
|
||||||
|
s += u.length; // more specific tends to be longer
|
||||||
|
if (/\bproduct\/\d+\//.test(u)) s += 50;
|
||||||
|
if (/[a-z0-9-]{8,}/i.test(u)) s += 10; // sluggy
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
for (const r of allRows) {
|
for (const r of allRows) {
|
||||||
|
// Keep active only
|
||||||
if (!r || r.removed) continue;
|
if (!r || r.removed) continue;
|
||||||
|
|
||||||
const skuKey = String(keySkuForRow(r) || "").trim();
|
const skuKey = String(keySkuForRow(r) || "").trim();
|
||||||
if (!skuKey) continue;
|
if (!skuKey) continue;
|
||||||
|
|
||||||
|
|
@ -662,13 +687,30 @@ export async function renderSkuLinker($app) {
|
||||||
|
|
||||||
let m = URL_BY_SKU_STORE.get(skuKey);
|
let m = URL_BY_SKU_STORE.get(skuKey);
|
||||||
if (!m) URL_BY_SKU_STORE.set(skuKey, (m = new Map()));
|
if (!m) URL_BY_SKU_STORE.set(skuKey, (m = new Map()));
|
||||||
if (!m.has(storeLabel)) m.set(storeLabel, url);
|
|
||||||
|
const prevUrl = m.get(storeLabel);
|
||||||
|
if (!prevUrl) {
|
||||||
|
m.set(storeLabel, url);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If duplicates exist, prefer the “better” URL deterministically.
|
||||||
|
const prevScore = urlQuality({ url: prevUrl });
|
||||||
|
const nextScore = urlQuality(r);
|
||||||
|
|
||||||
|
if (nextScore > prevScore) {
|
||||||
|
m.set(storeLabel, url);
|
||||||
|
} else if (nextScore === prevScore && url < prevUrl) {
|
||||||
|
// stable tie-break
|
||||||
|
m.set(storeLabel, url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const allAgg = aggregateBySku(allRows, (x) => x);
|
const allAgg = aggregateBySku(allRows, (x) => x);
|
||||||
|
|
||||||
const meta = await loadSkuMetaBestEffort();
|
const meta = await loadSkuMetaBestEffort();
|
||||||
const mappedSkus = buildMappedSkuSet(meta.links || []);
|
const mappedSkus = buildMappedSkuSet(meta.links || [], rules);
|
||||||
let ignoreSet = rules.ignoreSet;
|
let ignoreSet = rules.ignoreSet;
|
||||||
|
|
||||||
function isIgnoredPair(a, b) {
|
function isIgnoredPair(a, b) {
|
||||||
|
|
@ -895,7 +937,7 @@ export async function renderSkuLinker($app) {
|
||||||
rules = await loadSkuRules();
|
rules = await loadSkuRules();
|
||||||
ignoreSet = rules.ignoreSet;
|
ignoreSet = rules.ignoreSet;
|
||||||
|
|
||||||
const rebuilt = buildMappedSkuSet(rules.links || []);
|
const rebuilt = buildMappedSkuSet(rules.links || [], rules);
|
||||||
mappedSkus.clear();
|
mappedSkus.clear();
|
||||||
for (const x of rebuilt) mappedSkus.add(x);
|
for (const x of rebuilt) mappedSkus.add(x);
|
||||||
|
|
||||||
|
|
@ -952,7 +994,7 @@ export async function renderSkuLinker($app) {
|
||||||
rules = await loadSkuRules();
|
rules = await loadSkuRules();
|
||||||
ignoreSet = rules.ignoreSet;
|
ignoreSet = rules.ignoreSet;
|
||||||
|
|
||||||
const rebuilt = buildMappedSkuSet(rules.links || []);
|
const rebuilt = buildMappedSkuSet(rules.links || [], rules);
|
||||||
mappedSkus.clear();
|
mappedSkus.clear();
|
||||||
for (const x of rebuilt) mappedSkus.add(x);
|
for (const x of rebuilt) mappedSkus.add(x);
|
||||||
|
|
||||||
|
|
@ -1055,7 +1097,7 @@ export async function renderSkuLinker($app) {
|
||||||
rules = await loadSkuRules();
|
rules = await loadSkuRules();
|
||||||
ignoreSet = rules.ignoreSet;
|
ignoreSet = rules.ignoreSet;
|
||||||
|
|
||||||
const rebuilt = buildMappedSkuSet(rules.links || []);
|
const rebuilt = buildMappedSkuSet(rules.links || [], rules);
|
||||||
mappedSkus.clear();
|
mappedSkus.clear();
|
||||||
for (const x of rebuilt) mappedSkus.add(x);
|
for (const x of rebuilt) mappedSkus.add(x);
|
||||||
|
|
||||||
|
|
@ -1136,7 +1178,7 @@ export async function renderSkuLinker($app) {
|
||||||
rules = await loadSkuRules();
|
rules = await loadSkuRules();
|
||||||
ignoreSet = rules.ignoreSet;
|
ignoreSet = rules.ignoreSet;
|
||||||
|
|
||||||
const rebuilt = buildMappedSkuSet(rules.links || []);
|
const rebuilt = buildMappedSkuSet(rules.links || [], rules);
|
||||||
mappedSkus.clear();
|
mappedSkus.clear();
|
||||||
for (const x of rebuilt) mappedSkus.add(x);
|
for (const x of rebuilt) mappedSkus.add(x);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue