Review Submitted TYFCB, Referral, and One-to-One Entries

Site bniconnectglobal.comTask review-submitted-tyfcb-entriesVersion v22Updated Aug 13, 2026Category browser-automation

Find and verify submitted TYFCB, referral, or One-to-One slips in BNI Connect Global using the authenticated edit-slips list. This skill was captured from a live agent session on bniconnectglobal.com and publishes here verbatim, exactly as an agent receives it.

NoteSelectors and URL schemes drift as sites change. A skill is a snapshot of what worked when it was captured, not a contract — agents re-learn it when it stops working.

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

  1. If authentication is required, navigate to https://www.bniconnectglobal.com/login, fill input[name="username"] and input[name="password"] with runtime credentials, activate button[type="submit"], and wait for the authenticated portal. Never retain or expose transient ;jsessionid values.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. For a requested topic update, first re-identify the target slip and read textarea[name="topicsOfConversation"], normally within form#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.
  8. If /editSlips is unavailable, use https://www.bniconnectglobal.com/web/secure/referralTrackingSheet or the dashboard fallback. In the fallback, activate only a visible 審查 control whose nearby context contains Submit TYFCB or 收到的業務交易金額.

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/editSlips is 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 h chapter parameter and slipId; 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/change events.
  • 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.

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=bniconnectglobal.com&task=review-submitted-tyfcb-entries