ux improved
This commit is contained in:
112
src/modules/3-turn-engine/turnEngineV3.pendingSelection.js
Normal file
112
src/modules/3-turn-engine/turnEngineV3.pendingSelection.js
Normal file
@@ -0,0 +1,112 @@
|
||||
function parseIndexSelection(text) {
|
||||
const t = String(text || "").toLowerCase();
|
||||
const m = /\b(\d{1,2})\b/.exec(t);
|
||||
if (m) return parseInt(m[1], 10);
|
||||
if (/\bprimera\b|\bprimero\b/.test(t)) return 1;
|
||||
if (/\bsegunda\b|\bsegundo\b/.test(t)) return 2;
|
||||
if (/\btercera\b|\btercero\b/.test(t)) return 3;
|
||||
if (/\bcuarta\b|\bcuarto\b/.test(t)) return 4;
|
||||
if (/\bquinta\b|\bquinto\b/.test(t)) return 5;
|
||||
if (/\bsexta\b|\bsexto\b/.test(t)) return 6;
|
||||
if (/\bs[eé]ptima\b|\bs[eé]ptimo\b/.test(t)) return 7;
|
||||
if (/\boctava\b|\boctavo\b/.test(t)) return 8;
|
||||
if (/\bnovena\b|\bnoveno\b/.test(t)) return 9;
|
||||
if (/\bd[eé]cima\b|\bd[eé]cimo\b/.test(t)) return 10;
|
||||
return null;
|
||||
}
|
||||
|
||||
function isShowMoreRequest(text) {
|
||||
const t = String(text || "").toLowerCase();
|
||||
return (
|
||||
/\bmostr(a|ame)\s+m[aá]s\b/.test(t) ||
|
||||
/\bmas\s+opciones\b/.test(t) ||
|
||||
(/\bm[aá]s\b/.test(t) && /\b(opciones|productos|variedades|tipos)\b/.test(t)) ||
|
||||
/\bsiguiente(s)?\b/.test(t)
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeText(s) {
|
||||
return String(s || "")
|
||||
.toLowerCase()
|
||||
.replace(/[¿?¡!.,;:()"]/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function scoreTextMatch(query, candidateName) {
|
||||
const qt = new Set(normalizeText(query).split(" ").filter(Boolean));
|
||||
const nt = new Set(normalizeText(candidateName).split(" ").filter(Boolean));
|
||||
let hits = 0;
|
||||
for (const w of qt) if (nt.has(w)) hits++;
|
||||
return hits / Math.max(qt.size, 1);
|
||||
}
|
||||
|
||||
export function buildPagedOptions({ candidates, candidateOffset = 0, baseIdx = 1, pageSize = 9 }) {
|
||||
const cands = (candidates || []).filter((c) => c && c.woo_product_id && c.name);
|
||||
const off = Math.max(0, parseInt(candidateOffset, 10) || 0);
|
||||
const size = Math.max(1, Math.min(20, parseInt(pageSize, 10) || 9));
|
||||
const slice = cands.slice(off, off + size);
|
||||
const options = slice.map((c, i) => ({
|
||||
idx: baseIdx + i,
|
||||
type: "product",
|
||||
woo_product_id: c.woo_product_id,
|
||||
name: c.name,
|
||||
}));
|
||||
const hasMore = off + size < cands.length;
|
||||
if (hasMore) options.push({ idx: baseIdx + size, type: "more", name: "Mostrame más…" });
|
||||
const list = options
|
||||
.map((o) => (o.type === "more" ? `- ${o.idx}) ${o.name}` : `- ${o.idx}) ${o.name}`))
|
||||
.join("\n");
|
||||
const question = `¿Cuál de estos querés?\n${list}\n\nRespondé con el número.`;
|
||||
const pending = {
|
||||
candidates: cands,
|
||||
options,
|
||||
candidate_offset: off,
|
||||
page_size: size,
|
||||
base_idx: baseIdx,
|
||||
has_more: hasMore,
|
||||
next_candidate_offset: off + size,
|
||||
next_base_idx: baseIdx + size + (hasMore ? 1 : 0),
|
||||
};
|
||||
return { question, pending, options, hasMore };
|
||||
}
|
||||
|
||||
export function resolvePendingSelection({ text, nlu, pending }) {
|
||||
if (!pending?.candidates?.length) return { kind: "none" };
|
||||
|
||||
if (isShowMoreRequest(text)) {
|
||||
const { question, pending: nextPending } = buildPagedOptions({
|
||||
candidates: pending.candidates,
|
||||
candidateOffset: pending.next_candidate_offset ?? ((pending.candidate_offset || 0) + (pending.page_size || 9)),
|
||||
baseIdx: pending.next_base_idx ?? ((pending.base_idx || 1) + (pending.page_size || 9) + 1),
|
||||
pageSize: pending.page_size || 9,
|
||||
});
|
||||
return { kind: "more", question, pending: nextPending };
|
||||
}
|
||||
|
||||
const idx =
|
||||
(nlu?.entities?.selection?.type === "index" ? parseInt(String(nlu.entities.selection.value), 10) : null) ??
|
||||
parseIndexSelection(text);
|
||||
if (idx && Array.isArray(pending.options)) {
|
||||
const opt = pending.options.find((o) => o.idx === idx);
|
||||
if (opt?.type === "more") return { kind: "more", question: null, pending };
|
||||
if (opt?.woo_product_id) {
|
||||
const chosen = pending.candidates.find((c) => c.woo_product_id === opt.woo_product_id) || null;
|
||||
if (chosen) return { kind: "chosen", chosen };
|
||||
}
|
||||
}
|
||||
|
||||
const selText = nlu?.entities?.selection?.type === "text" ? String(nlu.entities.selection.value || "").trim() : null;
|
||||
const q = selText || nlu?.entities?.product_query || null;
|
||||
if (q) {
|
||||
const scored = pending.candidates
|
||||
.map((c) => ({ c, s: scoreTextMatch(q, c?.name) }))
|
||||
.sort((a, b) => b.s - a.s);
|
||||
if (scored[0]?.s >= 0.6 && (!scored[1] || scored[0].s - scored[1].s >= 0.2)) {
|
||||
return { kind: "chosen", chosen: scored[0].c };
|
||||
}
|
||||
}
|
||||
|
||||
return { kind: "ask" };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user