Purpose
Fill or submit an authenticated TYFCB or One-to-One slip on BNI Connect Global. Support placeholders {member-name}, {inviter-name}, {location}, {date}, and {topic} for One-to-One entries. Fill-only requests must stop after verification; submit only when the caller explicitly requests submission.
When to Use
Use for TYFCB or One-to-One form inspection, filling, or confirmed submission. Do not use for internal referrals, cross-chapter member lookup, or reviewing existing slips.
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/web/portal. Never retain credentials or expose transient;jsessionidvalues. - No stable direct form URL is known. On the authenticated dashboard, for One-to-One activate only the visible exact
提交control whose nearby context matches一對一會面,一對一,一对一,One-to-One, or1-2-1, excluding提供引薦and分會教育. For TYFCB, activate the visibleSubmit TYFCBcontrol qualified by referral/TYFCB context. Wait for the rendered form ordiv[role="dialog"]. - For One-to-One, open the member
.MuiSelect-selector[role="combobox"]associated with member/recipient by dispatching bubblingmousedownand callingclick(). Wait forul[role="listbox"] > li[role="option"], then run the evaluator below with{member-name}andselect=true. Select only an enabled option whose normalized visible text exactly matches the requested name. - Reopen the current inviter select identified by
邀請人,邀請者,Inviter, orinvite, wait for its current listbox options, and run the same evaluator with{inviter-name}andselect=true. Do not assume member and inviter options share a list or ordinal. - Run the One-to-One fill-and-verify evaluator below with
{member-name},{inviter-name},{location},{date}, and{topic}. It identifies fields by labels/context, uses React-safe native setters for location/date/topic, and verifies the actual values. Resolve “today” at runtime and verify the resulting date value rather than assuming a format. - For fill-only requests, stop at
allMatch:trueand do not activate Save, Submit, or another action control. For an explicitly confirmed submission, re-run verification, qualify an enabled action control within the same verified form, activate it, wait for visible success/result evidence, and run the submission-result extractor. Dialog closure or navigation alone is insufficient proof. - For TYFCB, use the same exact enabled listbox selection technique, then fill and verify
{recipient-name},{amount},{classification},{status}, and optional{comment}before any explicitly confirmed submission.
Exact option evaluator:
((wantedName = "", select = false) => {
const v = (e) =>
!!e && (e.offsetWidth || e.offsetHeight || e.getClientRects().length),
c = (s) => (s || "").replace(/\\s+/g, " ").trim(),
q = c(wantedName).toLocaleLowerCase(),
els = [
...document.querySelectorAll('ul[role="listbox"] > li[role="option"]'),
].filter(v),
opts = els.map((e, i) => ({
index: i,
text: c(e.innerText || e.textContent),
value: e.getAttribute("data-value") || e.getAttribute("value") || null,
disabled:
e.getAttribute("aria-disabled") === "true" ||
e.classList.contains("Mui-disabled"),
selected: e.getAttribute("aria-selected") === "true",
})),
m = opts.filter((x) => x.text.toLocaleLowerCase() === q),
chosen = m.find((x) => !x.disabled);
if (select && chosen) els[chosen.index].click();
return {
query: wantedName,
optionCount: opts.length,
matches: m,
selected: !!(select && chosen),
reason: select && !chosen ? "exact enabled option not found" : null,
};
})("{name}", true);One-to-One fill-and-verify evaluator:
((expected = {}) => {
const c = (s) => (s || "").replace(/\\s+/g, " ").trim(),
v = (e) => !!e && (e.offsetWidth || e.offsetHeight || e.getClientRects().length),
r = document.querySelector('div[role="dialog"]') || document,
els = [
...r.querySelectorAll('input,textarea,[role="combobox"],.MuiSelect-select'),
].filter(v),
ctx = (e) =>
c(
e.closest(".MuiFormControl-root,.MuiFormControl,fieldset,form,div")
?.innerText || "",
),
allText = (e) =>
[
e.getAttribute("aria-label"),
e.getAttribute("name"),
e.getAttribute("placeholder"),
ctx(e),
]
.filter(Boolean)
.join(" "),
set = (e, x) => {
if (!e) return;
const p =
e.tagName === "TEXTAREA"
? HTMLTextAreaElement.prototype
: HTMLInputElement.prototype,
s = Object.getOwnPropertyDescriptor(p, "value")?.set;
s ? s.call(e, x) : (e.value = x);
e.dispatchEvent(new Event("input", { bubbles: true }));
e.dispatchEvent(new Event("change", { bubbles: true }));
},
find = (re) => els.find((e) => re.test(allText(e))),
val = (e) => c(e?.value ?? e?.innerText ?? e?.textContent),
member =
find(/member|recipient|會員|成員|對象|對方/i) ||
els.find((e) => e.matches('.MuiSelect-select,[role="combobox"]')),
inviter = find(/inviter|invite|邀請人|邀請者/i),
location = find(/location|地點|場所/i),
date =
find(/date|日期|時間/i) ||
els.find((e) => e.tagName === "INPUT" && /20\d\d/.test(e.value)),
topic =
find(/topic|主題|內容|交流/i) || [...r.querySelectorAll("textarea")].find(v),
locFallback = [...r.querySelectorAll("input.MuiOutlinedInput-input")].find(
(e) => !location && !/20\d\d/.test(e.value),
);
if (expected.location != null) set(location || locFallback, expected.location);
if (expected.date != null) set(date, expected.date);
if (expected.topic != null) set(topic, expected.topic);
const actual = {
member: val(member) || null,
inviter: val(inviter) || null,
location: val(location || locFallback) || null,
date: val(date) || null,
topic: val(topic) || null,
},
matches = Object.fromEntries(
Object.entries(expected)
.filter(([, x]) => x != null)
.map(([k, x]) => [k, actual[k] === c(x)]),
);
return {
expected,
actual,
matches,
allMatch: Object.values(matches).every(Boolean),
fields: {
member: !!member,
inviter: !!inviter,
location: !!(location || locFallback),
date: !!date,
topic: !!topic,
},
formText: c(r.innerText || r.textContent).slice(0, 3000),
};
})({
member: "{member-name}",
inviter: "{inviter-name}",
location: "{location}",
date: "{date}",
topic: "{topic}",
});Submission-result extractor:
(() => {
const c = (s) => (s || "").replace(/\\s+/g, " ").trim(),
v = (e) => !!e && (e.offsetWidth || e.offsetHeight || e.getClientRects().length),
t = c(document.body?.innerText || document.body?.textContent),
d = !!document.querySelector('div[role="dialog"]'),
success =
/success|submitted|saved|completed|成功|已提交|提交成功|儲存成功|保存成功/i.test(
t,
),
controls = [...document.querySelectorAll('button,a,[role="button"]')]
.filter(v)
.map((e) => ({
label: c(e.innerText || e.textContent || e.getAttribute("aria-label")),
href: e.getAttribute("href"),
disabled: !!e.disabled,
}));
return {
url: location.href,
dialogPresent: d,
modalGone: !d,
success,
visibleText: t.slice(0, 4000),
controls,
};
})();Site-Specific Gotchas
- One-to-One and TYFCB forms have no stable direct URL in the observed installation; use authenticated dashboard controls.
- The dashboard contains multiple
提交controls. Qualify One-to-One by nearby One-to-One text and exclude internal-referral and chapter-education contexts. - Material UI options render only after bubbling
mousedownplusclick, followed by a wait forul[role="listbox"] > li[role="option"]. - Match options by exact normalized visible text and reject disabled options; ordinals vary by session.
- Member and inviter lists may refresh independently; reopen the inviter list after member selection and wait for its current options.
- One-to-One location and date are commonly
input.MuiOutlinedInput-input; identify them by labels first. A year-like value is only a date fallback, and a non-year-like value is only a location fallback. - React-controlled fields require the native prototype value setter plus bubbling
inputandchangeevents. - Do not use positional selectors for the final action. Qualify the enabled action within the verified form and verify visible success evidence afterward.
- Authentication may append a transient
;jsessionid=...; preserve the session but never hard-code or expose it. Keep credentials, cookies, and session URLs runtime-only.
Expected Output
Return form-open state, option counts, exact selected member/inviter matches, identified fields, requested and actual values, matches, and allMatch. For fill-only requests, state that no submission was attempted. For confirmed submissions, include the qualified action and verified post-submit success evidence. Never claim submission based only on a click, modal closure, or navigation.