@capxul/sdk-react 4.20.0-beta.4 → 4.20.0-beta.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.
- package/README.md +15 -0
- package/dist/{controllers-DXmEHrnw.mjs → controllers-L1UqwWS1.mjs} +5 -2
- package/dist/index.d.mts +99 -32
- package/dist/index.mjs +542 -198
- package/dist/testing/index.mjs +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { _ as useCapxul, a as useCapxulAuth, c as useCapxulIdentityOrNull, d as toInvocationControls, f as toInvocationOptions, g as useCapxulClientOrNull, h as capxulKeys, i as entered, l as useCapxulSend, m as actingAccount, n as CapxulOnboardingController, o as useCapxulDestination, p as accountKey, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-
|
|
2
|
+
import { _ as useCapxul, a as useCapxulAuth, c as useCapxulIdentityOrNull, d as toInvocationControls, f as toInvocationOptions, g as useCapxulClientOrNull, h as capxulKeys, i as entered, l as useCapxulSend, m as actingAccount, n as CapxulOnboardingController, o as useCapxulDestination, p as accountKey, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-L1UqwWS1.mjs";
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
5
|
-
import { CAPXUL_OPERATIONS, CapxulError, EVM_ADDRESS_RE, Errors, HANDLE_RE, PAYMENT_DIRECTIONS, PAYMENT_STATUSES, fingerprintPaymentIntent, formatMoney, isCapxulError, isClaimed, isClaimed as isClaimed$1, isMoneyParseError, isRestoring, isRestoring as isRestoring$1, parseMoney, paymentPhase, resolveIdentityDestination, toEvmAddress, toPartyId } from "@capxul/sdk";
|
|
5
|
+
import { CAPXUL_OPERATIONS, CapxulError, EVM_ADDRESS_RE, Errors, HANDLE_RE, PAYMENT_DIRECTIONS, PAYMENT_STATUSES, assetSymbolFor, fingerprintPaymentIntent, formatAssetAmount, formatMoney, isCapxulError, isClaimed, isClaimed as isClaimed$1, isMoneyParseError, isRestoring, isRestoring as isRestoring$1, parseAssetAmount, parseMoney, paymentPhase, requireAsset, resolveIdentityDestination, toEvmAddress, toPartyId, toRaw, valueActivitySummary } from "@capxul/sdk";
|
|
6
6
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
7
7
|
//#region src/internal/require-bootstrapped-client.ts
|
|
8
8
|
/**
|
|
@@ -1079,7 +1079,7 @@ function useContactsContext() {
|
|
|
1079
1079
|
if (engine === null) throw new Error("CapxulContacts parts must be used inside <CapxulContacts>");
|
|
1080
1080
|
return engine;
|
|
1081
1081
|
}
|
|
1082
|
-
function Root$
|
|
1082
|
+
function Root$7({ actor, onAdded, onFailed, children }) {
|
|
1083
1083
|
const engine = useContacts({
|
|
1084
1084
|
actor,
|
|
1085
1085
|
onAdded,
|
|
@@ -1103,7 +1103,7 @@ function Add({ children }) {
|
|
|
1103
1103
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useContactsContext().add) });
|
|
1104
1104
|
}
|
|
1105
1105
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
1106
|
-
const CapxulContacts = Object.assign(Root$
|
|
1106
|
+
const CapxulContacts = Object.assign(Root$7, {
|
|
1107
1107
|
Summary: Summary$3,
|
|
1108
1108
|
List,
|
|
1109
1109
|
Add
|
|
@@ -1141,15 +1141,8 @@ function useActivityEnabled() {
|
|
|
1141
1141
|
}
|
|
1142
1142
|
//#endregion
|
|
1143
1143
|
//#region src/headless/activity/detail.ts
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
* the payslip, else the receipt, else nothing. Those two kinds are the ones the
|
|
1147
|
-
* Download receipt button is for. A Movement detail carries no documents.
|
|
1148
|
-
*/
|
|
1149
|
-
function pickReceiptDocument(detail) {
|
|
1150
|
-
if (detail === null || detail.kind !== "payment") return null;
|
|
1151
|
-
const documents = detail.payment.documents;
|
|
1152
|
-
return documents.find((document) => document.kind === "payslip") ?? documents.find((document) => document.kind === "receipt") ?? null;
|
|
1144
|
+
function detailDocuments(detail) {
|
|
1145
|
+
return detail === null ? [] : detail.kind === "payment" ? detail.payment.documents : detail.documents;
|
|
1153
1146
|
}
|
|
1154
1147
|
/**
|
|
1155
1148
|
* The root actor and the whole reference are included. A payment reference can
|
|
@@ -1184,27 +1177,76 @@ function useActivityDetail(reference, actor) {
|
|
|
1184
1177
|
const enabled = useActivityEnabled() && reference !== void 0;
|
|
1185
1178
|
const detail = useQuery({
|
|
1186
1179
|
queryKey: accountKey(capxulKeys.activityDetail(reference, actor), identity),
|
|
1187
|
-
queryFn: async () => {
|
|
1180
|
+
queryFn: async ({ signal }) => {
|
|
1188
1181
|
if (reference === void 0) throw Errors.invalidInput("reference", "required for activity.get");
|
|
1189
|
-
return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.get).activity.get(reference,
|
|
1182
|
+
return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.get).activity.get(reference, {
|
|
1183
|
+
...actor === void 0 ? {} : { actor },
|
|
1184
|
+
signal
|
|
1185
|
+
}));
|
|
1190
1186
|
},
|
|
1191
1187
|
enabled
|
|
1192
1188
|
});
|
|
1193
|
-
const
|
|
1189
|
+
const documents = detailDocuments(detail.data ?? null);
|
|
1190
|
+
const [selected, setSelected] = useState(null);
|
|
1191
|
+
const selectedDocument = (selected === null ? void 0 : documents.find((doc) => doc.documentHash === selected.documentHash && doc.contentHash === selected.contentHash)) ?? documents.find((doc) => doc.role === "instruction") ?? documents.find((doc) => doc.role === "movement") ?? documents[0] ?? null;
|
|
1192
|
+
const pending = useRef(/* @__PURE__ */ new Set());
|
|
1193
|
+
const mounted = useRef(true);
|
|
1194
|
+
useEffect(() => {
|
|
1195
|
+
mounted.current = true;
|
|
1196
|
+
const reads = pending.current;
|
|
1197
|
+
return () => {
|
|
1198
|
+
mounted.current = false;
|
|
1199
|
+
for (const read of reads) read.abort();
|
|
1200
|
+
reads.clear();
|
|
1201
|
+
};
|
|
1202
|
+
}, []);
|
|
1203
|
+
const selectDocument = useCallback((ref) => {
|
|
1204
|
+
if (!documents.some((doc) => doc.documentHash === ref.documentHash && doc.contentHash === ref.contentHash)) throw Errors.invalidInput("documentHash", "document not found");
|
|
1205
|
+
for (const read of pending.current) read.abort();
|
|
1206
|
+
pending.current.clear();
|
|
1207
|
+
setIsOpeningDocument(false);
|
|
1208
|
+
setSelected({
|
|
1209
|
+
documentHash: ref.documentHash,
|
|
1210
|
+
contentHash: ref.contentHash
|
|
1211
|
+
});
|
|
1212
|
+
}, [documents]);
|
|
1194
1213
|
const [isOpeningDocument, setIsOpeningDocument] = useState(false);
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1214
|
+
const readDocument = useCallback(async (download) => {
|
|
1215
|
+
const operation = download ? CAPXUL_OPERATIONS.paymentDocuments.get : CAPXUL_OPERATIONS.paymentDocuments.render;
|
|
1216
|
+
if (!mounted.current) throw Errors.cancelled({ operation });
|
|
1217
|
+
if (reference === void 0 || selectedDocument === null) throw Errors.invalidInput("documentHash", "no document on this row");
|
|
1218
|
+
const controller = new AbortController();
|
|
1219
|
+
pending.current.add(controller);
|
|
1197
1220
|
setIsOpeningDocument(true);
|
|
1198
1221
|
try {
|
|
1199
|
-
|
|
1222
|
+
const bootstrapped = requireBootstrappedClient(client, download ? CAPXUL_OPERATIONS.paymentDocuments.get : CAPXUL_OPERATIONS.paymentDocuments.render);
|
|
1223
|
+
const pair = {
|
|
1224
|
+
documentHash: selectedDocument.documentHash,
|
|
1225
|
+
contentHash: selectedDocument.contentHash
|
|
1226
|
+
};
|
|
1227
|
+
const options = { signal: controller.signal };
|
|
1228
|
+
const result = download ? await bootstrapped.paymentDocuments.get(pair, options) : await bootstrapped.paymentDocuments.render(pair, options);
|
|
1229
|
+
if (controller.signal.aborted) throw Errors.cancelled({ operation });
|
|
1230
|
+
return unwrapCapxulResult(result);
|
|
1200
1231
|
} finally {
|
|
1201
|
-
|
|
1232
|
+
pending.current.delete(controller);
|
|
1233
|
+
if (!controller.signal.aborted) setIsOpeningDocument(pending.current.size > 0);
|
|
1202
1234
|
}
|
|
1203
1235
|
}, [
|
|
1204
1236
|
client,
|
|
1205
|
-
|
|
1237
|
+
selectedDocument,
|
|
1206
1238
|
reference
|
|
1207
1239
|
]);
|
|
1240
|
+
const renderDocument = useCallback(async () => {
|
|
1241
|
+
const result = await readDocument(false);
|
|
1242
|
+
if (!("html" in result)) throw Errors.invalidInput("documentHash", "invalid document render");
|
|
1243
|
+
return result;
|
|
1244
|
+
}, [readDocument]);
|
|
1245
|
+
const downloadDocument = useCallback(async () => {
|
|
1246
|
+
const result = await readDocument(true);
|
|
1247
|
+
if (!("bytesBase64" in result)) throw Errors.invalidInput("documentHash", "invalid document download");
|
|
1248
|
+
return result;
|
|
1249
|
+
}, [readDocument]);
|
|
1208
1250
|
const [typed, setTyped] = useState(null);
|
|
1209
1251
|
const annotate = useCapxulAnnotateMovement();
|
|
1210
1252
|
const saveMemo = useCallback(async () => {
|
|
@@ -1233,8 +1275,11 @@ function useActivityDetail(reference, actor) {
|
|
|
1233
1275
|
status: detail.data.payment.status,
|
|
1234
1276
|
...detail.data.payment.execution === void 0 ? {} : { execution: detail.data.payment.execution }
|
|
1235
1277
|
} } : {},
|
|
1236
|
-
|
|
1237
|
-
|
|
1278
|
+
documents,
|
|
1279
|
+
selectedDocument,
|
|
1280
|
+
selectDocument,
|
|
1281
|
+
renderDocument,
|
|
1282
|
+
downloadDocument,
|
|
1238
1283
|
isOpeningDocument,
|
|
1239
1284
|
memo: typed ?? detail.data?.annotation?.memo ?? "",
|
|
1240
1285
|
setMemo: setTyped,
|
|
@@ -1247,6 +1292,7 @@ function useActivityDetail(reference, actor) {
|
|
|
1247
1292
|
//#endregion
|
|
1248
1293
|
//#region src/headless/activity/summary.ts
|
|
1249
1294
|
const DAY_MS = 864e5;
|
|
1295
|
+
const display = (money) => money === null ? null : formatMoney(money);
|
|
1250
1296
|
/**
|
|
1251
1297
|
* `"last30days"` is the last 30 whole UTC days including today. Quantizing to
|
|
1252
1298
|
* the UTC day keeps the cache key stable for a whole day instead of changing on
|
|
@@ -1257,57 +1303,6 @@ function resolveWindow(window, now) {
|
|
|
1257
1303
|
if (window === "last30days") return { from: Math.floor(now / DAY_MS) * DAY_MS - 29 * DAY_MS };
|
|
1258
1304
|
return window;
|
|
1259
1305
|
}
|
|
1260
|
-
/** An integer raw-unit string lifted into a major-unit decimal string. */
|
|
1261
|
-
function majorUnits(raw, decimals) {
|
|
1262
|
-
const negative = raw.startsWith("-");
|
|
1263
|
-
const digits = negative ? raw.slice(1) : raw;
|
|
1264
|
-
const sign = negative ? "-" : "";
|
|
1265
|
-
if (decimals === 0) return `${sign}${digits}`;
|
|
1266
|
-
const padded = digits.padStart(decimals + 1, "0");
|
|
1267
|
-
const cut = padded.length - decimals;
|
|
1268
|
-
return `${sign}${padded.slice(0, cut)}.${padded.slice(cut)}`;
|
|
1269
|
-
}
|
|
1270
|
-
function absoluteNet(total) {
|
|
1271
|
-
const net = BigInt(total.net);
|
|
1272
|
-
return net < 0n ? -net : net;
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* Buckets carry different `decimals`, so their raw integers are not comparable.
|
|
1276
|
-
* Both are scaled to the wider precision before the magnitudes are ordered.
|
|
1277
|
-
*/
|
|
1278
|
-
function compareNet(left, right) {
|
|
1279
|
-
const width = Math.max(left.decimals, right.decimals);
|
|
1280
|
-
const scaledLeft = absoluteNet(left) * 10n ** BigInt(width - left.decimals);
|
|
1281
|
-
const scaledRight = absoluteNet(right) * 10n ** BigInt(width - right.decimals);
|
|
1282
|
-
if (scaledLeft === scaledRight) return 0;
|
|
1283
|
-
return scaledLeft < scaledRight ? -1 : 1;
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* Which currency `.Summary` shows: the bucket matching the primary holding's
|
|
1287
|
-
* currency, else the bucket with the largest absolute `net`, breaking a tie on
|
|
1288
|
-
* currency code ascending so the choice is stable across reloads.
|
|
1289
|
-
*
|
|
1290
|
-
* The primary holding is matched on `available.currency` — the consumer
|
|
1291
|
-
* `CurrencyCode` — and NOT on `Holding.symbol`, which is the raw token ticker
|
|
1292
|
-
* ("USDX") and can never equal a bucket's currency ("USD"). See the contract
|
|
1293
|
-
* correction on #1449.
|
|
1294
|
-
*/
|
|
1295
|
-
function pickBucket(totals, primaryCurrency) {
|
|
1296
|
-
if (primaryCurrency !== null) {
|
|
1297
|
-
const matched = totals.find((total) => total.currency === primaryCurrency);
|
|
1298
|
-
if (matched !== void 0) return matched;
|
|
1299
|
-
}
|
|
1300
|
-
let best = null;
|
|
1301
|
-
for (const total of totals) {
|
|
1302
|
-
if (best === null) {
|
|
1303
|
-
best = total;
|
|
1304
|
-
continue;
|
|
1305
|
-
}
|
|
1306
|
-
const order = compareNet(total, best);
|
|
1307
|
-
if (order > 0 || order === 0 && total.currency < best.currency) best = total;
|
|
1308
|
-
}
|
|
1309
|
-
return best;
|
|
1310
|
-
}
|
|
1311
1306
|
/**
|
|
1312
1307
|
* The current UTC day number, which advances ON ITS OWN at UTC midnight.
|
|
1313
1308
|
*
|
|
@@ -1351,47 +1346,36 @@ function useActivitySummary(actor, window) {
|
|
|
1351
1346
|
const params = {};
|
|
1352
1347
|
if (actor !== void 0) params.actor = actor;
|
|
1353
1348
|
if (range !== void 0) params.window = range;
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
queryKey: accountKey([...capxulKeys.holdings(actor), "primary"], identity),
|
|
1360
|
-
queryFn: async () => {
|
|
1361
|
-
return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.holdings.primary).holdings.primary(actor === void 0 ? void 0 : { actor }));
|
|
1349
|
+
const result = unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.summary).activity.summary(params));
|
|
1350
|
+
return {
|
|
1351
|
+
...result,
|
|
1352
|
+
valuation: valueActivitySummary(result.totals)
|
|
1353
|
+
};
|
|
1362
1354
|
},
|
|
1363
1355
|
enabled
|
|
1364
1356
|
});
|
|
1365
1357
|
const totals = summary.data?.totals;
|
|
1366
|
-
const
|
|
1367
|
-
|
|
1368
|
-
const bucket = useMemo(() => totals === void 0 || isLoading ? null : pickBucket(totals, primaryCurrency), [
|
|
1369
|
-
isLoading,
|
|
1370
|
-
primaryCurrency,
|
|
1371
|
-
totals
|
|
1372
|
-
]);
|
|
1373
|
-
if (bucket === null) return {
|
|
1358
|
+
const isLoading = summary.isLoading;
|
|
1359
|
+
if (summary.data === void 0 || totals === void 0 || totals.length === 0 || isLoading) return {
|
|
1374
1360
|
currency: null,
|
|
1375
1361
|
moneyIn: null,
|
|
1376
1362
|
moneyOut: null,
|
|
1377
1363
|
net: null,
|
|
1378
1364
|
pending: null,
|
|
1365
|
+
unrated: [],
|
|
1379
1366
|
count: summary.data?.count ?? 0,
|
|
1380
1367
|
isLoading,
|
|
1381
1368
|
error: asCapxulError$1(summary.error),
|
|
1382
1369
|
...summary.data?.readWarning === void 0 ? {} : { readWarning: summary.data.readWarning }
|
|
1383
1370
|
};
|
|
1384
|
-
const
|
|
1385
|
-
currency: bucket.currency,
|
|
1386
|
-
value: majorUnits(raw, bucket.decimals),
|
|
1387
|
-
decimals: bucket.decimals
|
|
1388
|
-
});
|
|
1371
|
+
const valuation = summary.data.valuation;
|
|
1389
1372
|
return {
|
|
1390
|
-
currency:
|
|
1391
|
-
moneyIn: display(
|
|
1392
|
-
moneyOut: display(
|
|
1393
|
-
net: display(
|
|
1394
|
-
pending: display(
|
|
1373
|
+
currency: valuation.moneyIn?.currency ?? null,
|
|
1374
|
+
moneyIn: display(valuation.moneyIn),
|
|
1375
|
+
moneyOut: display(valuation.moneyOut),
|
|
1376
|
+
net: display(valuation.net),
|
|
1377
|
+
pending: display(valuation.pending),
|
|
1378
|
+
unrated: valuation.unrated,
|
|
1395
1379
|
count: summary.data?.count ?? 0,
|
|
1396
1380
|
isLoading,
|
|
1397
1381
|
error: asCapxulError$1(summary.error),
|
|
@@ -1418,7 +1402,7 @@ const SIGN = Object.freeze({
|
|
|
1418
1402
|
self: ""
|
|
1419
1403
|
});
|
|
1420
1404
|
function toActivityRow(item, actor) {
|
|
1421
|
-
const amountDisplay =
|
|
1405
|
+
const amountDisplay = formatAssetAmount(item.amount);
|
|
1422
1406
|
const signedAmountDisplay = `${SIGN[item.direction]}${amountDisplay}`;
|
|
1423
1407
|
if (item.kind === "movement") return {
|
|
1424
1408
|
kind: "movement",
|
|
@@ -1430,7 +1414,7 @@ function toActivityRow(item, actor) {
|
|
|
1430
1414
|
counterpartyLabel: item.counterpartyLabel,
|
|
1431
1415
|
amountDisplay,
|
|
1432
1416
|
signedAmountDisplay,
|
|
1433
|
-
currency: item.amount.
|
|
1417
|
+
currency: assetSymbolFor(item.amount.asset),
|
|
1434
1418
|
createdAt: item.createdAt,
|
|
1435
1419
|
classification: item.classification,
|
|
1436
1420
|
phase: MOVEMENT_PHASE,
|
|
@@ -1452,7 +1436,7 @@ function toActivityRow(item, actor) {
|
|
|
1452
1436
|
counterpartyLabel: item.counterpartyLabel,
|
|
1453
1437
|
amountDisplay,
|
|
1454
1438
|
signedAmountDisplay,
|
|
1455
|
-
currency: item.amount.
|
|
1439
|
+
currency: assetSymbolFor(item.amount.asset),
|
|
1456
1440
|
createdAt: item.createdAt,
|
|
1457
1441
|
ledgerKind: item.ledgerKind,
|
|
1458
1442
|
paymentType: item.paymentType,
|
|
@@ -1533,7 +1517,7 @@ function buildCsv(items, actor, search) {
|
|
|
1533
1517
|
row.counterpartyLabel,
|
|
1534
1518
|
row.direction,
|
|
1535
1519
|
item.amount.value,
|
|
1536
|
-
item.amount.
|
|
1520
|
+
assetSymbolFor(item.amount.asset),
|
|
1537
1521
|
row.kind === "payment" ? row.execution?.notSent ? "not_sent" : row.execution?.status === "unresolved" ? "verifying" : row.status : "",
|
|
1538
1522
|
row.reference.id,
|
|
1539
1523
|
row.receipt?.transactionHash ?? ""
|
|
@@ -1789,7 +1773,7 @@ function useActivityContext() {
|
|
|
1789
1773
|
if (engine === null) throw new Error("CapxulActivity parts must be used inside <CapxulActivity>");
|
|
1790
1774
|
return engine;
|
|
1791
1775
|
}
|
|
1792
|
-
function Root$
|
|
1776
|
+
function Root$6({ actor, pageSize, children }) {
|
|
1793
1777
|
const engine = useActivity({
|
|
1794
1778
|
...actor === void 0 ? {} : { actor },
|
|
1795
1779
|
...pageSize === void 0 ? {} : { pageSize }
|
|
@@ -1840,7 +1824,7 @@ function Export({ children }) {
|
|
|
1840
1824
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useActivityContext().csv) });
|
|
1841
1825
|
}
|
|
1842
1826
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
1843
|
-
const CapxulActivity = Object.assign(Root$
|
|
1827
|
+
const CapxulActivity = Object.assign(Root$6, {
|
|
1844
1828
|
Summary: Summary$2,
|
|
1845
1829
|
Filters,
|
|
1846
1830
|
Search,
|
|
@@ -1881,6 +1865,26 @@ function useOrgMeReading(orgId) {
|
|
|
1881
1865
|
]);
|
|
1882
1866
|
}
|
|
1883
1867
|
//#endregion
|
|
1868
|
+
//#region src/internal/write-names-asset.ts
|
|
1869
|
+
/**
|
|
1870
|
+
* Identity law: "A payment names an asset or inherits it from an authorized
|
|
1871
|
+
* fact. Missing asset refuses." The rule was recorded in the retired
|
|
1872
|
+
* treasury-cut handoff (removed from the current tree by the exact-asset
|
|
1873
|
+
* contraction; its record survives in Git history).
|
|
1874
|
+
*
|
|
1875
|
+
* A Capxul payment writer takes `PaymentMoney` — a peg currency, a value and
|
|
1876
|
+
* `decimals` — and settles one fixed legacy token. No `AssetId` crosses that
|
|
1877
|
+
* seam, so a payment composed from a named position cannot name the asset it
|
|
1878
|
+
* moves: a position selected as USDT authorizes a different token.
|
|
1879
|
+
*
|
|
1880
|
+
* `false` until the writer takes the command's asset. The Send and Payroll
|
|
1881
|
+
* composers read this to refuse a submission rather than authorize a token the
|
|
1882
|
+
* person did not pick.
|
|
1883
|
+
*/
|
|
1884
|
+
function writeNamesAsset() {
|
|
1885
|
+
return false;
|
|
1886
|
+
}
|
|
1887
|
+
//#endregion
|
|
1884
1888
|
//#region src/headless/send-money/signer-status.ts
|
|
1885
1889
|
const NO_SUBSCRIPTION = () => () => {};
|
|
1886
1890
|
function useSignerStatus(client) {
|
|
@@ -2030,6 +2034,37 @@ function useSelectedOption(options) {
|
|
|
2030
2034
|
}, [options]);
|
|
2031
2035
|
return [options.find((option) => option.id === selectedId) ?? null, select];
|
|
2032
2036
|
}
|
|
2037
|
+
/**
|
|
2038
|
+
* One Send `.Asset` option per position the Account holds, taken from
|
|
2039
|
+
* `account.balances` — never from `account.available`, which is the Account's
|
|
2040
|
+
* fiat VALUATION of those positions (ADR-0028 R5/R6). Labelling the valuation
|
|
2041
|
+
* was the false identity this cut removes, and it collapsed USDC+USDT into one
|
|
2042
|
+
* "USD" row.
|
|
2043
|
+
*
|
|
2044
|
+
* The option is keyed on the ASSET id, never the symbol and never the account:
|
|
2045
|
+
* several positions share one account id, and a symbol is display only
|
|
2046
|
+
* (ADR-0028 R3). Every row is listed, so a real multi-position Account still
|
|
2047
|
+
* offers its holdings.
|
|
2048
|
+
*/
|
|
2049
|
+
function assetSelectionsFromAccount$1(account) {
|
|
2050
|
+
if (account === null) return [];
|
|
2051
|
+
return account.balances.map((position) => {
|
|
2052
|
+
return {
|
|
2053
|
+
option: {
|
|
2054
|
+
id: position.asset,
|
|
2055
|
+
symbol: position.symbol,
|
|
2056
|
+
availableDisplay: formatAssetAmount({
|
|
2057
|
+
asset: position.asset,
|
|
2058
|
+
value: position.available
|
|
2059
|
+
})
|
|
2060
|
+
},
|
|
2061
|
+
money: {
|
|
2062
|
+
asset: position.asset,
|
|
2063
|
+
value: position.available
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2033
2068
|
function useSendMoney(input) {
|
|
2034
2069
|
const { actor, onSent, onFailed } = input;
|
|
2035
2070
|
const client = useCapxulClientOrNull();
|
|
@@ -2042,15 +2077,12 @@ function useSendMoney(input) {
|
|
|
2042
2077
|
const treasury = useCapxulOrgTreasury(orgId, { enabled: actor.kind === "organization" });
|
|
2043
2078
|
const accountQuery = actor.kind === "personal" ? balance : treasury;
|
|
2044
2079
|
const account = accountQuery.data ?? null;
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
symbol: account.available.currency,
|
|
2048
|
-
availableDisplay: formatMoney(account.available, { grammar: "code" })
|
|
2049
|
-
}], [account]);
|
|
2080
|
+
const assetSelections = useMemo(() => assetSelectionsFromAccount$1(account), [account]);
|
|
2081
|
+
const assetOptions = useMemo(() => assetSelections.map((selection) => selection.option), [assetSelections]);
|
|
2050
2082
|
const [selectedAssetOption, selectAsset] = useSelectedOption(assetOptions);
|
|
2051
|
-
const selectedAssetMoney =
|
|
2083
|
+
const selectedAssetMoney = useMemo(() => assetSelections.find((selection) => selection.option.id === selectedAssetOption?.id)?.money ?? null, [assetSelections, selectedAssetOption]);
|
|
2052
2084
|
const [amountText, setAmountText] = useState("");
|
|
2053
|
-
const parsed = useMemo(() => selectedAssetMoney === null || amountText.trim() === "" ? null :
|
|
2085
|
+
const parsed = useMemo(() => selectedAssetMoney === null || amountText.trim() === "" ? null : parseAssetAmount(amountText, requireAsset(selectedAssetMoney.asset)), [amountText, selectedAssetMoney]);
|
|
2054
2086
|
const parsedMoney = parsed !== null && !isMoneyParseError(parsed) ? parsed : null;
|
|
2055
2087
|
const amountError = parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null;
|
|
2056
2088
|
const recipient = useRecipientResolution(client, JSON.stringify([
|
|
@@ -2062,7 +2094,7 @@ function useSendMoney(input) {
|
|
|
2062
2094
|
const personalPay = useCapxulPay();
|
|
2063
2095
|
const organizationPay = useCapxulOrganizationPay(orgId);
|
|
2064
2096
|
const pay = actor.kind === "personal" ? personalPay : organizationPay;
|
|
2065
|
-
const blockedReason = signerStatus !== "ready" ? "signer-not-ready" : actor.kind === "organization" && selectedSource === null ? "no-source" : selectedAssetOption === null ? "no-asset" : parsedMoney === null ? "amount-invalid" : resolvedReference === null ? "recipient-unresolved" : null;
|
|
2097
|
+
const blockedReason = signerStatus !== "ready" ? "signer-not-ready" : actor.kind === "organization" && selectedSource === null ? "no-source" : selectedAssetOption === null ? "no-asset" : parsedMoney === null ? "amount-invalid" : resolvedReference === null ? "recipient-unresolved" : writeNamesAsset() ? null : "asset-unnamed";
|
|
2066
2098
|
const submitGate = useRef(false);
|
|
2067
2099
|
const submit = useCallback(() => {
|
|
2068
2100
|
if (blockedReason !== null || submitGate.current) return;
|
|
@@ -2149,7 +2181,7 @@ function useSendMoneyContext() {
|
|
|
2149
2181
|
if (engine === null) throw new Error("CapxulSendMoney parts must be used inside <CapxulSendMoney>");
|
|
2150
2182
|
return engine;
|
|
2151
2183
|
}
|
|
2152
|
-
function Root$
|
|
2184
|
+
function Root$5({ actor, onSent, onFailed, children }) {
|
|
2153
2185
|
const engine = useSendMoney({
|
|
2154
2186
|
actor,
|
|
2155
2187
|
onSent,
|
|
@@ -2176,7 +2208,7 @@ function Actions$1({ children }) {
|
|
|
2176
2208
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useSendMoneyContext().actions) });
|
|
2177
2209
|
}
|
|
2178
2210
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
2179
|
-
const CapxulSendMoney = Object.assign(Root$
|
|
2211
|
+
const CapxulSendMoney = Object.assign(Root$5, {
|
|
2180
2212
|
Source,
|
|
2181
2213
|
Asset: Asset$1,
|
|
2182
2214
|
Amount,
|
|
@@ -2266,7 +2298,7 @@ function useOrgMemberContext() {
|
|
|
2266
2298
|
if (state === null) throw new Error("CapxulOrgMember parts must be used inside <CapxulOrgMember>");
|
|
2267
2299
|
return state;
|
|
2268
2300
|
}
|
|
2269
|
-
function Root$
|
|
2301
|
+
function Root$4({ orgId, children }) {
|
|
2270
2302
|
const state = useOrgMember(orgId);
|
|
2271
2303
|
return /* @__PURE__ */ jsx(OrgMemberContext.Provider, {
|
|
2272
2304
|
value: state,
|
|
@@ -2286,7 +2318,7 @@ function Standing({ children }) {
|
|
|
2286
2318
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useOrgMemberContext().standing) });
|
|
2287
2319
|
}
|
|
2288
2320
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
2289
|
-
const CapxulOrgMember = Object.assign(Root$
|
|
2321
|
+
const CapxulOrgMember = Object.assign(Root$4, {
|
|
2290
2322
|
Can,
|
|
2291
2323
|
Standing
|
|
2292
2324
|
});
|
|
@@ -2446,7 +2478,7 @@ function usePart(phase) {
|
|
|
2446
2478
|
}, [placed, phase]);
|
|
2447
2479
|
return state.phase === phase ? state : null;
|
|
2448
2480
|
}
|
|
2449
|
-
function Root$
|
|
2481
|
+
function Root$3({ scope, children }) {
|
|
2450
2482
|
const state = useDashboardAccess(scope);
|
|
2451
2483
|
const [placed] = useState(() => /* @__PURE__ */ new Set());
|
|
2452
2484
|
const value = useMemo(() => ({
|
|
@@ -2464,7 +2496,7 @@ function Root$2({ scope, children }) {
|
|
|
2464
2496
|
});
|
|
2465
2497
|
}
|
|
2466
2498
|
/** Hands nothing — today's app renders `null` here, exactly as it does now. */
|
|
2467
|
-
function Checking({ children }) {
|
|
2499
|
+
function Checking$1({ children }) {
|
|
2468
2500
|
return usePart("checking") === null ? null : /* @__PURE__ */ jsx(Fragment, { children });
|
|
2469
2501
|
}
|
|
2470
2502
|
function DashboardError({ children }) {
|
|
@@ -2491,19 +2523,14 @@ function Granted({ children }) {
|
|
|
2491
2523
|
return usePart("granted") === null ? null : /* @__PURE__ */ jsx(Fragment, { children });
|
|
2492
2524
|
}
|
|
2493
2525
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
2494
|
-
const CapxulDashboardAccess = Object.assign(Root$
|
|
2495
|
-
Checking,
|
|
2526
|
+
const CapxulDashboardAccess = Object.assign(Root$3, {
|
|
2527
|
+
Checking: Checking$1,
|
|
2496
2528
|
Error: DashboardError,
|
|
2497
2529
|
Redirect,
|
|
2498
2530
|
Granted
|
|
2499
2531
|
});
|
|
2500
2532
|
//#endregion
|
|
2501
2533
|
//#region src/headless/payroll/money.ts
|
|
2502
|
-
/** `{ value: "12.34", decimals: 6 }` → `12340000n`. */
|
|
2503
|
-
function toMinorUnits(money) {
|
|
2504
|
-
const [whole = "0", fraction = ""] = money.value.split(".");
|
|
2505
|
-
return BigInt(`${whole}${fraction.padEnd(money.decimals, "0")}`);
|
|
2506
|
-
}
|
|
2507
2534
|
const TYPED_AMOUNT = /^(-?)(\d+)(?:\.(\d+))?$/;
|
|
2508
2535
|
/**
|
|
2509
2536
|
* Σ of the employer's TYPED amounts on a saved group — a DISPLAY total.
|
|
@@ -2536,6 +2563,17 @@ function fromMinorUnits(raw, asset) {
|
|
|
2536
2563
|
decimals: asset.decimals
|
|
2537
2564
|
};
|
|
2538
2565
|
}
|
|
2566
|
+
/** Convert exact raw units to one asset quantity without a fiat denomination. */
|
|
2567
|
+
function assetFromMinorUnits(raw, asset) {
|
|
2568
|
+
const money = fromMinorUnits(raw, {
|
|
2569
|
+
currency: "USD",
|
|
2570
|
+
decimals: asset.decimals
|
|
2571
|
+
});
|
|
2572
|
+
return {
|
|
2573
|
+
asset: asset.asset,
|
|
2574
|
+
value: money.value
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2539
2577
|
//#endregion
|
|
2540
2578
|
//#region src/headless/payroll/reads.ts
|
|
2541
2579
|
function toRecipientOption(entry) {
|
|
@@ -2652,7 +2690,7 @@ function scopedClient(client, method, orgId) {
|
|
|
2652
2690
|
//#endregion
|
|
2653
2691
|
//#region src/headless/payroll/use-payroll.ts
|
|
2654
2692
|
/** Money reads the same way everywhere on this screen: amount then code. */
|
|
2655
|
-
const GRAMMAR
|
|
2693
|
+
const GRAMMAR = { grammar: "code" };
|
|
2656
2694
|
/**
|
|
2657
2695
|
* One captured snapshot, or nothing — never a fabricated empty options object,
|
|
2658
2696
|
* and never a DOM event a screen wired straight to `onClick`.
|
|
@@ -2668,7 +2706,7 @@ function toPayrollGroupRow(group) {
|
|
|
2668
2706
|
name: group.name,
|
|
2669
2707
|
tone: group.tone,
|
|
2670
2708
|
memberCount: group.members.length,
|
|
2671
|
-
totalDisplay: currency === void 0 ? "" : formatMoney(sumTypedAmounts(group.members.map((member) => member.amount), currency), GRAMMAR
|
|
2709
|
+
totalDisplay: currency === void 0 ? "" : formatMoney(sumTypedAmounts(group.members.map((member) => member.amount), currency), GRAMMAR),
|
|
2672
2710
|
members: group.members.map((member) => ({
|
|
2673
2711
|
recipientId: member.partyId,
|
|
2674
2712
|
amount: member.amount,
|
|
@@ -2758,8 +2796,8 @@ function usePayroll(orgId) {
|
|
|
2758
2796
|
const started = orgId !== void 0;
|
|
2759
2797
|
return {
|
|
2760
2798
|
summary: {
|
|
2761
|
-
walletBalanceDisplay: treasury.data === void 0 ? null : formatMoney(treasury.data.available, GRAMMAR
|
|
2762
|
-
paidThisMonthDisplay: runs.data === void 0 ? null :
|
|
2799
|
+
walletBalanceDisplay: treasury.data?.available === void 0 || treasury.data.available === null ? null : formatMoney(treasury.data.available, GRAMMAR),
|
|
2800
|
+
paidThisMonthDisplay: runs.data === void 0 ? null : runs.data.settledThisMonth.map(formatAssetAmount).join(", "),
|
|
2763
2801
|
isLoading: started && (treasury.data === void 0 && treasury.error === null || runs.data === void 0 && runs.error === null),
|
|
2764
2802
|
error: treasury.error ?? runs.error
|
|
2765
2803
|
},
|
|
@@ -2792,7 +2830,7 @@ function usePayrollContext() {
|
|
|
2792
2830
|
if (engine === null) throw new Error("CapxulPayroll parts must be used inside <CapxulPayroll>");
|
|
2793
2831
|
return engine;
|
|
2794
2832
|
}
|
|
2795
|
-
function Root$
|
|
2833
|
+
function Root$2({ orgId, children }) {
|
|
2796
2834
|
const engine = usePayroll(orgId);
|
|
2797
2835
|
return /* @__PURE__ */ jsx(PayrollContext.Provider, {
|
|
2798
2836
|
value: engine,
|
|
@@ -2810,7 +2848,7 @@ function Groups({ children }) {
|
|
|
2810
2848
|
* There is NO `.Runs` part (ruling Q6): no shipped region has the right shape
|
|
2811
2849
|
* for a run list, and a part without a production caller may not ship.
|
|
2812
2850
|
*/
|
|
2813
|
-
const CapxulPayroll = Object.assign(Root$
|
|
2851
|
+
const CapxulPayroll = Object.assign(Root$2, {
|
|
2814
2852
|
Summary: Summary$1,
|
|
2815
2853
|
Groups
|
|
2816
2854
|
});
|
|
@@ -2820,8 +2858,14 @@ function record(value) {
|
|
|
2820
2858
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("payrollDraft", "Invalid run draft");
|
|
2821
2859
|
return value;
|
|
2822
2860
|
}
|
|
2861
|
+
/**
|
|
2862
|
+
* A draft-scoped opaque key. `/` is in the class so `asset.id` can be the
|
|
2863
|
+
* canonical CAIP-19 `AssetId` (`eip155:<chainId>/erc20:<address>`): the draft
|
|
2864
|
+
* records WHICH asset was authorized, and a symbol is not an identity
|
|
2865
|
+
* (ADR-0028 R3). The field never reaches the backend — only `input` is sent.
|
|
2866
|
+
*/
|
|
2823
2867
|
function identifier(value) {
|
|
2824
|
-
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._
|
|
2868
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/u.test(value)) throw Errors.invalidInput("payrollDraft", "Invalid draft identifier");
|
|
2825
2869
|
return value;
|
|
2826
2870
|
}
|
|
2827
2871
|
function minorAmount(value) {
|
|
@@ -2837,8 +2881,9 @@ function decodeDraft(value, scope) {
|
|
|
2837
2881
|
const items = input.items.map((raw) => {
|
|
2838
2882
|
const item = record(raw);
|
|
2839
2883
|
const to = record(item.to);
|
|
2884
|
+
const amount = record(item.amount);
|
|
2840
2885
|
const partyId = toPartyId(identifier(item.partyId));
|
|
2841
|
-
if (to.kind !== "party" || to.partyId !== partyId || item.gross !== item.net || !Array.isArray(item.adjustments) || item.adjustments.length !== 0) throw Errors.invalidInput("payrollDraft", "Invalid draft recipient");
|
|
2886
|
+
if (to.kind !== "party" || to.partyId !== partyId || item.gross !== item.net || typeof amount.asset !== "string" || typeof amount.value !== "string" || typeof item.currency !== "string" || typeof item.decimals !== "number" || !Number.isInteger(item.decimals) || !Array.isArray(item.adjustments) || item.adjustments.length !== 0) throw Errors.invalidInput("payrollDraft", "Invalid draft recipient");
|
|
2842
2887
|
const net = minorAmount(item.net);
|
|
2843
2888
|
return {
|
|
2844
2889
|
to: {
|
|
@@ -2846,12 +2891,20 @@ function decodeDraft(value, scope) {
|
|
|
2846
2891
|
partyId
|
|
2847
2892
|
},
|
|
2848
2893
|
partyId,
|
|
2894
|
+
amount: {
|
|
2895
|
+
asset: requireAsset(amount.asset).assetId,
|
|
2896
|
+
value: amount.value
|
|
2897
|
+
},
|
|
2849
2898
|
gross: net,
|
|
2850
2899
|
net,
|
|
2900
|
+
currency: item.currency,
|
|
2901
|
+
decimals: item.decimals,
|
|
2851
2902
|
adjustments: []
|
|
2852
2903
|
};
|
|
2853
2904
|
});
|
|
2854
2905
|
if (new Set(items.map((item) => item.partyId)).size !== items.length) throw Errors.invalidInput("payrollDraft", "Duplicate draft recipient");
|
|
2906
|
+
const exactAsset = requireAsset(identifier(asset.id));
|
|
2907
|
+
if (items.some((item) => item.amount.asset !== exactAsset.assetId)) throw Errors.invalidInput("payrollDraft", "Draft item asset does not match the run asset");
|
|
2855
2908
|
return {
|
|
2856
2909
|
version: 1,
|
|
2857
2910
|
scope,
|
|
@@ -2862,6 +2915,7 @@ function decodeDraft(value, scope) {
|
|
|
2862
2915
|
},
|
|
2863
2916
|
input: {
|
|
2864
2917
|
permissionId: identifier(input.permissionId),
|
|
2918
|
+
asset: exactAsset.assetId,
|
|
2865
2919
|
period: {
|
|
2866
2920
|
start: period.start,
|
|
2867
2921
|
end: period.start
|
|
@@ -3065,7 +3119,6 @@ function termsSeeds(terms, asset, period) {
|
|
|
3065
3119
|
}
|
|
3066
3120
|
//#endregion
|
|
3067
3121
|
//#region src/headless/payroll/use-payroll-run.ts
|
|
3068
|
-
const GRAMMAR = { grammar: "code" };
|
|
3069
3122
|
/**
|
|
3070
3123
|
* The period this composer composes: an INSTANT run (ruling Q3). `authorizeRun`
|
|
3071
3124
|
* stamps `start === end === runAt` at submit, and the payslip's period renders
|
|
@@ -3089,6 +3142,32 @@ const INSTANT_RUN_PERIOD = {
|
|
|
3089
3142
|
function amountText(amounts, seeds, recipientId) {
|
|
3090
3143
|
return amounts[recipientId]?.value ?? seeds.get(recipientId) ?? "";
|
|
3091
3144
|
}
|
|
3145
|
+
function freshRunItems(input) {
|
|
3146
|
+
const items = [];
|
|
3147
|
+
for (const [index, recipientId] of input.selected.entries()) {
|
|
3148
|
+
const to = input.payableRefs[index];
|
|
3149
|
+
const amount = input.amounts[index];
|
|
3150
|
+
const terms = input.compensationTerms.get(recipientId);
|
|
3151
|
+
if (to === void 0 || amount === void 0 || terms === void 0) throw Errors.unknown();
|
|
3152
|
+
items.push({
|
|
3153
|
+
to: {
|
|
3154
|
+
kind: "party",
|
|
3155
|
+
partyId: toPartyId(recipientId)
|
|
3156
|
+
},
|
|
3157
|
+
partyId: recipientId,
|
|
3158
|
+
amount,
|
|
3159
|
+
gross: terms.rate,
|
|
3160
|
+
net: terms.rate,
|
|
3161
|
+
currency: terms.currency,
|
|
3162
|
+
decimals: terms.decimals,
|
|
3163
|
+
adjustments: []
|
|
3164
|
+
});
|
|
3165
|
+
}
|
|
3166
|
+
return items;
|
|
3167
|
+
}
|
|
3168
|
+
function hasRequiredCompensation(frozen, writerNamesSelectedAsset, selected, terms) {
|
|
3169
|
+
return frozen || !writerNamesSelectedAsset || selected.every((recipientId) => terms.has(recipientId));
|
|
3170
|
+
}
|
|
3092
3171
|
/**
|
|
3093
3172
|
* WHICH run the composer is composing, by MEANING rather than by object shape.
|
|
3094
3173
|
*
|
|
@@ -3120,6 +3199,33 @@ function initialAmounts(prefill) {
|
|
|
3120
3199
|
};
|
|
3121
3200
|
return seeded;
|
|
3122
3201
|
}
|
|
3202
|
+
/**
|
|
3203
|
+
* One `.Asset` option per position the treasury Account holds, taken from
|
|
3204
|
+
* `account.balances` — never from `account.available`, which is the Account's
|
|
3205
|
+
* fiat VALUATION of those positions (ADR-0028 R5/R6). The option is keyed on
|
|
3206
|
+
* the ASSET id: several positions share one account id, and a symbol is display
|
|
3207
|
+
* only (ADR-0028 R3). Every row is listed, so a real multi-position treasury
|
|
3208
|
+
* still offers its holdings.
|
|
3209
|
+
*/
|
|
3210
|
+
function assetSelectionsFromAccount(account) {
|
|
3211
|
+
if (account === null) return [];
|
|
3212
|
+
return account.balances.map((position) => {
|
|
3213
|
+
return {
|
|
3214
|
+
option: {
|
|
3215
|
+
id: position.asset,
|
|
3216
|
+
symbol: position.symbol,
|
|
3217
|
+
availableDisplay: formatAssetAmount({
|
|
3218
|
+
asset: position.asset,
|
|
3219
|
+
value: position.available
|
|
3220
|
+
})
|
|
3221
|
+
},
|
|
3222
|
+
money: {
|
|
3223
|
+
asset: position.asset,
|
|
3224
|
+
value: position.available
|
|
3225
|
+
}
|
|
3226
|
+
};
|
|
3227
|
+
});
|
|
3228
|
+
}
|
|
3123
3229
|
function usePayrollRun(input) {
|
|
3124
3230
|
const { orgId, prefill, onRun, onFailed } = input;
|
|
3125
3231
|
const client = useCapxulClientOrNull();
|
|
@@ -3133,17 +3239,13 @@ function usePayrollRun(input) {
|
|
|
3133
3239
|
const terms = useOrgTerms(orgId);
|
|
3134
3240
|
const [selected, setSelected] = useState(() => prefill?.recipientIds ?? []);
|
|
3135
3241
|
const [amounts, setAmounts] = useState(() => initialAmounts(prefill));
|
|
3136
|
-
const restoreCurrency = treasury.data?.available.currency;
|
|
3137
3242
|
const { draftScope, currentScope, draft, draftError, restorationPending, activateDraft, restoreDraft, setDraft, recovery, draftBlocked } = useRunDraft(client, orgId, useCallback((restored) => {
|
|
3138
3243
|
setSelected(restored.input.items.map((item) => item.partyId));
|
|
3139
|
-
if (
|
|
3140
|
-
value:
|
|
3141
|
-
currency: restoreCurrency,
|
|
3142
|
-
decimals: restored.asset.decimals
|
|
3143
|
-
}).value,
|
|
3244
|
+
if (requireAsset(restored.input.asset).assetId === restored.input.asset) setAmounts(Object.fromEntries(restored.input.items.map((item) => [item.partyId, {
|
|
3245
|
+
value: item.amount.value,
|
|
3144
3246
|
prefilled: false
|
|
3145
3247
|
}])));
|
|
3146
|
-
}, [
|
|
3248
|
+
}, []));
|
|
3147
3249
|
const frozen = draft !== null;
|
|
3148
3250
|
const composerKey = composerIdentity(orgId, prefill, draftScope, frozen);
|
|
3149
3251
|
const [seededKey, setSeededKey] = useState(composerKey);
|
|
@@ -3183,11 +3285,8 @@ function usePayrollRun(input) {
|
|
|
3183
3285
|
draftError
|
|
3184
3286
|
]);
|
|
3185
3287
|
const account = treasury.data ?? null;
|
|
3186
|
-
const
|
|
3187
|
-
|
|
3188
|
-
symbol: account.available.currency,
|
|
3189
|
-
availableDisplay: formatMoney(account.available, GRAMMAR)
|
|
3190
|
-
}], [account]);
|
|
3288
|
+
const assetSelections = useMemo(() => assetSelectionsFromAccount(account), [account]);
|
|
3289
|
+
const assetOptions = useMemo(() => assetSelections.map((selection) => selection.option), [assetSelections]);
|
|
3191
3290
|
const [selectedAssetId, setSelectedAssetId] = useState(null);
|
|
3192
3291
|
useEffect(() => {
|
|
3193
3292
|
if (!assetOptions.some((option) => option.id === selectedAssetId)) setSelectedAssetId(assetOptions[0]?.id ?? null);
|
|
@@ -3196,7 +3295,7 @@ function usePayrollRun(input) {
|
|
|
3196
3295
|
const selectAsset = useCallback((id) => {
|
|
3197
3296
|
if (!submitGate.current && !frozen) setSelectedAssetId((current) => assetOptions.some((option) => option.id === id) ? id : current);
|
|
3198
3297
|
}, [assetOptions, frozen]);
|
|
3199
|
-
const assetMoney =
|
|
3298
|
+
const assetMoney = assetSelections.find((selection) => selection.option.id === selectedAsset?.id)?.money ?? null;
|
|
3200
3299
|
const named = useMemo(() => new Map(roster.options.map((option) => [option.id, option])), [roster.options]);
|
|
3201
3300
|
const refs = roster.refs;
|
|
3202
3301
|
const payableRefs = useMemo(() => {
|
|
@@ -3208,12 +3307,19 @@ function usePayrollRun(input) {
|
|
|
3208
3307
|
}
|
|
3209
3308
|
return resolved;
|
|
3210
3309
|
}, [refs, selected]);
|
|
3211
|
-
const seeds = useMemo(() =>
|
|
3310
|
+
const seeds = useMemo(() => {
|
|
3311
|
+
if (assetMoney === null) return /* @__PURE__ */ new Map();
|
|
3312
|
+
const metadata = requireAsset(assetMoney.asset);
|
|
3313
|
+
return termsSeeds(terms.data ?? [], metadata.peg === null ? null : {
|
|
3314
|
+
currency: metadata.peg,
|
|
3315
|
+
decimals: metadata.decimals
|
|
3316
|
+
}, INSTANT_RUN_PERIOD);
|
|
3317
|
+
}, [assetMoney, terms.data]);
|
|
3212
3318
|
const entries = useMemo(() => {
|
|
3213
3319
|
if (assetMoney === null) return [];
|
|
3214
3320
|
return selected.map((recipientId) => {
|
|
3215
3321
|
const value = amountText(amounts, seeds, recipientId);
|
|
3216
|
-
const parsed = value.trim() === "" ? null :
|
|
3322
|
+
const parsed = value.trim() === "" ? null : parseAssetAmount(value, requireAsset(assetMoney.asset));
|
|
3217
3323
|
return {
|
|
3218
3324
|
recipientId,
|
|
3219
3325
|
name: named.get(recipientId)?.name ?? recipientId,
|
|
@@ -3221,7 +3327,7 @@ function usePayrollRun(input) {
|
|
|
3221
3327
|
change: (text) => change(recipientId, text),
|
|
3222
3328
|
error: parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null,
|
|
3223
3329
|
prefilled: amounts[recipientId]?.prefilled ?? seeds.has(recipientId),
|
|
3224
|
-
currency: assetMoney.
|
|
3330
|
+
currency: requireAsset(assetMoney.asset).symbol
|
|
3225
3331
|
};
|
|
3226
3332
|
});
|
|
3227
3333
|
}, [
|
|
@@ -3236,7 +3342,7 @@ function usePayrollRun(input) {
|
|
|
3236
3342
|
if (assetMoney === null || selected.length === 0) return null;
|
|
3237
3343
|
const money = [];
|
|
3238
3344
|
for (const recipientId of selected) {
|
|
3239
|
-
const parsed =
|
|
3345
|
+
const parsed = parseAssetAmount(amountText(amounts, seeds, recipientId), requireAsset(assetMoney.asset));
|
|
3240
3346
|
if (isMoneyParseError(parsed)) return null;
|
|
3241
3347
|
money.push(parsed);
|
|
3242
3348
|
}
|
|
@@ -3247,16 +3353,25 @@ function usePayrollRun(input) {
|
|
|
3247
3353
|
seeds,
|
|
3248
3354
|
selected
|
|
3249
3355
|
]);
|
|
3250
|
-
const total = useMemo(() => parsedAmounts === null || assetMoney === null ? null :
|
|
3251
|
-
|
|
3252
|
-
|
|
3356
|
+
const total = useMemo(() => parsedAmounts === null || assetMoney === null ? null : {
|
|
3357
|
+
asset: assetMoney.asset,
|
|
3358
|
+
value: assetFromMinorUnits(parsedAmounts.reduce((sum, amount) => sum + toRaw(amount, requireAsset(amount.asset)), 0n), {
|
|
3359
|
+
asset: assetMoney.asset,
|
|
3360
|
+
decimals: requireAsset(assetMoney.asset).decimals
|
|
3361
|
+
}).value
|
|
3362
|
+
}, [assetMoney, parsedAmounts]);
|
|
3363
|
+
const availableRaw = assetMoney === null ? null : toRaw(assetMoney, requireAsset(assetMoney.asset));
|
|
3364
|
+
const totalRaw = total === null ? null : toRaw(total, requireAsset(total.asset));
|
|
3253
3365
|
const funds = availableRaw === null || totalRaw === null ? "unknown" : totalRaw > availableRaw ? "insufficient" : "available";
|
|
3254
3366
|
const budgets = me.data?.budgets ?? [];
|
|
3367
|
+
const compensationTerms = useMemo(() => new Map((terms.data ?? []).filter((row) => row.unit === "paycheck" && /^[1-9][0-9]*$/u.test(row.rate)).map((row) => [row.partyId, row])), [terms.data]);
|
|
3368
|
+
const writerNamesSelectedAsset = writeNamesAsset();
|
|
3255
3369
|
const blockedReason = firstFalseFact({
|
|
3256
3370
|
signerReady: signerStatus === "ready",
|
|
3257
3371
|
...runAuthorityFacts(draft, budgets, selectedAsset, assetMoney, funds),
|
|
3258
3372
|
recipientCount: payableRefs === null ? 0 : selected.length,
|
|
3259
|
-
amountsParse: total !== null
|
|
3373
|
+
amountsParse: total !== null && hasRequiredCompensation(frozen, writerNamesSelectedAsset, selected, compensationTerms),
|
|
3374
|
+
writeNamesAsset: writerNamesSelectedAsset
|
|
3260
3375
|
});
|
|
3261
3376
|
const authorize = useMutation({
|
|
3262
3377
|
mutationFn: async ({ candidate, options }) => {
|
|
@@ -3278,7 +3393,7 @@ function usePayrollRun(input) {
|
|
|
3278
3393
|
if (candidate.scope === currentScope.current) restoreDraft(candidate.scope);
|
|
3279
3394
|
}
|
|
3280
3395
|
});
|
|
3281
|
-
const totalDisplay = total === null ? null :
|
|
3396
|
+
const totalDisplay = total === null ? null : formatAssetAmount(total);
|
|
3282
3397
|
const authorizeMutate = authorize.mutate;
|
|
3283
3398
|
const submit = useCallback((options) => {
|
|
3284
3399
|
const attempt = toInvocationOptions(options);
|
|
@@ -3287,41 +3402,35 @@ function usePayrollRun(input) {
|
|
|
3287
3402
|
if (permissionId === void 0 || totalDisplay === null || parsedAmounts === null) return;
|
|
3288
3403
|
if (payableRefs === null) return;
|
|
3289
3404
|
const recipientCount = selected.length;
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
const
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
kind: "party",
|
|
3299
|
-
partyId: toPartyId(recipientId)
|
|
3300
|
-
},
|
|
3301
|
-
partyId: recipientId,
|
|
3302
|
-
gross: raw,
|
|
3303
|
-
net: raw,
|
|
3304
|
-
adjustments: []
|
|
3405
|
+
let candidate;
|
|
3406
|
+
if (frozen) candidate = draft;
|
|
3407
|
+
else {
|
|
3408
|
+
const items = freshRunItems({
|
|
3409
|
+
selected,
|
|
3410
|
+
payableRefs,
|
|
3411
|
+
amounts: parsedAmounts,
|
|
3412
|
+
compensationTerms
|
|
3305
3413
|
});
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
decimals: assetMoney.decimals
|
|
3315
|
-
},
|
|
3316
|
-
input: {
|
|
3317
|
-
permissionId,
|
|
3318
|
-
period: {
|
|
3319
|
-
start: runAt,
|
|
3320
|
-
end: runAt
|
|
3414
|
+
const runAt = Date.now();
|
|
3415
|
+
candidate = {
|
|
3416
|
+
version: 1,
|
|
3417
|
+
scope: draftScope,
|
|
3418
|
+
asset: {
|
|
3419
|
+
id: selectedAsset.id,
|
|
3420
|
+
currency: requireAsset(assetMoney.asset).symbol,
|
|
3421
|
+
decimals: requireAsset(assetMoney.asset).decimals
|
|
3321
3422
|
},
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3423
|
+
input: {
|
|
3424
|
+
permissionId,
|
|
3425
|
+
asset: selectedAsset.id,
|
|
3426
|
+
period: {
|
|
3427
|
+
start: runAt,
|
|
3428
|
+
end: runAt
|
|
3429
|
+
},
|
|
3430
|
+
items
|
|
3431
|
+
}
|
|
3432
|
+
};
|
|
3433
|
+
}
|
|
3325
3434
|
submitGate.current = true;
|
|
3326
3435
|
authorizeMutate({
|
|
3327
3436
|
candidate,
|
|
@@ -3353,6 +3462,7 @@ function usePayrollRun(input) {
|
|
|
3353
3462
|
selectedAsset,
|
|
3354
3463
|
blockedReason,
|
|
3355
3464
|
budgets,
|
|
3465
|
+
compensationTerms,
|
|
3356
3466
|
onFailed,
|
|
3357
3467
|
onRun,
|
|
3358
3468
|
parsedAmounts,
|
|
@@ -3401,18 +3511,25 @@ function usePayrollRun(input) {
|
|
|
3401
3511
|
};
|
|
3402
3512
|
}
|
|
3403
3513
|
function remainingBalanceDisplay(available, total, asset) {
|
|
3404
|
-
|
|
3514
|
+
if (available === null || total === null || asset === null) return null;
|
|
3515
|
+
const remaining = available - total;
|
|
3516
|
+
const magnitude = remaining < 0n ? -remaining : remaining;
|
|
3517
|
+
const formatted = formatAssetAmount(assetFromMinorUnits(magnitude, {
|
|
3518
|
+
asset: asset.asset,
|
|
3519
|
+
decimals: requireAsset(asset.asset).decimals
|
|
3520
|
+
}));
|
|
3521
|
+
return remaining < 0n ? `-${formatted}` : formatted;
|
|
3405
3522
|
}
|
|
3406
3523
|
/** Persisted replay keeps its authorized Budget and asset; a prior debit cannot block its own recovery. */
|
|
3407
3524
|
function runAuthorityFacts(draft, budgets, asset, money, funds) {
|
|
3408
3525
|
return {
|
|
3409
3526
|
budgetCount: draft !== null && !budgets.some((budget) => budget.id === draft.input.permissionId) ? 0 : budgets.length,
|
|
3410
|
-
hasAsset: asset !== null && (draft === null || draft.asset.id === asset.id && draft.asset
|
|
3527
|
+
hasAsset: asset !== null && (draft === null || draft.asset.id === asset.id && draft.input.asset === money?.asset),
|
|
3411
3528
|
funds: draft?.input.requestKey === void 0 ? funds : "available"
|
|
3412
3529
|
};
|
|
3413
3530
|
}
|
|
3414
3531
|
/**
|
|
3415
|
-
* The
|
|
3532
|
+
* The facts, in the ONE order the contract fixes. Exactly one code comes
|
|
3416
3533
|
* out, and it is always the first thing that is false — so two problems never
|
|
3417
3534
|
* race to name the button.
|
|
3418
3535
|
*/
|
|
@@ -3423,7 +3540,8 @@ function firstFalseFact(facts) {
|
|
|
3423
3540
|
if (!facts.hasAsset) return "no-asset";
|
|
3424
3541
|
if (facts.recipientCount === 0) return "no-recipients";
|
|
3425
3542
|
if (!facts.amountsParse) return "amount-invalid";
|
|
3426
|
-
|
|
3543
|
+
if (facts.funds === "insufficient") return "insufficient-funds";
|
|
3544
|
+
return facts.writeNamesAsset ? null : "asset-unnamed";
|
|
3427
3545
|
}
|
|
3428
3546
|
//#endregion
|
|
3429
3547
|
//#region src/headless/payroll/payroll-run.tsx
|
|
@@ -3433,7 +3551,7 @@ function usePayrollRunContext() {
|
|
|
3433
3551
|
if (engine === null) throw new Error("CapxulPayrollRun parts must be used inside <CapxulPayrollRun>");
|
|
3434
3552
|
return engine;
|
|
3435
3553
|
}
|
|
3436
|
-
function Root({ orgId, prefill, onRun, onFailed, children }) {
|
|
3554
|
+
function Root$1({ orgId, prefill, onRun, onFailed, children }) {
|
|
3437
3555
|
const engine = usePayrollRun({
|
|
3438
3556
|
orgId,
|
|
3439
3557
|
prefill,
|
|
@@ -3463,7 +3581,7 @@ function Actions({ children }) {
|
|
|
3463
3581
|
return /* @__PURE__ */ jsx(Fragment, { children: children(engine.actions) });
|
|
3464
3582
|
}
|
|
3465
3583
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
3466
|
-
const CapxulPayrollRun = Object.assign(Root, {
|
|
3584
|
+
const CapxulPayrollRun = Object.assign(Root$1, {
|
|
3467
3585
|
Recipients,
|
|
3468
3586
|
Amounts,
|
|
3469
3587
|
Asset,
|
|
@@ -3471,4 +3589,230 @@ const CapxulPayrollRun = Object.assign(Root, {
|
|
|
3471
3589
|
Actions
|
|
3472
3590
|
});
|
|
3473
3591
|
//#endregion
|
|
3474
|
-
|
|
3592
|
+
//#region src/headless/client-connection/use-client-connection.ts
|
|
3593
|
+
function useClientConnection(input) {
|
|
3594
|
+
const [view, setView] = useState(null);
|
|
3595
|
+
const [pending, setPending] = useState(false);
|
|
3596
|
+
const [error, setError] = useState(null);
|
|
3597
|
+
const [resendReady, setResendReady] = useState(false);
|
|
3598
|
+
const identityRef = useRef({
|
|
3599
|
+
client: input.client,
|
|
3600
|
+
requestId: input.requestId
|
|
3601
|
+
});
|
|
3602
|
+
identityRef.current = {
|
|
3603
|
+
client: input.client,
|
|
3604
|
+
requestId: input.requestId
|
|
3605
|
+
};
|
|
3606
|
+
const belongsHere = useCallback((client, requestId) => identityRef.current.client === client && identityRef.current.requestId === requestId, []);
|
|
3607
|
+
const refresh = useCallback(async (signal) => {
|
|
3608
|
+
const { client, requestId } = identityRef.current;
|
|
3609
|
+
const result = await client.auth.clientRequests.read({ requestId }, signal === void 0 ? void 0 : { signal });
|
|
3610
|
+
if (signal?.aborted || !belongsHere(client, requestId)) return;
|
|
3611
|
+
if (result.ok) {
|
|
3612
|
+
setView(result.value);
|
|
3613
|
+
setError(null);
|
|
3614
|
+
return;
|
|
3615
|
+
}
|
|
3616
|
+
setError(result.error);
|
|
3617
|
+
}, [belongsHere]);
|
|
3618
|
+
useEffect(() => {
|
|
3619
|
+
setView(null);
|
|
3620
|
+
setError(null);
|
|
3621
|
+
setPending(false);
|
|
3622
|
+
setResendReady(false);
|
|
3623
|
+
const controller = new AbortController();
|
|
3624
|
+
refresh(controller.signal);
|
|
3625
|
+
return () => {
|
|
3626
|
+
controller.abort();
|
|
3627
|
+
};
|
|
3628
|
+
}, [
|
|
3629
|
+
input.client,
|
|
3630
|
+
input.requestId,
|
|
3631
|
+
refresh
|
|
3632
|
+
]);
|
|
3633
|
+
useEffect(() => {
|
|
3634
|
+
if (view?.phase !== "otp_required") {
|
|
3635
|
+
setResendReady(false);
|
|
3636
|
+
return;
|
|
3637
|
+
}
|
|
3638
|
+
const remaining = view.resendAt - Date.now();
|
|
3639
|
+
if (remaining <= 0) {
|
|
3640
|
+
setResendReady(true);
|
|
3641
|
+
return;
|
|
3642
|
+
}
|
|
3643
|
+
setResendReady(false);
|
|
3644
|
+
const timer = setTimeout(() => setResendReady(true), remaining);
|
|
3645
|
+
return () => clearTimeout(timer);
|
|
3646
|
+
}, [view]);
|
|
3647
|
+
const run = useCallback(async (work) => {
|
|
3648
|
+
const { client, requestId } = identityRef.current;
|
|
3649
|
+
setPending(true);
|
|
3650
|
+
try {
|
|
3651
|
+
const result = await work();
|
|
3652
|
+
if (!belongsHere(client, requestId)) return;
|
|
3653
|
+
if (!result.ok) {
|
|
3654
|
+
setError(result.error);
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3657
|
+
await refresh();
|
|
3658
|
+
} finally {
|
|
3659
|
+
if (belongsHere(client, requestId)) setPending(false);
|
|
3660
|
+
}
|
|
3661
|
+
}, [belongsHere, refresh]);
|
|
3662
|
+
return useMemo(() => {
|
|
3663
|
+
if (error !== null && (view === null || view.phase !== "refused")) return {
|
|
3664
|
+
phase: "failure",
|
|
3665
|
+
otp: null,
|
|
3666
|
+
consent: null,
|
|
3667
|
+
complete: null,
|
|
3668
|
+
failure: {
|
|
3669
|
+
code: error.code,
|
|
3670
|
+
retry: () => refresh(),
|
|
3671
|
+
cancel: async () => {
|
|
3672
|
+
if (view === null) return;
|
|
3673
|
+
const { client, requestId } = identityRef.current;
|
|
3674
|
+
await run(() => client.auth.clientRequests.cancel({
|
|
3675
|
+
requestId,
|
|
3676
|
+
revision: view.revision
|
|
3677
|
+
}));
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
};
|
|
3681
|
+
if (view === null) return {
|
|
3682
|
+
phase: "checking",
|
|
3683
|
+
otp: null,
|
|
3684
|
+
consent: null,
|
|
3685
|
+
complete: null,
|
|
3686
|
+
failure: null
|
|
3687
|
+
};
|
|
3688
|
+
if (view.phase === "otp_required") return {
|
|
3689
|
+
phase: view.phase,
|
|
3690
|
+
otp: {
|
|
3691
|
+
email: view.email,
|
|
3692
|
+
pending,
|
|
3693
|
+
canResend: resendReady,
|
|
3694
|
+
verifyCode: async (code) => {
|
|
3695
|
+
const { client, requestId } = identityRef.current;
|
|
3696
|
+
await run(() => client.auth.clientRequests.verifyOtp({
|
|
3697
|
+
requestId,
|
|
3698
|
+
code
|
|
3699
|
+
}));
|
|
3700
|
+
},
|
|
3701
|
+
resend: async () => {
|
|
3702
|
+
const { client, requestId } = identityRef.current;
|
|
3703
|
+
await run(() => client.auth.clientRequests.sendOtp({ requestId }));
|
|
3704
|
+
}
|
|
3705
|
+
},
|
|
3706
|
+
consent: null,
|
|
3707
|
+
complete: null,
|
|
3708
|
+
failure: null
|
|
3709
|
+
};
|
|
3710
|
+
if (view.phase === "review") return {
|
|
3711
|
+
phase: view.phase,
|
|
3712
|
+
otp: null,
|
|
3713
|
+
consent: {
|
|
3714
|
+
verifiedEmail: view.email,
|
|
3715
|
+
clientLabel: view.clientLabel,
|
|
3716
|
+
scopeCodes: view.scopes,
|
|
3717
|
+
purpose: view.intent.mode,
|
|
3718
|
+
setupState: view.setupState,
|
|
3719
|
+
pending,
|
|
3720
|
+
accept: async () => {
|
|
3721
|
+
const { client, requestId } = identityRef.current;
|
|
3722
|
+
await run(() => client.auth.clientRequests.accept({
|
|
3723
|
+
requestId,
|
|
3724
|
+
revision: view.revision
|
|
3725
|
+
}));
|
|
3726
|
+
},
|
|
3727
|
+
cancel: async () => {
|
|
3728
|
+
const { client, requestId } = identityRef.current;
|
|
3729
|
+
await run(() => client.auth.clientRequests.cancel({
|
|
3730
|
+
requestId,
|
|
3731
|
+
revision: view.revision
|
|
3732
|
+
}));
|
|
3733
|
+
}
|
|
3734
|
+
},
|
|
3735
|
+
complete: null,
|
|
3736
|
+
failure: null
|
|
3737
|
+
};
|
|
3738
|
+
if (view.phase === "connected") return {
|
|
3739
|
+
phase: view.phase,
|
|
3740
|
+
otp: null,
|
|
3741
|
+
consent: null,
|
|
3742
|
+
complete: { outcome: "connected" },
|
|
3743
|
+
failure: null
|
|
3744
|
+
};
|
|
3745
|
+
if (view.phase === "refused") return {
|
|
3746
|
+
phase: view.phase,
|
|
3747
|
+
otp: null,
|
|
3748
|
+
consent: null,
|
|
3749
|
+
complete: null,
|
|
3750
|
+
failure: {
|
|
3751
|
+
code: view.code,
|
|
3752
|
+
...view.field === void 0 ? {} : { field: view.field },
|
|
3753
|
+
retry: () => refresh(),
|
|
3754
|
+
cancel: async () => void 0
|
|
3755
|
+
}
|
|
3756
|
+
};
|
|
3757
|
+
return {
|
|
3758
|
+
phase: view.phase,
|
|
3759
|
+
otp: null,
|
|
3760
|
+
consent: null,
|
|
3761
|
+
complete: null,
|
|
3762
|
+
failure: null
|
|
3763
|
+
};
|
|
3764
|
+
}, [
|
|
3765
|
+
error,
|
|
3766
|
+
pending,
|
|
3767
|
+
refresh,
|
|
3768
|
+
resendReady,
|
|
3769
|
+
run,
|
|
3770
|
+
view
|
|
3771
|
+
]);
|
|
3772
|
+
}
|
|
3773
|
+
//#endregion
|
|
3774
|
+
//#region src/headless/client-connection/client-connection.tsx
|
|
3775
|
+
const ConnectionContext = createContext(null);
|
|
3776
|
+
function useConnection() {
|
|
3777
|
+
const engine = useContext(ConnectionContext);
|
|
3778
|
+
if (engine === null) throw new Error("CapxulClientConnection parts must be used inside <CapxulClientConnection>");
|
|
3779
|
+
return engine;
|
|
3780
|
+
}
|
|
3781
|
+
function Root({ client, requestId, children }) {
|
|
3782
|
+
const engine = useClientConnection({
|
|
3783
|
+
client,
|
|
3784
|
+
requestId
|
|
3785
|
+
});
|
|
3786
|
+
return /* @__PURE__ */ jsx(ConnectionContext.Provider, {
|
|
3787
|
+
value: engine,
|
|
3788
|
+
children
|
|
3789
|
+
});
|
|
3790
|
+
}
|
|
3791
|
+
function Checking({ children }) {
|
|
3792
|
+
return useConnection().phase === "checking" ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
|
|
3793
|
+
}
|
|
3794
|
+
function Otp({ children }) {
|
|
3795
|
+
const otp = useConnection().otp;
|
|
3796
|
+
return otp === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(otp) });
|
|
3797
|
+
}
|
|
3798
|
+
function Consent({ children }) {
|
|
3799
|
+
const consent = useConnection().consent;
|
|
3800
|
+
return consent === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(consent) });
|
|
3801
|
+
}
|
|
3802
|
+
function Complete({ children }) {
|
|
3803
|
+
const complete = useConnection().complete;
|
|
3804
|
+
return complete === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(complete) });
|
|
3805
|
+
}
|
|
3806
|
+
function Failure({ children }) {
|
|
3807
|
+
const failure = useConnection().failure;
|
|
3808
|
+
return failure === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(failure) });
|
|
3809
|
+
}
|
|
3810
|
+
const CapxulClientConnection = Object.assign(Root, {
|
|
3811
|
+
Checking,
|
|
3812
|
+
Otp,
|
|
3813
|
+
Consent,
|
|
3814
|
+
Complete,
|
|
3815
|
+
Failure
|
|
3816
|
+
});
|
|
3817
|
+
//#endregion
|
|
3818
|
+
export { CapxulActivity, CapxulAuthenticationController, CapxulClientConnection, CapxulContacts, CapxulDashboardAccess, CapxulOnboardingController, CapxulOrgMember, CapxulPayroll, CapxulPayrollRun, CapxulProvider, CapxulSendMoney, acknowledgeOnboardingDestination, activeOnboardingRecovery, capxulKeys, clearOnboardingJourney, currentOnboardingJourneyId, entered, invalidateOnboardingJourneyObservation, isClaimed, isRestoring, loadOnboardingJourney, onboardingJourneyPosition, saveOnboardingJourney, startOnboardingJourney, useCapxul, useCapxulAccountBalance, useCapxulAccountFund, useCapxulActivity, useCapxulActivityDetail, useCapxulAnnotateMovement, useCapxulAuth, useCapxulCancelPayment, useCapxulClaimPayment, useCapxulClientOrNull, useCapxulCreateCommitment, useCapxulCreateOrg, useCapxulDestination, useCapxulHandleAvailability, useCapxulHoldings, useCapxulIdentity, useCapxulImageUpload, useCapxulInviteMember, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationPay, useCapxulOrganizationPayBatch, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPermission, useCapxulPermissionAssign, useCapxulPermissionChange, useCapxulPermissionCreate, useCapxulPermissionReplace, useCapxulPermissionRevoke, useCapxulPermissions, useCapxulProfile, useCapxulRedirectPayment, useCapxulSend, useCapxulTransitions, useCapxulUploadImage };
|