Purpose
Inspect the authenticated BNI Connect Global submitted-slips list, locate rows matching supplied member names and optional amount, slip kind, date, or slip ID, and return complete row text plus row-scoped edit links. This supports verification of TYFCB, referral, and One-to-One records. It is read-only unless a separately confirmed edit is requested.
When to Use
Use when verifying whether a submitted referral, TYFCB, or One-to-One record exists, identifying the exact matching row, or discovering its edit URL. The preferred route is /web/secure/editSlips. Use the direct One-to-One edit route only after observing and verifying the row's exact h and slipId parameters.
Workflow
- If authentication is required, navigate to
https://www.bniconnectglobal.com/login, fillinput[name="username"]andinput[name="password"]with runtime credentials, activatebutton[type="submit"], and wait for the authenticated portal. Never retain or expose transient;jsessionidvalues. - Navigate directly to
https://www.bniconnectglobal.com/web/secure/editSlips; do not first open the dashboard or referral-tracking page. This direct route was confirmed to load successfully. Wait for the table or rendered slip rows. - Run the edit-slip extractor below on the loaded page with
{member-name}. Supply optional{amount},{slip-kind},{date}, and{slip-id}when available. For a verification-only request, navigation and extraction are the only required browser actions. - Confirm a record only when the visible row context contains the requested member and any requested kind, date, amount, or ID. Return every distinct matching row, including status/type text and row-scoped links/buttons.
- If an exact row's edit href is present, use that observed href directly. One-to-One edit forms conventionally use
https://www.bniconnectglobal.com/web/secure/editOneToOneSlip?h={chapter-code}&slipId={slip-id}.{chapter-code}is opaque: read it from the observed href and never guess it. - For an exact supplied slip ID, still verify the visible row where possible before navigating to the ID-bearing edit URL. On the loaded form, run the One-to-One form extractor below.
- For a requested topic update, first re-identify the target slip and read
textarea[name="topicsOfConversation"], normally withinform#editform. For fill-only requests, use the native setter below and stop after verification. Save only after explicit confirmation, then verify visible post-save evidence and reread the value. - If
/editSlipsis unavailable, usehttps://www.bniconnectglobal.com/web/secure/referralTrackingSheetor the dashboard fallback. In the fallback, activate only a visible審查control whose nearby context containsSubmit TYFCBor收到的業務交易金額.
Edit-slip extractor (run on the loaded /web/secure/editSlips page):
((memberName = "", amount = "", slipKind = "", date = "", slipId = "") => {
const visible = (e) =>
!!e && (e.offsetWidth || e.offsetHeight || e.getClientRects().length),
clean = (s) => (s || "").replace(/\s+/g, " ").trim(),
low = (s) => clean(s).toLocaleLowerCase(),
member = low(memberName),
kind = low(slipKind),
wanted = String(amount).replace(/[^0-9.]/g, ""),
wantedDate = low(date),
wantedId = String(slipId),
money = /(?:NT\s*[$$]|TWD\s*|\$)\s*[0-9][0-9,]*(?:\.[0-9]+)?/gi,
selectors = [
"table tbody tr",
"table tr",
'[role="row"]',
".MuiDataGrid-row",
"li",
".card",
'[class*="card"]',
'[class*="list-item"]',
],
seen = new Set(),
rows = [];
const scan = (doc) => {
for (const el of [
...new Set(selectors.flatMap((s) => [...doc.querySelectorAll(s)])),
]) {
if (!visible(el) || seen.has(el)) continue;
seen.add(el);
const text = clean(el.innerText || el.textContent);
if (!text) continue;
const amounts = [...text.matchAll(money)].map((m) => m[0]),
normalizedAmounts = amounts.map((x) => x.replace(/[^0-9.]/g, "")),
links = [...el.querySelectorAll("a")]
.filter(visible)
.map((a) => ({
text: clean(a.innerText || a.textContent),
title: clean(a.title),
ariaLabel: clean(a.getAttribute("aria-label")),
href: a.href,
})),
buttons = [...el.querySelectorAll('button,[role="button"]')]
.filter(visible)
.map((b) => ({
text: clean(b.innerText || b.textContent),
title: clean(b.title),
ariaLabel: clean(b.getAttribute("aria-label")),
}));
rows.push({
text,
amounts,
normalizedAmounts,
memberMatch: member ? low(text).includes(member) : true,
amountMatch: wanted ? normalizedAmounts.includes(wanted) : true,
slipKindMatch: kind ? low(text).includes(kind) : true,
dateMatch: wantedDate ? low(text).includes(wantedDate) : true,
slipIdMatch: wantedId
? text.includes(wantedId) || links.some((x) => x.href.includes(wantedId))
: true,
editLinks: links.filter((x) =>
/edit|修改|編輯|编辑/i.test(
[x.text, x.title, x.ariaLabel, x.href].join(" "),
),
),
links,
buttons,
tag: el.tagName.toLowerCase(),
className: typeof el.className === "string" ? el.className : "",
});
}
for (const f of doc.querySelectorAll("iframe"))
try {
if (f.contentDocument) scan(f.contentDocument);
} catch (_) {}
};
scan(document);
const c = rows.filter(
(x) =>
x.memberMatch &&
x.amountMatch &&
x.slipKindMatch &&
x.dateMatch &&
x.slipIdMatch,
),
matches = c.filter((x, i) => !c.some((y, j) => i !== j && y.text === x.text));
return {
query: { memberName, amount, slipKind, date, slipId },
matchCount: matches.length,
matches,
};
})("{member-name}", "{amount}", "{slip-kind}", "{date}", "{slip-id}");One-to-One edit-form extractor:
(() => {
const clean = (s) => (s || "").replace(/\s+/g, " ").trim(),
visible = (e) =>
!!e && (e.offsetWidth || e.offsetHeight || e.getClientRects().length),
context = (e) =>
clean(
e.closest(".MuiFormControl-root,.MuiFormControl,fieldset,form,div")
?.innerText || "",
),
root = document.querySelector('div[role="dialog"]') || document,
fields = [
...root.querySelectorAll(
'input,textarea,select,[role="combobox"],.MuiSelect-select',
),
]
.filter(visible)
.map((e, i) => ({
index: i,
tag: e.tagName.toLowerCase(),
id: e.id || null,
name: e.getAttribute("name"),
label: clean(
e.getAttribute("aria-label") ||
e.getAttribute("placeholder") ||
context(e),
),
value: clean(e.value ?? e.innerText ?? e.textContent),
type: e.getAttribute("type"),
})),
buttons = [
...root.querySelectorAll('button,[role="button"],input[type="submit"]'),
]
.filter(visible)
.map((e, i) => ({
index: i,
label: clean(
e.innerText || e.textContent || e.value || e.getAttribute("aria-label"),
),
disabled: !!e.disabled,
type: e.getAttribute("type"),
}));
return {
url: location.href,
slipId: new URL(location.href).searchParams.get("slipId"),
chapterCode: new URL(location.href).searchParams.get("h"),
topicField: !!root.querySelector('textarea[name="topicsOfConversation"]'),
formId: root.querySelector("form#editform") ? "editform" : null,
fields,
buttons,
formText: clean(root.innerText || root.textContent).slice(0, 5000),
};
})();Topic update verifier/setter (run only on the already verified target form):
((newTopic, save = false) => {
const ta =
document.querySelector('form#editform textarea[name="topicsOfConversation"]') ||
document.querySelector('textarea[name="topicsOfConversation"]');
if (!ta) return { updated: false, reason: "topic textarea not found" };
const before = ta.value,
proto = Object.getPrototypeOf(ta),
setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
if (setter) setter.call(ta, newTopic);
else ta.value = newTopic;
ta.dispatchEvent(new Event("input", { bubbles: true }));
ta.dispatchEvent(new Event("change", { bubbles: true }));
const after = ta.value,
form = ta.closest("form#editform") || ta.form;
if (save && form) form.submit();
return {
updated: after === newTopic,
before,
after,
submitted: !!(save && form),
formId: form?.id || null,
};
})("{new-topic}", false);Site-Specific Gotchas
/web/secure/editSlipsis the stable direct authenticated verification route and should be preferred over the referral-tracking detour.- The list can contain mixed TYFCB, referral, and One-to-One rows. Match visible type/kind, member, date, status, amount, and complete row text together rather than relying on columns or row order.
- Edit controls are row-scoped and may expose targets through href, title, aria-label, or localized text. Never infer an ID from position.
- One-to-One edit URLs require the observed opaque
hchapter parameter andslipId; never fabricate either value. - The UI may render tables, Material UI grids, lists, cards, or same-origin iframe content. Traverse accessible iframe documents and deduplicate identical complete row text.
- Amounts may appear as
NT$,NT$,TWD, or$, with optional spaces and thousands separators; normalize numeric values before comparison. - React-controlled topic inputs require the native value setter and bubbling
input/changeevents. - Do not click arbitrary review, edit, save, or submit controls. Qualify controls by the exact matching row or verified form.
- Authentication may append a transient
;jsessionid=...; preserve the session but never hard-code or expose it. Keep credentials and cookies runtime-only.
Expected Output
Return the authenticated route without session identifiers, query parameters, matchCount, every matching row's complete visible text, detected amounts, member/kind/date/ID match flags, status and DOM context, and observed edit links/buttons. For One-to-One inspection, also return the exact observed edit URL, slipId, chapterCode, form fields, current topic, and verification state. For edits, report whether saving was requested and verified post-save evidence; never claim a modification from navigation or a click alone.