@ixo/editor 5.20.0-experimental.4 → 5.20.0-experimental.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,6 +11,7 @@ import {
11
11
  createUcanDelegationStore,
12
12
  createUcanService,
13
13
  executeActionBlock,
14
+ extractDid,
14
15
  extractSurveyAnswerSchema,
15
16
  extractSurveyAnswersTemplate,
16
17
  formatCoin,
@@ -42,7 +43,7 @@ import {
42
43
  setActiveEditor,
43
44
  tempDomainCreatorSurvey,
44
45
  transformSurveyToCredentialSubject
45
- } from "./chunk-25XKVLR3.mjs";
46
+ } from "./chunk-6N5JNDZ3.mjs";
46
47
 
47
48
  // src/mantine/hooks/useCreateIxoEditor.ts
48
49
  import { useCreateBlockNote } from "@blocknote/react";
@@ -24988,7 +24989,7 @@ function useEntities(editor) {
24988
24989
 
24989
24990
  // src/mantine/blocks/action/actionTypes/pod/domainSingleSelection/DomainSingleSelectionFlowDetail.tsx
24990
24991
  function toCandidate(e) {
24991
- const did = String(e.did || e.id || "");
24992
+ const did = extractDid(e.did || e.id);
24992
24993
  if (!did) return null;
24993
24994
  return {
24994
24995
  did,
@@ -31896,6 +31897,7 @@ function emptyPaymentRowFields() {
31896
31897
  accountName: "",
31897
31898
  accountNumber: "",
31898
31899
  accountType: "",
31900
+ bankName: "",
31899
31901
  networkId: "",
31900
31902
  country: "",
31901
31903
  bvn: "",
@@ -31919,7 +31921,9 @@ function emptyPaymentSender() {
31919
31921
  address: "",
31920
31922
  dob: "",
31921
31923
  idType: "",
31922
- idNumber: ""
31924
+ idNumber: "",
31925
+ additionalIdType: "",
31926
+ additionalIdNumber: ""
31923
31927
  };
31924
31928
  }
31925
31929
  function emptyPaymentFieldTemplate() {
@@ -31927,6 +31931,7 @@ function emptyPaymentFieldTemplate() {
31927
31931
  accountName: "",
31928
31932
  accountNumber: "",
31929
31933
  accountType: "",
31934
+ bankName: "",
31930
31935
  networkId: "",
31931
31936
  country: "",
31932
31937
  bvn: "",
@@ -31938,14 +31943,7 @@ function emptyPaymentFieldTemplate() {
31938
31943
  outcomeCurrency: ""
31939
31944
  };
31940
31945
  }
31941
- var DEFAULT_REQUIRED_FIELDS = [
31942
- "accountName",
31943
- "accountNumber",
31944
- "accountType",
31945
- "country",
31946
- "amount",
31947
- "currency"
31948
- ];
31946
+ var DEFAULT_REQUIRED_FIELDS = ["accountName", "accountNumber", "accountType", "bankName", "country", "amount", "currency", "recipientDid"];
31949
31947
  function emptyPaymentInputs() {
31950
31948
  return {
31951
31949
  collectionId: "",
@@ -31988,7 +31986,9 @@ function parsePaymentInputs(json) {
31988
31986
  address: asString(parsed.sender?.address),
31989
31987
  dob: asString(parsed.sender?.dob),
31990
31988
  idType: asString(parsed.sender?.idType),
31991
- idNumber: asString(parsed.sender?.idNumber)
31989
+ idNumber: asString(parsed.sender?.idNumber),
31990
+ additionalIdType: asString(parsed.sender?.additionalIdType),
31991
+ additionalIdNumber: asString(parsed.sender?.additionalIdNumber)
31992
31992
  },
31993
31993
  defaultReason: asString(parsed.defaultReason, "other"),
31994
31994
  customerType: asString(parsed.customerType, "retail"),
@@ -31996,6 +31996,7 @@ function parsePaymentInputs(json) {
31996
31996
  accountName: asString(parsed.fieldTemplate?.accountName),
31997
31997
  accountNumber: asString(parsed.fieldTemplate?.accountNumber),
31998
31998
  accountType: asString(parsed.fieldTemplate?.accountType),
31999
+ bankName: asString(parsed.fieldTemplate?.bankName),
31999
32000
  networkId: asString(parsed.fieldTemplate?.networkId),
32000
32001
  country: asString(parsed.fieldTemplate?.country),
32001
32002
  bvn: asString(parsed.fieldTemplate?.bvn),
@@ -32025,21 +32026,19 @@ function parsePaymentInputs(json) {
32025
32026
  // intent. If a flow needs the old behaviour, surface it on the row UI
32026
32027
  // (e.g. ban empty networkId at propose time inside the action def
32027
32028
  // itself, not via requiredFields). For now no flow needs that.
32028
- requiredFields: Array.isArray(parsed.requiredFields) ? parsed.requiredFields.filter(
32029
- (f) => typeof f === "string" && // Drop the oracle-resolved fields from any persisted list.
32030
- f !== "networkId" && f !== "channelId" && [
32031
- "accountName",
32032
- "accountNumber",
32033
- "accountType",
32034
- "country",
32035
- "bvn",
32036
- "amount",
32037
- "currency",
32038
- "reason",
32039
- "recipientDid",
32040
- "outcomeCurrency"
32041
- ].includes(f)
32042
- ) : [...DEFAULT_REQUIRED_FIELDS]
32029
+ requiredFields: (() => {
32030
+ if (!Array.isArray(parsed.requiredFields)) {
32031
+ return [...DEFAULT_REQUIRED_FIELDS];
32032
+ }
32033
+ const cleaned = parsed.requiredFields.filter(
32034
+ (f) => typeof f === "string" && // Drop the oracle-resolved fields from any persisted list.
32035
+ f !== "networkId" && f !== "channelId" && ["accountName", "accountNumber", "accountType", "bankName", "country", "bvn", "amount", "currency", "reason", "recipientDid", "outcomeCurrency"].includes(f)
32036
+ );
32037
+ if (!cleaned.includes("recipientDid")) {
32038
+ cleaned.push("recipientDid");
32039
+ }
32040
+ return cleaned;
32041
+ })()
32043
32042
  };
32044
32043
  }
32045
32044
  function serializePaymentInputs(inputs) {
@@ -32072,6 +32071,7 @@ function parsePaymentRows(raw) {
32072
32071
  accountName: asString(entry.fields?.accountName),
32073
32072
  accountNumber: asString(entry.fields?.accountNumber),
32074
32073
  accountType: asString(entry.fields?.accountType),
32074
+ bankName: asString(entry.fields?.bankName),
32075
32075
  networkId: asString(entry.fields?.networkId),
32076
32076
  country: asString(entry.fields?.country),
32077
32077
  bvn: asString(entry.fields?.bvn),
@@ -32095,9 +32095,7 @@ function parsePaymentRows(raw) {
32095
32095
  }).filter((r) => r !== null);
32096
32096
  }
32097
32097
  function missingRequiredFields(row, required) {
32098
- return required.filter(
32099
- (key) => !row.fields[key] || String(row.fields[key]).trim() === ""
32100
- );
32098
+ return required.filter((key) => !row.fields[key] || String(row.fields[key]).trim() === "");
32101
32099
  }
32102
32100
  function checkCurrencyCompatibility(row) {
32103
32101
  const outcomeRaw = row.fields.outcomeCurrency?.trim() ?? "";
@@ -32128,6 +32126,10 @@ var FIELD_LABEL = {
32128
32126
  label: "Account type",
32129
32127
  hint: '"bank" or "momo" \u2014 usually fixed per flow, not from claim'
32130
32128
  },
32129
+ bankName: {
32130
+ label: "Recipient bank name (required)",
32131
+ hint: "e.g. {{claim.surveyAnswers.umuzi:bankName}} \u2014 must match a YellowCard network name (Capitec Bank, FNB, MTN, \u2026). The oracle resolves this to a networkId at propose time. WRONG-bank routes funds to the wrong clearing system, so this MUST be accurate."
32132
+ },
32131
32133
  networkId: {
32132
32134
  label: "Network / bank UUID (optional)",
32133
32135
  hint: "Leave empty \u2014 the oracle resolves the network UUID at propose time via the worker's `/channels` discovery. Only set this if you want to pin a specific bank."
@@ -32157,8 +32159,8 @@ var FIELD_LABEL = {
32157
32159
  hint: "Falls back to block-level Default Reason when empty"
32158
32160
  },
32159
32161
  recipientDid: {
32160
- label: "Recipient DID (optional)",
32161
- hint: "e.g. {{claim.agentDid}} \u2014 falls back to invoker DID server-side"
32162
+ label: "Recipient DID (required)",
32163
+ hint: "e.g. {{claim.agentDid}} \u2014 DID of the person being paid out to. Worker uses this as YC customerUID for per-user KYC tracking. NEVER falls back to invoker \u2014 that would attribute the payout to the operator, not the actual recipient."
32162
32164
  },
32163
32165
  outcomeCurrency: {
32164
32166
  label: "Outcome currency",
@@ -32169,6 +32171,7 @@ var REQUIRED_FIELD_OPTIONS = [
32169
32171
  { value: "accountName", label: "Recipient account name" },
32170
32172
  { value: "accountNumber", label: "Recipient account number" },
32171
32173
  { value: "accountType", label: "Account type" },
32174
+ { value: "bankName", label: "Bank name" },
32172
32175
  { value: "country", label: "Country" },
32173
32176
  { value: "bvn", label: "BVN" },
32174
32177
  { value: "amount", label: "Amount" },
@@ -32177,12 +32180,7 @@ var REQUIRED_FIELD_OPTIONS = [
32177
32180
  { value: "recipientDid", label: "Recipient DID" },
32178
32181
  { value: "outcomeCurrency", label: "Outcome currency" }
32179
32182
  ];
32180
- var PaymentConfig = ({
32181
- inputs,
32182
- onInputsChange,
32183
- editor,
32184
- blockId
32185
- }) => {
32183
+ var PaymentConfig = ({ inputs, onInputsChange, editor, blockId }) => {
32186
32184
  const [local, setLocal] = useState140(() => parsePaymentInputs(inputs));
32187
32185
  useEffect112(() => {
32188
32186
  setLocal(parsePaymentInputs(inputs));
@@ -32202,16 +32200,7 @@ var PaymentConfig = ({
32202
32200
  [local.fieldTemplate, update]
32203
32201
  );
32204
32202
  const editorDocument = editor?.document || [];
32205
- return /* @__PURE__ */ React288.createElement(Stack205, { gap: "lg" }, /* @__PURE__ */ React288.createElement(Alert61, { color: "blue", variant: "light" }, /* @__PURE__ */ React288.createElement(Text180, { size: "xs" }, /* @__PURE__ */ React288.createElement("strong", null, "Sender details are NOT set here."), " Each operator running this flow fills in their own org name + KYC at runtime, in the payment block's side panel. Templates ship clean \u2014 no operator PII. The worker URL below is a default; operators can override it per-flow.")), /* @__PURE__ */ React288.createElement(Stack205, { gap: "xs" }, /* @__PURE__ */ React288.createElement(Text180, { size: "sm", fw: 600 }, "Claim source"), /* @__PURE__ */ React288.createElement(Text180, { size: "xs", c: "dimmed" }, "The Claims tab in the flow-mode UI pulls approved claims from this collection so the payer can pick which ones to pay out. Use the same collection ID that the claim and evaluation blocks point at."), /* @__PURE__ */ React288.createElement(
32206
- TextInput9,
32207
- {
32208
- size: "xs",
32209
- label: "Claim collection ID",
32210
- placeholder: "e.g. 781",
32211
- value: local.collectionId,
32212
- onChange: (e) => update({ collectionId: e.currentTarget.value })
32213
- }
32214
- )), /* @__PURE__ */ React288.createElement(Divider25, { variant: "dashed" }), /* @__PURE__ */ React288.createElement(Stack205, { gap: "xs" }, /* @__PURE__ */ React288.createElement(Text180, { size: "sm", fw: 600 }, "Default YellowCard worker URL"), /* @__PURE__ */ React288.createElement(Text180, { size: "xs", c: "dimmed" }, "The template-author's default. Used when an operator hasn't set a flow-level override. Operators running self-hosted workers paste their own URL in the flow-mode Payout setup panel \u2014 that override wins. Leave empty to force every operator to choose their own."), /* @__PURE__ */ React288.createElement(
32203
+ return /* @__PURE__ */ React288.createElement(Stack205, { gap: "lg" }, /* @__PURE__ */ React288.createElement(Alert61, { color: "blue", variant: "light" }, /* @__PURE__ */ React288.createElement(Text180, { size: "xs" }, /* @__PURE__ */ React288.createElement("strong", null, "Sender details are NOT set here."), " Each operator running this flow fills in their own org name + KYC at runtime, in the payment block's side panel. Templates ship clean \u2014 no operator PII. The worker URL below is a default; operators can override it per-flow.")), /* @__PURE__ */ React288.createElement(Stack205, { gap: "xs" }, /* @__PURE__ */ React288.createElement(Text180, { size: "sm", fw: 600 }, "Claim source"), /* @__PURE__ */ React288.createElement(Text180, { size: "xs", c: "dimmed" }, "The Claims tab in the flow-mode UI pulls approved claims from this collection so the payer can pick which ones to pay out. Use the same collection ID that the claim and evaluation blocks point at."), /* @__PURE__ */ React288.createElement(TextInput9, { size: "xs", label: "Claim collection ID", placeholder: "e.g. 781", value: local.collectionId, onChange: (e) => update({ collectionId: e.currentTarget.value }) })), /* @__PURE__ */ React288.createElement(Divider25, { variant: "dashed" }), /* @__PURE__ */ React288.createElement(Stack205, { gap: "xs" }, /* @__PURE__ */ React288.createElement(Text180, { size: "sm", fw: 600 }, "Default YellowCard worker URL"), /* @__PURE__ */ React288.createElement(Text180, { size: "xs", c: "dimmed" }, "The template-author's default. Used when an operator hasn't set a flow-level override. Operators running self-hosted workers paste their own URL in the flow-mode Payout setup panel \u2014 that override wins. Leave empty to force every operator to choose their own."), /* @__PURE__ */ React288.createElement(
32215
32204
  TextInput9,
32216
32205
  {
32217
32206
  size: "xs",
@@ -32220,25 +32209,7 @@ var PaymentConfig = ({
32220
32209
  value: local.defaultWorkerBaseUrl,
32221
32210
  onChange: (e) => update({ defaultWorkerBaseUrl: e.currentTarget.value })
32222
32211
  }
32223
- )), /* @__PURE__ */ React288.createElement(Divider25, { variant: "dashed" }), /* @__PURE__ */ React288.createElement(Stack205, { gap: "xs" }, /* @__PURE__ */ React288.createElement(Text180, { size: "sm", fw: 600 }, "Defaults"), /* @__PURE__ */ React288.createElement(Group111, { grow: true }, /* @__PURE__ */ React288.createElement(
32224
- TextInput9,
32225
- {
32226
- size: "xs",
32227
- label: "Default reason",
32228
- placeholder: "other",
32229
- value: local.defaultReason,
32230
- onChange: (e) => update({ defaultReason: e.currentTarget.value })
32231
- }
32232
- ), /* @__PURE__ */ React288.createElement(
32233
- TextInput9,
32234
- {
32235
- size: "xs",
32236
- label: "Customer type",
32237
- placeholder: "retail",
32238
- value: local.customerType,
32239
- onChange: (e) => update({ customerType: e.currentTarget.value })
32240
- }
32241
- ))), /* @__PURE__ */ React288.createElement(Divider25, { variant: "dashed" }), /* @__PURE__ */ React288.createElement(Stack205, { gap: "xs" }, /* @__PURE__ */ React288.createElement(Text180, { size: "sm", fw: 600 }, "Row template \u2014 claim & evaluation mapping"), /* @__PURE__ */ React288.createElement(Text180, { size: "xs", c: "dimmed" }, "Each value is a reference expression that the payment bridge resolves against the approved claim's survey answers and the evaluator's outcome. Use the variable picker (V icon) to insert refs like ", /* @__PURE__ */ React288.createElement("code", null, "{{claim.surveyAnswers.<field>}}"), " or", " ", /* @__PURE__ */ React288.createElement("code", null, "{{evaluation.amount}}"), ". Operators can override any field inline before proposing."), Object.keys(FIELD_LABEL).map((key) => /* @__PURE__ */ React288.createElement(
32212
+ )), /* @__PURE__ */ React288.createElement(Divider25, { variant: "dashed" }), /* @__PURE__ */ React288.createElement(Stack205, { gap: "xs" }, /* @__PURE__ */ React288.createElement(Text180, { size: "sm", fw: 600 }, "Defaults"), /* @__PURE__ */ React288.createElement(Group111, { grow: true }, /* @__PURE__ */ React288.createElement(TextInput9, { size: "xs", label: "Default reason", placeholder: "other", value: local.defaultReason, onChange: (e) => update({ defaultReason: e.currentTarget.value }) }), /* @__PURE__ */ React288.createElement(TextInput9, { size: "xs", label: "Customer type", placeholder: "retail", value: local.customerType, onChange: (e) => update({ customerType: e.currentTarget.value }) }))), /* @__PURE__ */ React288.createElement(Divider25, { variant: "dashed" }), /* @__PURE__ */ React288.createElement(Stack205, { gap: "xs" }, /* @__PURE__ */ React288.createElement(Text180, { size: "sm", fw: 600 }, "Row template \u2014 claim & evaluation mapping"), /* @__PURE__ */ React288.createElement(Text180, { size: "xs", c: "dimmed" }, "Each value is a reference expression that the payment bridge resolves against the approved claim's survey answers and the evaluator's outcome. Use the variable picker (V icon) to insert refs like ", /* @__PURE__ */ React288.createElement("code", null, "{{claim.surveyAnswers.<field>}}"), " or ", /* @__PURE__ */ React288.createElement("code", null, "{{evaluation.amount}}"), ". Operators can override any field inline before proposing."), Object.keys(FIELD_LABEL).map((key) => /* @__PURE__ */ React288.createElement(
32242
32213
  DataInput,
32243
32214
  {
32244
32215
  key,
@@ -32364,6 +32335,7 @@ function resolvePaymentRowFields(fieldTemplate, editorDocument, scope) {
32364
32335
  accountName: r(fieldTemplate.accountName),
32365
32336
  accountNumber: r(fieldTemplate.accountNumber),
32366
32337
  accountType: r(fieldTemplate.accountType),
32338
+ bankName: r(fieldTemplate.bankName),
32367
32339
  networkId: r(fieldTemplate.networkId),
32368
32340
  country: r(fieldTemplate.country),
32369
32341
  bvn: r(fieldTemplate.bvn),
@@ -32437,9 +32409,7 @@ function appendManualPaymentRow(editor, paymentBlock, fields) {
32437
32409
  }
32438
32410
  function patchPaymentRow(editor, paymentBlock, rowId, patch) {
32439
32411
  const rows = readPaymentRows(paymentBlock);
32440
- const next = rows.map(
32441
- (r) => r.id === rowId ? { ...r, ...patch, fields: { ...r.fields, ...patch.fields ?? {} } } : r
32442
- );
32412
+ const next = rows.map((r) => r.id === rowId ? { ...r, ...patch, fields: { ...r.fields, ...patch.fields ?? {} } } : r);
32443
32413
  writePaymentRows(editor, paymentBlock, next);
32444
32414
  }
32445
32415
  function removePaymentRow(editor, paymentBlock, rowId) {
@@ -32905,7 +32875,8 @@ var PaymentFlowDetail = ({ inputs, editor, block, runtime, isDisabled, executeAc
32905
32875
  }, [block, chainClaims, checkedClaimIds, editor, parsed.fieldTemplate]);
32906
32876
  const [setupOpen, setSetupOpen] = useState141(() => {
32907
32877
  const initial = parsePaymentInputs(inputs);
32908
- return !initial.workerBaseUrl.trim() || !initial.sender.name.trim() || !initial.sender.country.trim();
32878
+ const initialIsNG = initial.sender.country.trim().toUpperCase() === "NG";
32879
+ return !initial.workerBaseUrl.trim() || !initial.sender.name.trim() || !initial.sender.country.trim() || initialIsNG && (!initial.sender.idNumber.trim() || !initial.sender.additionalIdNumber.trim());
32909
32880
  });
32910
32881
  const [submitting, setSubmitting] = useState141(false);
32911
32882
  const [error, setError] = useState141(null);
@@ -32951,6 +32922,9 @@ var PaymentFlowDetail = ({ inputs, editor, block, runtime, isDisabled, executeAc
32951
32922
  if (!parsed.sender.name.trim() || !parsed.sender.country.trim()) {
32952
32923
  return "Sender name and country are required. Open Payout setup above and fill them in.";
32953
32924
  }
32925
+ if (parsed.sender.country.trim().toUpperCase() === "NG" && (!parsed.sender.idNumber.trim() || !parsed.sender.additionalIdNumber.trim())) {
32926
+ return "NG senders must supply both NIN (idNumber) and BVN (additionalIdNumber). Open Payout setup above and fill them in \u2014 the worker auto-stamps the type fields.";
32927
+ }
32954
32928
  if (oracleResolving) return "Resolving the oracle DID \u2014 try again in a moment.";
32955
32929
  if (!oracleDid) return "Cannot determine the chat oracle DID.";
32956
32930
  if (!selectedDelegationCid) return "Select a UCAN delegation (or create one).";
@@ -32963,7 +32937,17 @@ var PaymentFlowDetail = ({ inputs, editor, block, runtime, isDisabled, executeAc
32963
32937
  }
32964
32938
  }
32965
32939
  return null;
32966
- }, [effectiveWorkerBaseUrl, parsed.sender.name, parsed.sender.country, oracleDid, oracleResolving, selectedDelegationCid, usableDelegations]);
32940
+ }, [
32941
+ effectiveWorkerBaseUrl,
32942
+ parsed.sender.name,
32943
+ parsed.sender.country,
32944
+ parsed.sender.idNumber,
32945
+ parsed.sender.additionalIdNumber,
32946
+ oracleDid,
32947
+ oracleResolving,
32948
+ selectedDelegationCid,
32949
+ usableDelegations
32950
+ ]);
32967
32951
  const rowValidity = useMemo117(() => {
32968
32952
  const out = /* @__PURE__ */ new Map();
32969
32953
  for (const r of rows) {
@@ -33148,7 +33132,8 @@ var PaymentFlowDetail = ({ inputs, editor, block, runtime, isDisabled, executeAc
33148
33132
  }
33149
33133
  const oracleUnavailable = !oracleResolving && !oracleDid;
33150
33134
  const workerUrlMissing = !effectiveWorkerBaseUrl;
33151
- const senderIncomplete = !parsed.sender.name.trim() || !parsed.sender.country.trim();
33135
+ const senderIsNG = parsed.sender.country.trim().toUpperCase() === "NG";
33136
+ const senderIncomplete = !parsed.sender.name.trim() || !parsed.sender.country.trim() || senderIsNG && (!parsed.sender.idNumber.trim() || !parsed.sender.additionalIdNumber.trim());
33152
33137
  const setupIncomplete = workerUrlMissing || senderIncomplete;
33153
33138
  const noUsableDelegation = usableDelegations.length === 0;
33154
33139
  const delegationNotSelected = !selectedDelegationCid;
@@ -33188,7 +33173,7 @@ var PaymentFlowDetail = ({ inputs, editor, block, runtime, isDisabled, executeAc
33188
33173
  onChange: (e) => patchInputs({ workerBaseUrl: e.currentTarget.value }),
33189
33174
  error: workerUrlMissing ? "Required" : void 0
33190
33175
  }
33191
- ), /* @__PURE__ */ React289.createElement(Divider26, { variant: "dashed", my: 4 }), /* @__PURE__ */ React289.createElement(Text181, { size: "xs", fw: 600, c: "dimmed" }, "Sender (organisation paying out)"), /* @__PURE__ */ React289.createElement(Text181, { size: "xs", c: "dimmed" }, "These details go with every row you propose. YellowCard requires full KYC (address, dob, idType, idNumber) once cumulative payments per recipient cross $200 USD \u2014 set them now to avoid mid-batch rejections."), /* @__PURE__ */ React289.createElement(Group112, { grow: true }, /* @__PURE__ */ React289.createElement(
33176
+ ), /* @__PURE__ */ React289.createElement(Divider26, { variant: "dashed", my: 4 }), /* @__PURE__ */ React289.createElement(Text181, { size: "xs", fw: 600, c: "dimmed" }, "Sender (organisation paying out)"), /* @__PURE__ */ React289.createElement(Text181, { size: "xs", c: "dimmed" }, "These details go with every row you propose. YellowCard requires full KYC (address, dob, idType, idNumber) once cumulative payments per recipient cross $200 USD \u2014 set them now to avoid mid-batch rejections. NG senders additionally need NIN + BVN (extra fields appear when Country is set to NG)."), /* @__PURE__ */ React289.createElement(Group112, { grow: true }, /* @__PURE__ */ React289.createElement(
33192
33177
  TextInput10,
33193
33178
  {
33194
33179
  size: "xs",
@@ -33216,7 +33201,27 @@ var PaymentFlowDetail = ({ inputs, editor, block, runtime, isDisabled, executeAc
33216
33201
  value: parsed.sender.dob,
33217
33202
  onChange: (e) => patchSender({ dob: e.currentTarget.value })
33218
33203
  }
33219
- ), /* @__PURE__ */ React289.createElement(TextInput10, { size: "xs", label: "ID type", placeholder: "passport", value: parsed.sender.idType, onChange: (e) => patchSender({ idType: e.currentTarget.value }) }), /* @__PURE__ */ React289.createElement(TextInput10, { size: "xs", label: "ID number", value: parsed.sender.idNumber, onChange: (e) => patchSender({ idNumber: e.currentTarget.value }) }))))), /* @__PURE__ */ React289.createElement(Divider26, null), /* @__PURE__ */ React289.createElement(Stack206, { gap: "xs" }, /* @__PURE__ */ React289.createElement(Group112, { justify: "space-between", align: "flex-end" }, /* @__PURE__ */ React289.createElement(Stack206, { gap: 2, style: { flex: 1 } }, /* @__PURE__ */ React289.createElement(Text181, { size: "xs", fw: 600, c: "dimmed" }, "UCAN Authorization"), /* @__PURE__ */ React289.createElement(Text181, { size: "xs", c: "dimmed" }, "Sign once \u2014 the oracle mints per-call invocations against the worker for every row.")), /* @__PURE__ */ React289.createElement(
33204
+ ), parsed.sender.country.trim().toUpperCase() === "NG" ? /* @__PURE__ */ React289.createElement(TextInput10, { size: "xs", label: "ID type", value: "NIN", readOnly: true, description: "Auto-set for NG senders" }) : /* @__PURE__ */ React289.createElement(TextInput10, { size: "xs", label: "ID type", placeholder: "passport", value: parsed.sender.idType, onChange: (e) => patchSender({ idType: e.currentTarget.value }) }), /* @__PURE__ */ React289.createElement(
33205
+ TextInput10,
33206
+ {
33207
+ size: "xs",
33208
+ label: parsed.sender.country.trim().toUpperCase() === "NG" ? "NIN (National Identification Number)" : "ID number",
33209
+ value: parsed.sender.idNumber,
33210
+ onChange: (e) => patchSender({ idNumber: e.currentTarget.value }),
33211
+ error: parsed.sender.country.trim().toUpperCase() === "NG" && !parsed.sender.idNumber.trim() ? "Required for NG" : void 0
33212
+ }
33213
+ )), parsed.sender.country.trim().toUpperCase() === "NG" && /* @__PURE__ */ React289.createElement(React289.Fragment, null, /* @__PURE__ */ React289.createElement(Text181, { size: "xs", c: "dimmed" }, "YellowCard requires NG senders to supply both NIN and BVN. The ID type is fixed by regulation \u2014 only fill in the BVN number below."), /* @__PURE__ */ React289.createElement(
33214
+ TextInput10,
33215
+ {
33216
+ size: "xs",
33217
+ label: "BVN (Bank Verification Number)",
33218
+ value: parsed.sender.additionalIdNumber,
33219
+ onChange: (e) => patchSender({
33220
+ additionalIdNumber: e.currentTarget.value
33221
+ }),
33222
+ error: !parsed.sender.additionalIdNumber.trim() ? "Required for NG" : void 0
33223
+ }
33224
+ ))))), /* @__PURE__ */ React289.createElement(Divider26, null), /* @__PURE__ */ React289.createElement(Stack206, { gap: "xs" }, /* @__PURE__ */ React289.createElement(Group112, { justify: "space-between", align: "flex-end" }, /* @__PURE__ */ React289.createElement(Stack206, { gap: 2, style: { flex: 1 } }, /* @__PURE__ */ React289.createElement(Text181, { size: "xs", fw: 600, c: "dimmed" }, "UCAN Authorization"), /* @__PURE__ */ React289.createElement(Text181, { size: "xs", c: "dimmed" }, "Sign once \u2014 the oracle mints per-call invocations against the worker for every row.")), /* @__PURE__ */ React289.createElement(
33220
33225
  Button62,
33221
33226
  {
33222
33227
  variant: "light",
@@ -33375,10 +33380,16 @@ var FIELD_DISPLAY = [
33375
33380
  { key: "accountName", label: "Recipient account name" },
33376
33381
  { key: "accountNumber", label: "Account number" },
33377
33382
  { key: "accountType", label: "Account type", placeholder: "bank or momo" },
33383
+ {
33384
+ key: "bankName",
33385
+ label: "Bank name (required)",
33386
+ placeholder: "e.g. Capitec Bank, FNB, MTN",
33387
+ description: "Must match a YellowCard network name for the recipient's country. The oracle resolves this to a networkId \u2014 a wrong bank routes funds to the wrong clearing system."
33388
+ },
33378
33389
  {
33379
33390
  key: "networkId",
33380
- label: "Network / bank UUID (optional)",
33381
- description: "Leave empty \u2014 oracle resolves via /channels discovery."
33391
+ label: "Network / bank UUID (optional override)",
33392
+ description: "Leave empty \u2014 oracle resolves via /channels discovery using Bank name. Only set to override resolution."
33382
33393
  },
33383
33394
  { key: "country", label: "Country (ISO 3166-1 alpha-2)" },
33384
33395
  { key: "bvn", label: "BVN (NG, optional)" },
@@ -33400,7 +33411,11 @@ var FIELD_DISPLAY = [
33400
33411
  description: "Local currency the recipient receives \u2014 never USD."
33401
33412
  },
33402
33413
  { key: "reason", label: "Reason (optional)" },
33403
- { key: "recipientDid", label: "Recipient DID (optional)" }
33414
+ {
33415
+ key: "recipientDid",
33416
+ label: "Recipient DID (required)",
33417
+ description: "DID of the person being paid out to \u2014 used as YC customerUID for per-user KYC tracking. NEVER inferred from the operator: a missing value fails the row at propose time."
33418
+ }
33404
33419
  ];
33405
33420
  var PaymentRowDetail = ({ row, missing, compat, onBack, onPatch, onRemove, onPropose, onExecute, onCheck, onReset, submitting }) => {
33406
33421
  const isFilled = row.status === "filled";
@@ -44927,4 +44942,4 @@ export {
44927
44942
  getExtraSlashMenuItems,
44928
44943
  useCreateIxoEditor
44929
44944
  };
44930
- //# sourceMappingURL=chunk-GK3FNKQL.mjs.map
44945
+ //# sourceMappingURL=chunk-DJHCUHR5.mjs.map