Spaces:
Running
Running
SP thêm qua URL: luôn nằm trên danh sách SP chính (kể cả không có SKU), không trùng lặp (dedup theo sku/slug/link/tên trong danh sách added + với catalog gốc), và khi có giảm giá thì vào BIGSALE (>=40%) hoặc FLASHSALE (<40%) — tự refresh panel sau khi thêm; cache-bust gc=65/v=14; rebuild 1787031022
Browse files- Dockerfile +1 -1
- index.html +2 -2
- src/vaix-add-product.js +4 -0
- src/vaix-rag.js +32 -4
Dockerfile
CHANGED
|
@@ -11,4 +11,4 @@ EXPOSE 7860
|
|
| 11 |
USER bun
|
| 12 |
CMD ["bun", "index.ts"]
|
| 13 |
|
| 14 |
-
# Rebuild trigger:
|
|
|
|
| 11 |
USER bun
|
| 12 |
CMD ["bun", "index.ts"]
|
| 13 |
|
| 14 |
+
# Rebuild trigger: 1787031022 url-added-on-main-list-dedup-bigsale-flashsale
|
index.html
CHANGED
|
@@ -829,7 +829,7 @@
|
|
| 829 |
first paint — the welcome/avatar-picker modal renders immediately. -->
|
| 830 |
<script defer src="./src/cart-quote.js?gc=34"></script> <!-- Cart + Quote module (Giỏ hàng / Báo giá) -->
|
| 831 |
<script defer src="./src/customers.js?gc=18"></script> <!-- Customer list (Danh sách khách hàng) -->
|
| 832 |
-
<script defer src="./src/vaix-rag.js?gc=
|
| 833 |
<script defer src="./src/greeting-news.js?gc=41"></script> <!-- Greeting HOT news cards + source links -->
|
| 834 |
<script defer src="./src/order-sync.js?gc=17"></script> <!-- Order sync with V.AISTUDIO backend -->
|
| 835 |
<script type="module" src="./src/app.js?gc=61"></script>
|
|
@@ -861,7 +861,7 @@
|
|
| 861 |
|
| 862 |
<!-- ── Thêm SP (Add Product) module: 100% port from V.AISTUDIO, gated by the
|
| 863 |
homepage V.AISTUDIO access code (window.isVaAdmin()). ── -->
|
| 864 |
-
<script defer src="./src/vaix-add-product.js?v=
|
| 865 |
<script defer src="./src/vaix-bulk-import.js?v=7"></script>
|
| 866 |
</body>
|
| 867 |
</html>
|
|
|
|
| 829 |
first paint — the welcome/avatar-picker modal renders immediately. -->
|
| 830 |
<script defer src="./src/cart-quote.js?gc=34"></script> <!-- Cart + Quote module (Giỏ hàng / Báo giá) -->
|
| 831 |
<script defer src="./src/customers.js?gc=18"></script> <!-- Customer list (Danh sách khách hàng) -->
|
| 832 |
+
<script defer src="./src/vaix-rag.js?gc=65"></script> <!-- V.AI STUDIO RAG Module - loads FASTER via light index; lazy galleries; spec-aware search -->
|
| 833 |
<script defer src="./src/greeting-news.js?gc=41"></script> <!-- Greeting HOT news cards + source links -->
|
| 834 |
<script defer src="./src/order-sync.js?gc=17"></script> <!-- Order sync with V.AISTUDIO backend -->
|
| 835 |
<script type="module" src="./src/app.js?gc=61"></script>
|
|
|
|
| 861 |
|
| 862 |
<!-- ── Thêm SP (Add Product) module: 100% port from V.AISTUDIO, gated by the
|
| 863 |
homepage V.AISTUDIO access code (window.isVaAdmin()). ── -->
|
| 864 |
+
<script defer src="./src/vaix-add-product.js?v=14"></script>
|
| 865 |
<script defer src="./src/vaix-bulk-import.js?v=7"></script>
|
| 866 |
</body>
|
| 867 |
</html>
|
src/vaix-add-product.js
CHANGED
|
@@ -1236,6 +1236,10 @@
|
|
| 1236 |
persisted = !!(pj2 && pj2.promos && pj2.promos.productEdits && pj2.promos.productEdits[String(sku || slug)]);
|
| 1237 |
}
|
| 1238 |
} catch (e2) { /* mirror is best-effort */ }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1239 |
}
|
| 1240 |
if (!persisted) saveAdded(list);
|
| 1241 |
|
|
|
|
| 1236 |
persisted = !!(pj2 && pj2.promos && pj2.promos.productEdits && pj2.promos.productEdits[String(sku || slug)]);
|
| 1237 |
}
|
| 1238 |
} catch (e2) { /* mirror is best-effort */ }
|
| 1239 |
+
// Notify FLASHSALE / BIGSALE / COMBO panels to rebuild now that a new
|
| 1240 |
+
// (possibly discounted) product has been injected into the catalog.
|
| 1241 |
+
try { window.dispatchEvent(new CustomEvent('vai-promo-changed')); } catch (e3) {}
|
| 1242 |
+
try { window.dispatchEvent(new CustomEvent('vai-catalog-loaded')); } catch (e3) {}
|
| 1243 |
}
|
| 1244 |
if (!persisted) saveAdded(list);
|
| 1245 |
|
src/vaix-rag.js
CHANGED
|
@@ -1119,11 +1119,39 @@ async function load() {
|
|
| 1119 |
images: Array.isArray(p.imgs) ? p.imgs : (Array.isArray(p.g) ? p.g : []), link: p.l || "",
|
| 1120 |
_addedBy: "products_url_added"
|
| 1121 |
}));
|
| 1122 |
-
// Dedup
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1123 |
const baseSkus = new Set(allProducts.map((p) => String(p.sku).toLowerCase()).filter(Boolean));
|
| 1124 |
-
const
|
| 1125 |
-
|
| 1126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1127 |
}
|
| 1128 |
}
|
| 1129 |
} catch (e) { console.warn("[VAIX] products_url_added merge skipped:", e && e.message); }
|
|
|
|
| 1119 |
images: Array.isArray(p.imgs) ? p.imgs : (Array.isArray(p.g) ? p.g : []), link: p.l || "",
|
| 1120 |
_addedBy: "products_url_added"
|
| 1121 |
}));
|
| 1122 |
+
// Dedup added products against base catalog AND against each other
|
| 1123 |
+
// (by sku, slug, link, or normalized name) so a URL added twice never
|
| 1124 |
+
// yields two identical entries on the main product list. Products
|
| 1125 |
+
// WITHOUT a sku are still kept (matched by link/name) — they must
|
| 1126 |
+
// appear on the main list and in FLASHSALE.
|
| 1127 |
const baseSkus = new Set(allProducts.map((p) => String(p.sku).toLowerCase()).filter(Boolean));
|
| 1128 |
+
const baseSlugs = new Set(allProducts.map((p) => String(p.slug || "").toLowerCase()).filter(Boolean));
|
| 1129 |
+
const seenKeys = new Set();
|
| 1130 |
+
const uniqueAdded = added.filter((p) => {
|
| 1131 |
+
const sk = String(p.sku || "").toLowerCase();
|
| 1132 |
+
const sl = String(p.slug || "").toLowerCase();
|
| 1133 |
+
const lk = String(p.link || "").toLowerCase();
|
| 1134 |
+
const nm = String(p.title_clean || p.name || "").toLowerCase().replace(/\s+/g, " ");
|
| 1135 |
+
// Collision with a base catalog product (same sku or slug) → drop.
|
| 1136 |
+
if (sk && baseSkus.has(sk)) return false;
|
| 1137 |
+
if (sl && baseSlugs.has(sl)) return false;
|
| 1138 |
+
// Dedup WITHIN the added list.
|
| 1139 |
+
let key = "";
|
| 1140 |
+
if (sk) key = "sku:" + sk;
|
| 1141 |
+
else if (sl) key = "slug:" + sl;
|
| 1142 |
+
else if (lk) key = "link:" + lk;
|
| 1143 |
+
else if (nm) key = "name:" + nm;
|
| 1144 |
+
if (!key) return false;
|
| 1145 |
+
if (seenKeys.has(key)) return false;
|
| 1146 |
+
seenKeys.add(key);
|
| 1147 |
+
return true;
|
| 1148 |
+
});
|
| 1149 |
+
if (uniqueAdded.length) {
|
| 1150 |
+
allProducts = uniqueAdded.concat(allProducts);
|
| 1151 |
+
console.log("[VAIX] Merged " + uniqueAdded.length + " added products from products_url_added.json");
|
| 1152 |
+
} else {
|
| 1153 |
+
console.log("[VAIX] No new added products to merge (all duplicates)");
|
| 1154 |
+
}
|
| 1155 |
}
|
| 1156 |
}
|
| 1157 |
} catch (e) { console.warn("[VAIX] products_url_added merge skipped:", e && e.message); }
|