@capxul/sdk-react 2.4.0 → 2.5.1
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 +8 -0
- package/dist/{controllers-DLBe4-OP.mjs → controllers-DpScp8c5.mjs} +18 -2
- package/dist/index.d.mts +246 -20
- package/dist/index.mjs +607 -25
- package/dist/testing/index.mjs +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -61,6 +61,14 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
61
61
|
actor paths ship: a personal actor reads the account book, and an
|
|
62
62
|
organization actor reads the organization book. The `managePeople` capability
|
|
63
63
|
gate stays in the app, on `CapxulOrgMember.Can`.
|
|
64
|
+
- `CapxulPayroll` and `CapxulPayrollRun` are the two payroll compound
|
|
65
|
+
components. `CapxulPayroll` owns the payroll page's reads through its
|
|
66
|
+
`.Summary` and `.Groups` region parts; `CapxulPayrollRun` owns the run
|
|
67
|
+
composer through `.Recipients`, `.Amounts`, `.Asset`, `.Summary`, and
|
|
68
|
+
`.Actions`. A screen places any subset — the pay-now sheet places all five
|
|
69
|
+
run parts, the group sheet places two. `.Actions.blockedReason` names the
|
|
70
|
+
first false fact as a `CapxulPayrollRunBlockedReason` code, never a
|
|
71
|
+
sentence. Both engine hooks stay module-scoped.
|
|
64
72
|
|
|
65
73
|
The packed npm package exposes `@capxul/sdk-react` and
|
|
66
74
|
`@capxul/sdk-react/testing`. The workspace-only `@capxul/sdk-react/headless`
|
|
@@ -103,6 +103,20 @@ const capxulKeys = {
|
|
|
103
103
|
scope,
|
|
104
104
|
includeHidden ? "all" : "visible"
|
|
105
105
|
],
|
|
106
|
+
payrollRuns: (orgId) => [
|
|
107
|
+
"capxul",
|
|
108
|
+
"org",
|
|
109
|
+
orgId ?? "pending",
|
|
110
|
+
"payroll",
|
|
111
|
+
"runs"
|
|
112
|
+
],
|
|
113
|
+
payrollGroups: (orgId) => [
|
|
114
|
+
"capxul",
|
|
115
|
+
"org",
|
|
116
|
+
orgId ?? "pending",
|
|
117
|
+
"payroll",
|
|
118
|
+
"groups"
|
|
119
|
+
],
|
|
106
120
|
activity: ["capxul", "activity"],
|
|
107
121
|
activityDetail: (reference, actor = void 0) => [
|
|
108
122
|
"capxul",
|
|
@@ -502,7 +516,7 @@ function clearClientScopedQueries(queryClient, ownsQueryClient) {
|
|
|
502
516
|
queryClient.removeQueries({ predicate: (query) => isCapxulQueryKey(query.queryKey) });
|
|
503
517
|
}
|
|
504
518
|
function CapxulProvider(props) {
|
|
505
|
-
const { publishableKey, client: injectedClient, requirement, signer, observability, queryClient, children } = props;
|
|
519
|
+
const { publishableKey, client: injectedClient, requirement, signer, observability, bootstrapBaseUrl, queryClient, children } = props;
|
|
506
520
|
const [resolvedQueryClient] = useState(() => queryClient ?? makeDefaultQueryClient());
|
|
507
521
|
const [ownsQueryClient] = useState(() => queryClient === void 0);
|
|
508
522
|
const [attempt, setAttempt] = useState(0);
|
|
@@ -513,12 +527,14 @@ function CapxulProvider(props) {
|
|
|
513
527
|
publishableKey,
|
|
514
528
|
...requirement === void 0 ? {} : { requirement },
|
|
515
529
|
...signer === void 0 ? {} : { signer },
|
|
516
|
-
...observability === void 0 ? {} : { observability }
|
|
530
|
+
...observability === void 0 ? {} : { observability },
|
|
531
|
+
...bootstrapBaseUrl === void 0 ? {} : { bootstrapBaseUrl }
|
|
517
532
|
}, [
|
|
518
533
|
publishableKey,
|
|
519
534
|
requirement,
|
|
520
535
|
signer,
|
|
521
536
|
observability,
|
|
537
|
+
bootstrapBaseUrl,
|
|
522
538
|
attempt
|
|
523
539
|
]);
|
|
524
540
|
const [ownedBootstrap, setOwnedBootstrap] = useState(null);
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { A as useCapxulAuth, C as CreateOrganizationSubmission, D as ProfileDetails, E as OrganizationDetails, M as useCapxulIdentity, N as useCapxulSend, O as SendResult, P as useCapxulTransitions, S as CapxulSend, T as InvocationOptions, _ as ReadyDestination, a as AuthenticationSlots, b as Slot, c as ControllerAction, d as OnboardingControllerProps, f as OrgFailure, g as ProfileSlotProps, h as PendingAuthState, i as AuthenticatedState, j as useCapxulDestination, k as entered, l as FaultedState, m as OtpPendingState, n as AccountProgress, o as CapxulAuthenticationController, p as OrgProgress, r as ActionResult, s as CapxulOnboardingController, t as AccountFailure, u as NavigationAction, v as RetryAction, w as Destination, x as CapxulAuth, y as SignedOutState } from "./controllers-BT4YzBr9.mjs";
|
|
2
2
|
import { ReactNode, RefCallback } from "react";
|
|
3
3
|
import { QueryClient, UseMutationResult, UseQueryResult } from "@tanstack/react-query";
|
|
4
|
-
import { Account, AccountRequirement, ActivityAnnotation, ActivityAnnotationInput, ActivityDetail, ActivityKind, ActivityListParams, ActivityPage, ActivityPhase, ActivityRange, ActivityReference, ActorReference, AddressBookEntry, CapxulClient, CapxulError, CapxulSigner, CreateOrgInput, CurrentHoldings, HostObservability, IdentityDestination, InviteMemberInput, MemberView, Money, MoneyParseErrorReason, OrgId, OrgView, OrganizationPaymentBatchInput, OrganizationPaymentInput, PartyId, Payment, PaymentDirection, PaymentDocumentRef, PaymentDocumentRender, PaymentStatus, PaymentTiming, PaymentType, PaymentsPayInput, Permission, PermissionReadResult, Profile, RoleView, isClaimed, isRestoring } from "@capxul/sdk";
|
|
4
|
+
import { Account, AccountRequirement, ActivityAnnotation, ActivityAnnotationInput, ActivityDetail, ActivityKind, ActivityListParams, ActivityPage, ActivityPhase, ActivityRange, ActivityReference, ActorReference, AddressBookEntry, CapxulClient, CapxulError, CapxulResult, CapxulSigner, CreateOrgInput, CurrentHoldings, HostObservability, IdentityDestination, InviteMemberInput, MemberView, Money, MoneyParseErrorReason, OrgId, OrgView, OrganizationPaymentBatchInput, OrganizationPaymentInput, PartyId, Payment, PaymentDirection, PaymentDocumentRef, PaymentDocumentRender, PaymentStatus, PaymentTiming, PaymentType, PaymentsPayInput, PayrollGroupId, PayrollGroupInput, PayrollRunId, PayrollRunStatus, Permission, PermissionReadResult, Profile, RoleView, isClaimed, isRestoring } from "@capxul/sdk";
|
|
5
5
|
|
|
6
6
|
//#region src/provider.d.ts
|
|
7
7
|
type CapxulProviderSharedProps = {
|
|
@@ -19,6 +19,13 @@ type CapxulProviderPublishableKeyProps = CapxulProviderSharedProps & {
|
|
|
19
19
|
* with `requirement: "deployed"` — the SDK wires Openfort from bootstrap.
|
|
20
20
|
*/
|
|
21
21
|
readonly signer?: CapxulSigner;
|
|
22
|
+
/**
|
|
23
|
+
* Where the bootstrap call goes. Default: the page's own origin, which a
|
|
24
|
+
* host app proxies to Capxul. Set it to the Capxul Site URL to call the
|
|
25
|
+
* backend directly and skip the host's proxy hop; the backend answers the
|
|
26
|
+
* route cross-origin for the publishable key's allowed origins.
|
|
27
|
+
*/
|
|
28
|
+
readonly bootstrapBaseUrl?: string;
|
|
22
29
|
};
|
|
23
30
|
/**
|
|
24
31
|
* Node / server / test path — a pre-built client is supplied; lifecycle stays
|
|
@@ -30,6 +37,7 @@ type CapxulProviderInjectedClientProps = CapxulProviderSharedProps & {
|
|
|
30
37
|
readonly observability?: never;
|
|
31
38
|
readonly requirement?: never;
|
|
32
39
|
readonly signer?: never;
|
|
40
|
+
readonly bootstrapBaseUrl?: never;
|
|
33
41
|
};
|
|
34
42
|
type CapxulProviderProps = CapxulProviderPublishableKeyProps | CapxulProviderInjectedClientProps;
|
|
35
43
|
declare function CapxulProvider(props: CapxulProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -113,9 +121,32 @@ type PermissionId = Brand<string, "PermissionId">;
|
|
|
113
121
|
type PermissionAssignmentId = Brand<string, "PermissionAssignmentId">;
|
|
114
122
|
type PaymentCommandId = Brand<string, "PaymentCommandId">;
|
|
115
123
|
type OrgId$1 = Brand<string, "OrgId">;
|
|
124
|
+
type CurrencyCode = Brand<SupportedCurrencyCode, "CurrencyCode">;
|
|
116
125
|
type WeiAmount = Brand<string, "WeiAmount">;
|
|
117
126
|
type RoleKey = Brand<string, "RoleKey">;
|
|
118
127
|
type AllowanceKey = Brand<string, "AllowanceKey">;
|
|
128
|
+
declare const SUPPORTED_CURRENCIES: readonly [{
|
|
129
|
+
readonly code: "USD";
|
|
130
|
+
readonly symbol: "$";
|
|
131
|
+
readonly name: "US Dollar";
|
|
132
|
+
}, {
|
|
133
|
+
readonly code: "NGN";
|
|
134
|
+
readonly symbol: "NGN";
|
|
135
|
+
readonly name: "Nigerian Naira";
|
|
136
|
+
}, {
|
|
137
|
+
readonly code: "GHS";
|
|
138
|
+
readonly symbol: "GHS";
|
|
139
|
+
readonly name: "Ghanaian Cedi";
|
|
140
|
+
}, {
|
|
141
|
+
readonly code: "KES";
|
|
142
|
+
readonly symbol: "KSh";
|
|
143
|
+
readonly name: "Kenyan Shilling";
|
|
144
|
+
}, {
|
|
145
|
+
readonly code: "UGX";
|
|
146
|
+
readonly symbol: "USh";
|
|
147
|
+
readonly name: "Ugandan Shilling";
|
|
148
|
+
}];
|
|
149
|
+
type SupportedCurrencyCode = (typeof SUPPORTED_CURRENCIES)[number]["code"];
|
|
119
150
|
//#endregion
|
|
120
151
|
//#region src/hooks/use-capxul-permissions.d.ts
|
|
121
152
|
declare const useCapxulPermissionCreate: (orgId: OrgId) => UseMutationResult<{
|
|
@@ -604,13 +635,13 @@ interface CapxulContactsProps {
|
|
|
604
635
|
readonly onFailed: (error: CapxulError) => void;
|
|
605
636
|
readonly children: ReactNode;
|
|
606
637
|
}
|
|
607
|
-
declare function Root$
|
|
638
|
+
declare function Root$6({
|
|
608
639
|
actor,
|
|
609
640
|
onAdded,
|
|
610
641
|
onFailed,
|
|
611
642
|
children
|
|
612
643
|
}: CapxulContactsProps): import("react/jsx-runtime").JSX.Element;
|
|
613
|
-
declare function Summary$
|
|
644
|
+
declare function Summary$3({
|
|
614
645
|
children
|
|
615
646
|
}: {
|
|
616
647
|
readonly children: (slice: ContactsSummarySlice) => ReactNode;
|
|
@@ -628,8 +659,8 @@ declare function Add({
|
|
|
628
659
|
readonly children: (slice: ContactsAddSlice) => ReactNode;
|
|
629
660
|
}): import("react/jsx-runtime").JSX.Element;
|
|
630
661
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
631
|
-
declare const CapxulContacts: typeof Root$
|
|
632
|
-
Summary: typeof Summary$
|
|
662
|
+
declare const CapxulContacts: typeof Root$6 & {
|
|
663
|
+
Summary: typeof Summary$3;
|
|
633
664
|
List: typeof List;
|
|
634
665
|
Add: typeof Add;
|
|
635
666
|
};
|
|
@@ -653,6 +684,7 @@ type ActivityRow = {
|
|
|
653
684
|
readonly counterpartyLabel: string;
|
|
654
685
|
readonly amountDisplay: string;
|
|
655
686
|
readonly signedAmountDisplay: string;
|
|
687
|
+
readonly currency: string;
|
|
656
688
|
readonly createdAt: number;
|
|
657
689
|
readonly ledgerKind: "Payment" | "Payout" | "Deposit";
|
|
658
690
|
readonly paymentType: PaymentType;
|
|
@@ -666,6 +698,7 @@ type ActivityRow = {
|
|
|
666
698
|
readonly counterpartyLabel: string;
|
|
667
699
|
readonly amountDisplay: string;
|
|
668
700
|
readonly signedAmountDisplay: string;
|
|
701
|
+
readonly currency: string;
|
|
669
702
|
readonly createdAt: number;
|
|
670
703
|
readonly classification: "movement_only_outflow";
|
|
671
704
|
readonly phase: ActivityPhase;
|
|
@@ -777,7 +810,7 @@ interface CapxulActivityProps {
|
|
|
777
810
|
readonly pageSize?: number;
|
|
778
811
|
readonly children: ReactNode;
|
|
779
812
|
}
|
|
780
|
-
declare function Root$
|
|
813
|
+
declare function Root$5({
|
|
781
814
|
actor,
|
|
782
815
|
pageSize,
|
|
783
816
|
children
|
|
@@ -788,7 +821,7 @@ declare function Root$3({
|
|
|
788
821
|
* pre-fetched result cannot serve both. It still holds no logic and draws
|
|
789
822
|
* nothing.
|
|
790
823
|
*/
|
|
791
|
-
declare function Summary({
|
|
824
|
+
declare function Summary$2({
|
|
792
825
|
window,
|
|
793
826
|
children
|
|
794
827
|
}: {
|
|
@@ -828,8 +861,8 @@ declare function Export({
|
|
|
828
861
|
readonly children: (slice: ActivityExportSlice) => ReactNode;
|
|
829
862
|
}): import("react/jsx-runtime").JSX.Element;
|
|
830
863
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
831
|
-
declare const CapxulActivity: typeof Root$
|
|
832
|
-
Summary: typeof Summary;
|
|
864
|
+
declare const CapxulActivity: typeof Root$5 & {
|
|
865
|
+
Summary: typeof Summary$2;
|
|
833
866
|
Filters: typeof Filters;
|
|
834
867
|
Search: typeof Search;
|
|
835
868
|
Rows: typeof Rows;
|
|
@@ -903,13 +936,13 @@ interface CapxulSendMoneyProps {
|
|
|
903
936
|
readonly onFailed: (error: CapxulError) => void;
|
|
904
937
|
readonly children: ReactNode;
|
|
905
938
|
}
|
|
906
|
-
declare function Root$
|
|
939
|
+
declare function Root$4({
|
|
907
940
|
actor,
|
|
908
941
|
onSent,
|
|
909
942
|
onFailed,
|
|
910
943
|
children
|
|
911
944
|
}: CapxulSendMoneyProps): import("react/jsx-runtime").JSX.Element;
|
|
912
|
-
declare function Asset({
|
|
945
|
+
declare function Asset$1({
|
|
913
946
|
children
|
|
914
947
|
}: {
|
|
915
948
|
readonly children: (slice: SendMoneyAssetSlice) => ReactNode;
|
|
@@ -924,17 +957,17 @@ declare function Recipient({
|
|
|
924
957
|
}: {
|
|
925
958
|
readonly children: (slice: SendMoneyRecipientSlice) => ReactNode;
|
|
926
959
|
}): import("react/jsx-runtime").JSX.Element;
|
|
927
|
-
declare function Actions({
|
|
960
|
+
declare function Actions$1({
|
|
928
961
|
children
|
|
929
962
|
}: {
|
|
930
963
|
readonly children: (slice: SendMoneyActionsSlice) => ReactNode;
|
|
931
964
|
}): import("react/jsx-runtime").JSX.Element;
|
|
932
965
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
933
|
-
declare const CapxulSendMoney: typeof Root$
|
|
934
|
-
Asset: typeof Asset;
|
|
966
|
+
declare const CapxulSendMoney: typeof Root$4 & {
|
|
967
|
+
Asset: typeof Asset$1;
|
|
935
968
|
Amount: typeof Amount;
|
|
936
969
|
Recipient: typeof Recipient;
|
|
937
|
-
Actions: typeof Actions;
|
|
970
|
+
Actions: typeof Actions$1;
|
|
938
971
|
};
|
|
939
972
|
//#endregion
|
|
940
973
|
//#region src/headless/access/org-member.d.ts
|
|
@@ -961,7 +994,7 @@ interface CapxulOrgMemberProps {
|
|
|
961
994
|
readonly orgId: OrgId | string | undefined;
|
|
962
995
|
readonly children: ReactNode;
|
|
963
996
|
}
|
|
964
|
-
declare function Root$
|
|
997
|
+
declare function Root$3({
|
|
965
998
|
orgId,
|
|
966
999
|
children
|
|
967
1000
|
}: CapxulOrgMemberProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -984,7 +1017,7 @@ declare function Standing({
|
|
|
984
1017
|
readonly children: (slice: OrgMemberStandingSlice) => ReactNode;
|
|
985
1018
|
}): import("react/jsx-runtime").JSX.Element;
|
|
986
1019
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
987
|
-
declare const CapxulOrgMember: typeof Root$
|
|
1020
|
+
declare const CapxulOrgMember: typeof Root$3 & {
|
|
988
1021
|
Can: typeof Can;
|
|
989
1022
|
Standing: typeof Standing;
|
|
990
1023
|
};
|
|
@@ -1009,7 +1042,7 @@ interface CapxulDashboardAccessProps {
|
|
|
1009
1042
|
readonly scope: DashboardScope;
|
|
1010
1043
|
readonly children: ReactNode;
|
|
1011
1044
|
}
|
|
1012
|
-
declare function Root({
|
|
1045
|
+
declare function Root$2({
|
|
1013
1046
|
scope,
|
|
1014
1047
|
children
|
|
1015
1048
|
}: CapxulDashboardAccessProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1035,13 +1068,204 @@ declare function Granted({
|
|
|
1035
1068
|
readonly children?: ReactNode;
|
|
1036
1069
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
1037
1070
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
1038
|
-
declare const CapxulDashboardAccess: typeof Root & {
|
|
1071
|
+
declare const CapxulDashboardAccess: typeof Root$2 & {
|
|
1039
1072
|
Checking: typeof Checking;
|
|
1040
1073
|
Error: typeof DashboardError;
|
|
1041
1074
|
Redirect: typeof Redirect;
|
|
1042
1075
|
Granted: typeof Granted;
|
|
1043
1076
|
};
|
|
1044
1077
|
//#endregion
|
|
1078
|
+
//#region src/headless/payroll/use-payroll.d.ts
|
|
1079
|
+
interface PayrollGroupRow {
|
|
1080
|
+
readonly id: PayrollGroupId;
|
|
1081
|
+
readonly name: string;
|
|
1082
|
+
/** A stored display preference; the app narrows it to its own DS union. */
|
|
1083
|
+
readonly tone: string;
|
|
1084
|
+
readonly memberCount: number;
|
|
1085
|
+
/** `formatMoney` over the amounts the employer typed on this group. */
|
|
1086
|
+
readonly totalDisplay: string;
|
|
1087
|
+
readonly members: readonly {
|
|
1088
|
+
/** The `partyId` — the stable id (ruling Q1), never an email. */readonly recipientId: string;
|
|
1089
|
+
readonly amount: string;
|
|
1090
|
+
readonly currency: CurrencyCode;
|
|
1091
|
+
}[];
|
|
1092
|
+
}
|
|
1093
|
+
interface PayrollSummarySlice {
|
|
1094
|
+
readonly walletBalanceDisplay: string | null;
|
|
1095
|
+
readonly paidThisMonthDisplay: string | null;
|
|
1096
|
+
readonly isLoading: boolean;
|
|
1097
|
+
readonly error: CapxulError | null;
|
|
1098
|
+
}
|
|
1099
|
+
interface PayrollGroupsSlice {
|
|
1100
|
+
readonly groups: readonly PayrollGroupRow[];
|
|
1101
|
+
readonly save: (input: PayrollGroupInput) => Promise<CapxulResult<PayrollGroupRow>>;
|
|
1102
|
+
readonly remove: (id: PayrollGroupId) => Promise<CapxulResult<void>>;
|
|
1103
|
+
readonly isSaving: boolean;
|
|
1104
|
+
readonly isLoading: boolean;
|
|
1105
|
+
readonly error: CapxulError | null;
|
|
1106
|
+
}
|
|
1107
|
+
//#endregion
|
|
1108
|
+
//#region src/headless/payroll/payroll.d.ts
|
|
1109
|
+
interface CapxulPayrollProps {
|
|
1110
|
+
readonly orgId: OrgId | undefined;
|
|
1111
|
+
readonly children: ReactNode;
|
|
1112
|
+
}
|
|
1113
|
+
declare function Root$1({
|
|
1114
|
+
orgId,
|
|
1115
|
+
children
|
|
1116
|
+
}: CapxulPayrollProps): import("react/jsx-runtime").JSX.Element;
|
|
1117
|
+
declare function Summary$1({
|
|
1118
|
+
children
|
|
1119
|
+
}: {
|
|
1120
|
+
readonly children: (slice: PayrollSummarySlice) => ReactNode;
|
|
1121
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
1122
|
+
declare function Groups({
|
|
1123
|
+
children
|
|
1124
|
+
}: {
|
|
1125
|
+
readonly children: (slice: PayrollGroupsSlice) => ReactNode;
|
|
1126
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
1127
|
+
/**
|
|
1128
|
+
* The compound shape the app's own design system already uses (`StatusTabs`).
|
|
1129
|
+
* There is NO `.Runs` part (ruling Q6): no shipped region has the right shape
|
|
1130
|
+
* for a run list, and a part without a production caller may not ship.
|
|
1131
|
+
*/
|
|
1132
|
+
declare const CapxulPayroll: typeof Root$1 & {
|
|
1133
|
+
Summary: typeof Summary$1;
|
|
1134
|
+
Groups: typeof Groups;
|
|
1135
|
+
};
|
|
1136
|
+
//#endregion
|
|
1137
|
+
//#region src/headless/payroll/reads.d.ts
|
|
1138
|
+
/**
|
|
1139
|
+
* One payable person, as the run composer and the group sheet see them.
|
|
1140
|
+
*
|
|
1141
|
+
* `id` is the `PartyId` — the stable key ADR-0022 R1 exists to provide, and the
|
|
1142
|
+
* end of the email-keyed recipient ids the deleted app hook minted.
|
|
1143
|
+
*/
|
|
1144
|
+
interface PayrollRecipientOption {
|
|
1145
|
+
readonly id: string;
|
|
1146
|
+
/** The party's real display name, never an email local-part. */
|
|
1147
|
+
readonly name: string;
|
|
1148
|
+
readonly email: string | null;
|
|
1149
|
+
}
|
|
1150
|
+
//#endregion
|
|
1151
|
+
//#region src/headless/payroll/use-payroll-run.d.ts
|
|
1152
|
+
/**
|
|
1153
|
+
* Refusal CODES, never sentences (ADR-0023 R1). `blockedReason` names the FIRST
|
|
1154
|
+
* false fact in the fixed order of the contract's seven-row table, so the same
|
|
1155
|
+
* composer state always yields the same code.
|
|
1156
|
+
*/
|
|
1157
|
+
type CapxulPayrollRunBlockedReason = "signer-not-ready" | "no-source" | "ambiguous-source" | "no-asset" | "no-recipients" | "amount-invalid" | "insufficient-funds";
|
|
1158
|
+
interface PayrollRunGroupOption {
|
|
1159
|
+
readonly id: PayrollGroupId;
|
|
1160
|
+
readonly label: string;
|
|
1161
|
+
/** The member count as text — the app supplies any word beside it. */
|
|
1162
|
+
readonly badgeLabel: string;
|
|
1163
|
+
readonly recipientIds: readonly string[];
|
|
1164
|
+
}
|
|
1165
|
+
interface PayrollRunRecipientsSlice {
|
|
1166
|
+
readonly options: readonly PayrollRecipientOption[];
|
|
1167
|
+
readonly groups: readonly PayrollRunGroupOption[];
|
|
1168
|
+
readonly selected: readonly string[];
|
|
1169
|
+
readonly toggle: (id: string) => void;
|
|
1170
|
+
readonly remove: (id: string) => void;
|
|
1171
|
+
readonly isLoading: boolean;
|
|
1172
|
+
readonly error: CapxulError | null;
|
|
1173
|
+
}
|
|
1174
|
+
interface PayrollRunAmountEntry {
|
|
1175
|
+
readonly recipientId: string;
|
|
1176
|
+
readonly name: string;
|
|
1177
|
+
readonly value: string;
|
|
1178
|
+
readonly change: (text: string) => void;
|
|
1179
|
+
/** The `parseMoney` verdict as a closed code; the app maps it to copy. */
|
|
1180
|
+
readonly error: MoneyParseErrorReason | null;
|
|
1181
|
+
/** True while the value is one we seeded and the employer has not edited. */
|
|
1182
|
+
readonly prefilled: boolean;
|
|
1183
|
+
/** The treasury asset's currency — the one-option dropdown stays. */
|
|
1184
|
+
readonly currency: CurrencyCode;
|
|
1185
|
+
}
|
|
1186
|
+
interface PayrollRunAmountsSlice {
|
|
1187
|
+
readonly entries: readonly PayrollRunAmountEntry[];
|
|
1188
|
+
}
|
|
1189
|
+
/** The SAME slice shape as `CapxulSendMoney.Asset` — the cross-item rule. */
|
|
1190
|
+
type PayrollRunAssetSlice = SendMoneyAssetSlice;
|
|
1191
|
+
interface PayrollRunSummarySlice {
|
|
1192
|
+
readonly recipientCount: number;
|
|
1193
|
+
/** Null until every selected amount parses. */
|
|
1194
|
+
readonly totalDisplay: string | null;
|
|
1195
|
+
readonly balanceAfterDisplay: string | null;
|
|
1196
|
+
readonly funds: "available" | "insufficient" | "unknown";
|
|
1197
|
+
}
|
|
1198
|
+
interface PayrollRunActionsSlice {
|
|
1199
|
+
readonly submit: () => void;
|
|
1200
|
+
readonly isSubmitting: boolean;
|
|
1201
|
+
readonly blocked: boolean;
|
|
1202
|
+
readonly blockedReason: CapxulPayrollRunBlockedReason | null;
|
|
1203
|
+
}
|
|
1204
|
+
interface PayrollRunPrefill {
|
|
1205
|
+
/** `partyId`s — how a group card opens pay-now pre-filled. */
|
|
1206
|
+
readonly recipientIds: readonly string[];
|
|
1207
|
+
readonly amounts?: readonly {
|
|
1208
|
+
readonly recipientId: string;
|
|
1209
|
+
readonly amount: string;
|
|
1210
|
+
}[];
|
|
1211
|
+
}
|
|
1212
|
+
interface SentPayrollRun {
|
|
1213
|
+
readonly id: PayrollRunId;
|
|
1214
|
+
readonly totalDisplay: string;
|
|
1215
|
+
readonly recipientCount: number;
|
|
1216
|
+
}
|
|
1217
|
+
//#endregion
|
|
1218
|
+
//#region src/headless/payroll/payroll-run.d.ts
|
|
1219
|
+
interface CapxulPayrollRunProps {
|
|
1220
|
+
readonly orgId: OrgId | undefined;
|
|
1221
|
+
/** How a group card opens pay-now pre-filled — today's group-context merge. */
|
|
1222
|
+
readonly prefill?: PayrollRunPrefill;
|
|
1223
|
+
readonly onRun: (run: SentPayrollRun) => void;
|
|
1224
|
+
/** ADR-0023 R1: the app maps `error.code` to its own copy; no SDK sentence. */
|
|
1225
|
+
readonly onFailed: (error: CapxulError) => void;
|
|
1226
|
+
readonly children: ReactNode;
|
|
1227
|
+
}
|
|
1228
|
+
declare function Root({
|
|
1229
|
+
orgId,
|
|
1230
|
+
prefill,
|
|
1231
|
+
onRun,
|
|
1232
|
+
onFailed,
|
|
1233
|
+
children
|
|
1234
|
+
}: CapxulPayrollRunProps): import("react/jsx-runtime").JSX.Element;
|
|
1235
|
+
declare function Recipients({
|
|
1236
|
+
children
|
|
1237
|
+
}: {
|
|
1238
|
+
readonly children: (slice: PayrollRunRecipientsSlice) => ReactNode;
|
|
1239
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
1240
|
+
declare function Amounts({
|
|
1241
|
+
children
|
|
1242
|
+
}: {
|
|
1243
|
+
readonly children: (slice: PayrollRunAmountsSlice) => ReactNode;
|
|
1244
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
1245
|
+
declare function Asset({
|
|
1246
|
+
children
|
|
1247
|
+
}: {
|
|
1248
|
+
readonly children: (slice: PayrollRunAssetSlice) => ReactNode;
|
|
1249
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
1250
|
+
declare function Summary({
|
|
1251
|
+
children
|
|
1252
|
+
}: {
|
|
1253
|
+
readonly children: (slice: PayrollRunSummarySlice) => ReactNode;
|
|
1254
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
1255
|
+
declare function Actions({
|
|
1256
|
+
children
|
|
1257
|
+
}: {
|
|
1258
|
+
readonly children: (slice: PayrollRunActionsSlice) => ReactNode;
|
|
1259
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
1260
|
+
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
1261
|
+
declare const CapxulPayrollRun: typeof Root & {
|
|
1262
|
+
Recipients: typeof Recipients;
|
|
1263
|
+
Amounts: typeof Amounts;
|
|
1264
|
+
Asset: typeof Asset;
|
|
1265
|
+
Summary: typeof Summary;
|
|
1266
|
+
Actions: typeof Actions;
|
|
1267
|
+
};
|
|
1268
|
+
//#endregion
|
|
1045
1269
|
//#region src/internal/reactivity-keys.d.ts
|
|
1046
1270
|
declare const capxulKeys: {
|
|
1047
1271
|
root: readonly ["capxul"];
|
|
@@ -1061,6 +1285,8 @@ declare const capxulKeys: {
|
|
|
1061
1285
|
orgLifecycle: (orgId: OrgId | undefined) => readonly ["capxul", "org", "pending" | OrgId, "lifecycle"];
|
|
1062
1286
|
contacts: (scope: string) => readonly ["capxul", "contacts", string];
|
|
1063
1287
|
contactsList: (scope: string, includeHidden: boolean) => readonly ["capxul", "contacts", string, "all" | "visible"];
|
|
1288
|
+
payrollRuns: (orgId: OrgId | undefined) => readonly ["capxul", "org", "pending" | OrgId, "payroll", "runs"];
|
|
1289
|
+
payrollGroups: (orgId: OrgId | undefined) => readonly ["capxul", "org", "pending" | OrgId, "payroll", "groups"];
|
|
1064
1290
|
activity: readonly ["capxul", "activity"];
|
|
1065
1291
|
activityDetail: (reference: ActivityReference | undefined, actor?: ActorReference | undefined) => readonly ["capxul", "activity", "self" | ActorReference, "pending" | ActivityReference];
|
|
1066
1292
|
activitySummary: (actor?: ActorReference, window?: ActivityRange) => readonly ["capxul", "activity", "summary", "self" | ActorReference, "all" | ActivityRange];
|
|
@@ -1069,4 +1295,4 @@ declare const capxulKeys: {
|
|
|
1069
1295
|
payment: (paymentId: string | undefined) => readonly ["capxul", "payments", string];
|
|
1070
1296
|
};
|
|
1071
1297
|
//#endregion
|
|
1072
|
-
export { type AccountFailure, type AccountProgress, type ActionResult, type ActivityDetailSlice, type ActivityExportSlice, type ActivityFilterValues, type ActivityFiltersSlice, type ActivityLoadMoreSlice, type ActivityReceipt, type ActivityRow, type ActivityRowsSlice, type ActivitySearchSlice, type ActivitySummarySlice, type AuthenticatedState, type AuthenticationSlots, type CanReason, CapxulActivity, type CapxulActivityProps, type CapxulAuth, CapxulAuthenticationController, type CapxulBootstrapState, type CapxulBootstrapStatus, CapxulContacts, type CapxulContactsProps, CapxulDashboardAccess, type CapxulDashboardAccessProps, CapxulOnboardingController, CapxulOrgMember, type CapxulOrgMemberProps, CapxulProvider, type CapxulProviderProps, type CapxulSend, CapxulSendMoney, type CapxulSendMoneyBlockedReason, type CapxulSendMoneyProps, type Contact, type ContactRelationship, type ContactRow, type ContactsActor, type ContactsAddBlockedReason, type ContactsAddError, type ContactsAddSlice, type ContactsListOptions, type ContactsListSlice, type ContactsRefusal, type ContactsSummarySlice, type ControllerAction, type CreateOrganizationSubmission, type CsvExport, type DashboardAccessErrorSlice, type DashboardAccessPhase, type DashboardAccessRedirectSlice, type DashboardScope, type Destination, type FaultedState, type ImageUploadInput, type ImageUploadTarget, type InvocationOptions, type NavigationAction, type OnboardingControllerProps, type OnboardingIntent, type OnboardingJourney, type OnboardingJourneyPosition, type OnboardingOrigin, type OnboardingStep, type OrgFailure, type OrgMemberAction, type OrgMemberCanSlice, type OrgMemberStanding, type OrgMemberStandingSlice, type OrgProgress, type OrganizationDetails, type OrganizationDraft, type OtpPendingState, type PayoutDraftChain, type PayoutDraftEntry, type PendingAuthState, type ProfileDetails, type ProfileDraft, type ProfileSlotProps, type ReadyDestination, type RetryAction, type SendActor, type SendMoneyActionsSlice, type SendMoneyAmountSlice, type SendMoneyAssetError, type SendMoneyAssetOption, type SendMoneyAssetSlice, type SendMoneyRecipientMethod, type SendMoneyRecipientSlice, type SendMoneyRecipientStatus, type SendResult, type SentPayment, type SignedOutState, type Slot, type SummaryWindow, type UseCapxulAccountBalanceOptions, type UseCapxulAccountBalanceReturn, type UseCapxulAccountFundReturn, type UseCapxulCreateCommitmentReturn, type UseCapxulCreateOrgReturn, type UseCapxulImageUploadReturn, type UseCapxulInviteMemberReturn, type UseCapxulOrgMembersOptions, type UseCapxulOrgMembersReturn, type UseCapxulOrgRolesOptions, type UseCapxulOrgRolesReturn, type UseCapxulOrgTreasuryOptions, type UseCapxulOrgTreasuryReturn, type UseCapxulOrgsReturn, type UseCapxulPayReturn, type UseCapxulPaymentReturn, type UseCapxulPaymentsReturn, type UseCapxulProfileReturn, type UseCapxulRedirectPaymentReturn, type UseCapxulUsernameAvailabilityReturn, type UsernameAvailability, 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, useCapxulHoldings, useCapxulIdentity, useCapxulImageUpload, useCapxulInviteMember, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationPay, useCapxulOrganizationPayBatch, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPermission, useCapxulPermissionAssign, useCapxulPermissionChange, useCapxulPermissionCreate, useCapxulPermissionReplace, useCapxulPermissionRevoke, useCapxulPermissions, useCapxulProfile, useCapxulRedirectPayment, useCapxulSend, useCapxulTransitions, useCapxulUsernameAvailability };
|
|
1298
|
+
export { type AccountFailure, type AccountProgress, type ActionResult, type ActivityDetailSlice, type ActivityExportSlice, type ActivityFilterValues, type ActivityFiltersSlice, type ActivityLoadMoreSlice, type ActivityReceipt, type ActivityRow, type ActivityRowsSlice, type ActivitySearchSlice, type ActivitySummarySlice, type AuthenticatedState, type AuthenticationSlots, type CanReason, CapxulActivity, type CapxulActivityProps, type CapxulAuth, CapxulAuthenticationController, type CapxulBootstrapState, type CapxulBootstrapStatus, CapxulContacts, type CapxulContactsProps, CapxulDashboardAccess, type CapxulDashboardAccessProps, CapxulOnboardingController, CapxulOrgMember, type CapxulOrgMemberProps, CapxulPayroll, type CapxulPayrollProps, CapxulPayrollRun, type CapxulPayrollRunBlockedReason, type CapxulPayrollRunProps, CapxulProvider, type CapxulProviderProps, type CapxulSend, CapxulSendMoney, type CapxulSendMoneyBlockedReason, type CapxulSendMoneyProps, type Contact, type ContactRelationship, type ContactRow, type ContactsActor, type ContactsAddBlockedReason, type ContactsAddError, type ContactsAddSlice, type ContactsListOptions, type ContactsListSlice, type ContactsRefusal, type ContactsSummarySlice, type ControllerAction, type CreateOrganizationSubmission, type CsvExport, type DashboardAccessErrorSlice, type DashboardAccessPhase, type DashboardAccessRedirectSlice, type DashboardScope, type Destination, type FaultedState, type ImageUploadInput, type ImageUploadTarget, type InvocationOptions, type NavigationAction, type OnboardingControllerProps, type OnboardingIntent, type OnboardingJourney, type OnboardingJourneyPosition, type OnboardingOrigin, type OnboardingStep, type OrgFailure, type OrgMemberAction, type OrgMemberCanSlice, type OrgMemberStanding, type OrgMemberStandingSlice, type OrgProgress, type OrganizationDetails, type OrganizationDraft, type OtpPendingState, type PayoutDraftChain, type PayoutDraftEntry, type PayrollGroupRow, type PayrollGroupsSlice, type PayrollRecipientOption, type PayrollRunActionsSlice, type PayrollRunAmountEntry, type PayrollRunAmountsSlice, type PayrollRunAssetSlice, type PayrollRunGroupOption, type PayrollRunPrefill, type PayrollRunRecipientsSlice, type PayrollRunStatus, type PayrollRunSummarySlice, type PayrollSummarySlice, type PendingAuthState, type ProfileDetails, type ProfileDraft, type ProfileSlotProps, type ReadyDestination, type RetryAction, type SendActor, type SendMoneyActionsSlice, type SendMoneyAmountSlice, type SendMoneyAssetError, type SendMoneyAssetOption, type SendMoneyAssetSlice, type SendMoneyRecipientMethod, type SendMoneyRecipientSlice, type SendMoneyRecipientStatus, type SendResult, type SentPayment, type SentPayrollRun, type SignedOutState, type Slot, type SummaryWindow, type UseCapxulAccountBalanceOptions, type UseCapxulAccountBalanceReturn, type UseCapxulAccountFundReturn, type UseCapxulCreateCommitmentReturn, type UseCapxulCreateOrgReturn, type UseCapxulImageUploadReturn, type UseCapxulInviteMemberReturn, type UseCapxulOrgMembersOptions, type UseCapxulOrgMembersReturn, type UseCapxulOrgRolesOptions, type UseCapxulOrgRolesReturn, type UseCapxulOrgTreasuryOptions, type UseCapxulOrgTreasuryReturn, type UseCapxulOrgsReturn, type UseCapxulPayReturn, type UseCapxulPaymentReturn, type UseCapxulPaymentsReturn, type UseCapxulProfileReturn, type UseCapxulRedirectPaymentReturn, type UseCapxulUsernameAvailabilityReturn, type UsernameAvailability, 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, useCapxulHoldings, useCapxulIdentity, useCapxulImageUpload, useCapxulInviteMember, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationPay, useCapxulOrganizationPayBatch, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPermission, useCapxulPermissionAssign, useCapxulPermissionChange, useCapxulPermissionCreate, useCapxulPermissionReplace, useCapxulPermissionRevoke, useCapxulPermissions, useCapxulProfile, useCapxulRedirectPayment, useCapxulSend, useCapxulTransitions, useCapxulUsernameAvailability };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { a as useCapxulAuth, c as useCapxulIdentityOrNull, d as capxulKeys, f as useCapxulClientOrNull, i as entered, l as useCapxulSend, n as CapxulOnboardingController, o as useCapxulDestination, p as useCapxul, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-
|
|
2
|
+
import { a as useCapxulAuth, c as useCapxulIdentityOrNull, d as capxulKeys, f as useCapxulClientOrNull, i as entered, l as useCapxulSend, n as CapxulOnboardingController, o as useCapxulDestination, p as useCapxul, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-DpScp8c5.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
5
|
import { CapxulError, Errors, PAYMENT_DIRECTIONS, PAYMENT_STATUSES, fingerprintPaymentIntent, formatMoney, isCapxulError, isClaimed, isClaimed as isClaimed$1, isMoneyParseError, isRestoring, isRestoring as isRestoring$1, parseMoney, paymentPhase, resolveIdentityDestination } from "@capxul/sdk";
|
|
@@ -1014,7 +1014,7 @@ function useContactsContext() {
|
|
|
1014
1014
|
if (engine === null) throw new Error("CapxulContacts parts must be used inside <CapxulContacts>");
|
|
1015
1015
|
return engine;
|
|
1016
1016
|
}
|
|
1017
|
-
function Root$
|
|
1017
|
+
function Root$6({ actor, onAdded, onFailed, children }) {
|
|
1018
1018
|
const engine = useContacts({
|
|
1019
1019
|
actor,
|
|
1020
1020
|
onAdded,
|
|
@@ -1025,7 +1025,7 @@ function Root$4({ actor, onAdded, onFailed, children }) {
|
|
|
1025
1025
|
children
|
|
1026
1026
|
});
|
|
1027
1027
|
}
|
|
1028
|
-
function Summary$
|
|
1028
|
+
function Summary$3({ children }) {
|
|
1029
1029
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useContactsSummary(useContactsContext())) });
|
|
1030
1030
|
}
|
|
1031
1031
|
function List({ limit, show, children }) {
|
|
@@ -1038,8 +1038,8 @@ function Add({ children }) {
|
|
|
1038
1038
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useContactsContext().add) });
|
|
1039
1039
|
}
|
|
1040
1040
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
1041
|
-
const CapxulContacts = Object.assign(Root$
|
|
1042
|
-
Summary: Summary$
|
|
1041
|
+
const CapxulContacts = Object.assign(Root$6, {
|
|
1042
|
+
Summary: Summary$3,
|
|
1043
1043
|
List,
|
|
1044
1044
|
Add
|
|
1045
1045
|
});
|
|
@@ -1053,7 +1053,7 @@ const CapxulContacts = Object.assign(Root$4, {
|
|
|
1053
1053
|
* the app an error VALUE carrying a code and the app writes the sentence
|
|
1054
1054
|
* (ADR-0023).
|
|
1055
1055
|
*/
|
|
1056
|
-
function asCapxulError(error) {
|
|
1056
|
+
function asCapxulError$1(error) {
|
|
1057
1057
|
if (error === null) return null;
|
|
1058
1058
|
return isCapxulError(error) ? error : Errors.unknown(error);
|
|
1059
1059
|
}
|
|
@@ -1163,7 +1163,7 @@ function useActivityDetail(reference, actor) {
|
|
|
1163
1163
|
saveMemo,
|
|
1164
1164
|
isSavingMemo: annotate.isPending,
|
|
1165
1165
|
isLoading: enabled && detail.isLoading,
|
|
1166
|
-
error: asCapxulError(detail.error) ?? annotate.error ?? null
|
|
1166
|
+
error: asCapxulError$1(detail.error) ?? annotate.error ?? null
|
|
1167
1167
|
};
|
|
1168
1168
|
}
|
|
1169
1169
|
//#endregion
|
|
@@ -1299,7 +1299,7 @@ function useActivitySummary(actor, window) {
|
|
|
1299
1299
|
pending: null,
|
|
1300
1300
|
count: summary.data?.count ?? 0,
|
|
1301
1301
|
isLoading,
|
|
1302
|
-
error: asCapxulError(summary.error)
|
|
1302
|
+
error: asCapxulError$1(summary.error)
|
|
1303
1303
|
};
|
|
1304
1304
|
const display = (raw) => formatMoney({
|
|
1305
1305
|
currency: bucket.currency,
|
|
@@ -1314,7 +1314,7 @@ function useActivitySummary(actor, window) {
|
|
|
1314
1314
|
pending: display(bucket.pendingIn),
|
|
1315
1315
|
count: summary.data?.count ?? 0,
|
|
1316
1316
|
isLoading,
|
|
1317
|
-
error: asCapxulError(summary.error)
|
|
1317
|
+
error: asCapxulError$1(summary.error)
|
|
1318
1318
|
};
|
|
1319
1319
|
}
|
|
1320
1320
|
//#endregion
|
|
@@ -1349,6 +1349,7 @@ function toActivityRow(item, actor) {
|
|
|
1349
1349
|
counterpartyLabel: item.counterpartyLabel,
|
|
1350
1350
|
amountDisplay,
|
|
1351
1351
|
signedAmountDisplay,
|
|
1352
|
+
currency: item.amount.currency,
|
|
1352
1353
|
createdAt: item.createdAt,
|
|
1353
1354
|
classification: item.classification,
|
|
1354
1355
|
phase: MOVEMENT_PHASE,
|
|
@@ -1368,6 +1369,7 @@ function toActivityRow(item, actor) {
|
|
|
1368
1369
|
counterpartyLabel: item.counterpartyLabel,
|
|
1369
1370
|
amountDisplay,
|
|
1370
1371
|
signedAmountDisplay,
|
|
1372
|
+
currency: item.amount.currency,
|
|
1371
1373
|
createdAt: item.createdAt,
|
|
1372
1374
|
ledgerKind: item.ledgerKind,
|
|
1373
1375
|
paymentType: item.paymentType,
|
|
@@ -1620,7 +1622,7 @@ function useActivity(input) {
|
|
|
1620
1622
|
rows: {
|
|
1621
1623
|
rows,
|
|
1622
1624
|
isLoading: query.isLoading,
|
|
1623
|
-
error: asCapxulError(query.error),
|
|
1625
|
+
error: asCapxulError$1(query.error),
|
|
1624
1626
|
retry,
|
|
1625
1627
|
observedAt: pages === void 0 ? null : pages[pages.length - 1]?.observedAt ?? null
|
|
1626
1628
|
},
|
|
@@ -1644,7 +1646,7 @@ function useActivityContext() {
|
|
|
1644
1646
|
if (engine === null) throw new Error("CapxulActivity parts must be used inside <CapxulActivity>");
|
|
1645
1647
|
return engine;
|
|
1646
1648
|
}
|
|
1647
|
-
function Root$
|
|
1649
|
+
function Root$5({ actor, pageSize, children }) {
|
|
1648
1650
|
const engine = useActivity({
|
|
1649
1651
|
...actor === void 0 ? {} : { actor },
|
|
1650
1652
|
...pageSize === void 0 ? {} : { pageSize }
|
|
@@ -1660,7 +1662,7 @@ function Root$3({ actor, pageSize, children }) {
|
|
|
1660
1662
|
* pre-fetched result cannot serve both. It still holds no logic and draws
|
|
1661
1663
|
* nothing.
|
|
1662
1664
|
*/
|
|
1663
|
-
function Summary({ window = "all", children }) {
|
|
1665
|
+
function Summary$2({ window = "all", children }) {
|
|
1664
1666
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useActivitySummary(useActivityContext().actor, window)) });
|
|
1665
1667
|
}
|
|
1666
1668
|
function Filters({ children }) {
|
|
@@ -1694,8 +1696,8 @@ function Export({ children }) {
|
|
|
1694
1696
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useActivityContext().csv) });
|
|
1695
1697
|
}
|
|
1696
1698
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
1697
|
-
const CapxulActivity = Object.assign(Root$
|
|
1698
|
-
Summary,
|
|
1699
|
+
const CapxulActivity = Object.assign(Root$5, {
|
|
1700
|
+
Summary: Summary$2,
|
|
1699
1701
|
Filters,
|
|
1700
1702
|
Search,
|
|
1701
1703
|
Rows,
|
|
@@ -1866,7 +1868,7 @@ function useSendMoneyContext() {
|
|
|
1866
1868
|
if (engine === null) throw new Error("CapxulSendMoney parts must be used inside <CapxulSendMoney>");
|
|
1867
1869
|
return engine;
|
|
1868
1870
|
}
|
|
1869
|
-
function Root$
|
|
1871
|
+
function Root$4({ actor, onSent, onFailed, children }) {
|
|
1870
1872
|
const engine = useSendMoney({
|
|
1871
1873
|
actor,
|
|
1872
1874
|
onSent,
|
|
@@ -1877,7 +1879,7 @@ function Root$2({ actor, onSent, onFailed, children }) {
|
|
|
1877
1879
|
children
|
|
1878
1880
|
});
|
|
1879
1881
|
}
|
|
1880
|
-
function Asset({ children }) {
|
|
1882
|
+
function Asset$1({ children }) {
|
|
1881
1883
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useSendMoneyContext().asset) });
|
|
1882
1884
|
}
|
|
1883
1885
|
function Amount({ children }) {
|
|
@@ -1886,15 +1888,15 @@ function Amount({ children }) {
|
|
|
1886
1888
|
function Recipient({ children }) {
|
|
1887
1889
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useSendMoneyContext().recipient) });
|
|
1888
1890
|
}
|
|
1889
|
-
function Actions({ children }) {
|
|
1891
|
+
function Actions$1({ children }) {
|
|
1890
1892
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useSendMoneyContext().actions) });
|
|
1891
1893
|
}
|
|
1892
1894
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
1893
|
-
const CapxulSendMoney = Object.assign(Root$
|
|
1894
|
-
Asset,
|
|
1895
|
+
const CapxulSendMoney = Object.assign(Root$4, {
|
|
1896
|
+
Asset: Asset$1,
|
|
1895
1897
|
Amount,
|
|
1896
1898
|
Recipient,
|
|
1897
|
-
Actions
|
|
1899
|
+
Actions: Actions$1
|
|
1898
1900
|
});
|
|
1899
1901
|
//#endregion
|
|
1900
1902
|
//#region src/headless/access/org-member.tsx
|
|
@@ -2005,7 +2007,7 @@ function useOrgMemberContext() {
|
|
|
2005
2007
|
if (state === null) throw new Error("CapxulOrgMember parts must be used inside <CapxulOrgMember>");
|
|
2006
2008
|
return state;
|
|
2007
2009
|
}
|
|
2008
|
-
function Root$
|
|
2010
|
+
function Root$3({ orgId, children }) {
|
|
2009
2011
|
const state = useOrgMember(orgId);
|
|
2010
2012
|
return /* @__PURE__ */ jsx(OrgMemberContext.Provider, {
|
|
2011
2013
|
value: state,
|
|
@@ -2025,7 +2027,7 @@ function Standing({ children }) {
|
|
|
2025
2027
|
return /* @__PURE__ */ jsx(Fragment, { children: children(useOrgMemberContext().standing) });
|
|
2026
2028
|
}
|
|
2027
2029
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
2028
|
-
const CapxulOrgMember = Object.assign(Root$
|
|
2030
|
+
const CapxulOrgMember = Object.assign(Root$3, {
|
|
2029
2031
|
Can,
|
|
2030
2032
|
Standing
|
|
2031
2033
|
});
|
|
@@ -2172,7 +2174,7 @@ function usePart(phase) {
|
|
|
2172
2174
|
}, [placed, phase]);
|
|
2173
2175
|
return state.phase === phase ? state : null;
|
|
2174
2176
|
}
|
|
2175
|
-
function Root({ scope, children }) {
|
|
2177
|
+
function Root$2({ scope, children }) {
|
|
2176
2178
|
const state = useDashboardAccess(scope);
|
|
2177
2179
|
const [placed] = useState(() => /* @__PURE__ */ new Set());
|
|
2178
2180
|
const value = useMemo(() => ({
|
|
@@ -2210,11 +2212,591 @@ function Granted({ children }) {
|
|
|
2210
2212
|
return usePart("granted") === null ? null : /* @__PURE__ */ jsx(Fragment, { children });
|
|
2211
2213
|
}
|
|
2212
2214
|
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
2213
|
-
const CapxulDashboardAccess = Object.assign(Root, {
|
|
2215
|
+
const CapxulDashboardAccess = Object.assign(Root$2, {
|
|
2214
2216
|
Checking,
|
|
2215
2217
|
Error: DashboardError,
|
|
2216
2218
|
Redirect,
|
|
2217
2219
|
Granted
|
|
2218
2220
|
});
|
|
2219
2221
|
//#endregion
|
|
2220
|
-
|
|
2222
|
+
//#region src/headless/payroll/money.ts
|
|
2223
|
+
/** `{ value: "12.34", decimals: 6 }` → `12340000n`. */
|
|
2224
|
+
function toMinorUnits(money) {
|
|
2225
|
+
const [whole = "0", fraction = ""] = money.value.split(".");
|
|
2226
|
+
return BigInt(`${whole}${fraction.padEnd(money.decimals, "0")}`);
|
|
2227
|
+
}
|
|
2228
|
+
const TYPED_AMOUNT = /^(-?)(\d+)(?:\.(\d+))?$/;
|
|
2229
|
+
/**
|
|
2230
|
+
* Σ of the employer's TYPED amounts on a saved group — a DISPLAY total.
|
|
2231
|
+
*
|
|
2232
|
+
* A group is a draft, so its amounts were never required to parse (backend
|
|
2233
|
+
* `payroll/groups.ts` validates the name and the recipients, never the money).
|
|
2234
|
+
* What cannot be read is skipped rather than poisoning the card's total, and
|
|
2235
|
+
* the widest fraction any member typed sets the scale so nothing rounds away.
|
|
2236
|
+
*/
|
|
2237
|
+
function sumTypedAmounts(amounts, currency) {
|
|
2238
|
+
const parsed = amounts.map((amount) => TYPED_AMOUNT.exec(amount.trim().replaceAll(",", ""))).filter((match) => match !== null);
|
|
2239
|
+
const decimals = parsed.reduce((widest, match) => Math.max(widest, (match[3] ?? "").length), 0);
|
|
2240
|
+
return fromMinorUnits(parsed.reduce((sum, match) => {
|
|
2241
|
+
const scaled = BigInt(`${match[2] ?? "0"}${(match[3] ?? "").padEnd(decimals, "0")}`);
|
|
2242
|
+
return match[1] === "-" ? sum - scaled : sum + scaled;
|
|
2243
|
+
}, 0n), {
|
|
2244
|
+
currency,
|
|
2245
|
+
decimals
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
/** The inverse, so a summed total can be formatted by the shipped `formatMoney`. */
|
|
2249
|
+
function fromMinorUnits(raw, asset) {
|
|
2250
|
+
const negative = raw < 0n;
|
|
2251
|
+
const digits = (negative ? -raw : raw).toString().padStart(asset.decimals + 1, "0");
|
|
2252
|
+
const whole = digits.slice(0, digits.length - asset.decimals);
|
|
2253
|
+
const fraction = asset.decimals === 0 ? "" : `.${digits.slice(digits.length - asset.decimals)}`;
|
|
2254
|
+
return {
|
|
2255
|
+
currency: asset.currency,
|
|
2256
|
+
value: `${negative ? "-" : ""}${whole}${fraction}`,
|
|
2257
|
+
decimals: asset.decimals
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
//#endregion
|
|
2261
|
+
//#region src/headless/payroll/reads.ts
|
|
2262
|
+
function toRecipientOption(entry) {
|
|
2263
|
+
return {
|
|
2264
|
+
id: entry.id,
|
|
2265
|
+
name: entry.label,
|
|
2266
|
+
email: entry.label.includes("@") ? entry.label : null
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
/**
|
|
2270
|
+
* The roster: active `employed` edges ∪ org members, deduped by party
|
|
2271
|
+
* (ruling Q1 = C).
|
|
2272
|
+
*
|
|
2273
|
+
* The organization address book computes exactly that union server-side —
|
|
2274
|
+
* members accumulate from `orgMembers`, employment is PROJECTED from open
|
|
2275
|
+
* `employed` edges, and both land in ONE map keyed by party
|
|
2276
|
+
* (`financialOps/addressBook.ts`). Contact edges live in the same book and are
|
|
2277
|
+
* not the roster, so the two relationships the roster is made of are the
|
|
2278
|
+
* filter.
|
|
2279
|
+
*/
|
|
2280
|
+
function useOrgRoster(orgId) {
|
|
2281
|
+
const client = useCapxulClientOrNull();
|
|
2282
|
+
const query = useQuery({
|
|
2283
|
+
queryKey: capxulKeys.contactsList(orgId ?? "pending", false),
|
|
2284
|
+
queryFn: async () => unwrapCapxulResult(await scopedClient(client, "org.addressBook.list", orgId).addressBook.list({ includeHidden: false })),
|
|
2285
|
+
enabled: client !== null && orgId !== void 0
|
|
2286
|
+
});
|
|
2287
|
+
const entries = query.data;
|
|
2288
|
+
return useMemo(() => {
|
|
2289
|
+
const roster = (entries ?? []).filter((entry) => entry.relationship.includes("member") || entry.relationship.includes("employed"));
|
|
2290
|
+
return {
|
|
2291
|
+
options: roster.map(toRecipientOption),
|
|
2292
|
+
refs: new Map(roster.map((entry) => [entry.id, entry.ref])),
|
|
2293
|
+
isLoading: orgId !== void 0 && entries === void 0 && query.error === null,
|
|
2294
|
+
error: query.error
|
|
2295
|
+
};
|
|
2296
|
+
}, [
|
|
2297
|
+
entries,
|
|
2298
|
+
orgId,
|
|
2299
|
+
query.error
|
|
2300
|
+
]);
|
|
2301
|
+
}
|
|
2302
|
+
function usePayrollGroups(orgId) {
|
|
2303
|
+
const client = useCapxulClientOrNull();
|
|
2304
|
+
return useQuery({
|
|
2305
|
+
queryKey: capxulKeys.payrollGroups(orgId),
|
|
2306
|
+
queryFn: async () => unwrapCapxulResult(await scopedClient(client, "org.payroll.groups.list", orgId).payroll.groups.list()),
|
|
2307
|
+
enabled: client !== null && orgId !== void 0
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2310
|
+
function usePayrollRuns(orgId) {
|
|
2311
|
+
const client = useCapxulClientOrNull();
|
|
2312
|
+
return useQuery({
|
|
2313
|
+
queryKey: capxulKeys.payrollRuns(orgId),
|
|
2314
|
+
queryFn: async () => unwrapCapxulResult(await scopedClient(client, "org.payroll.runs", orgId).payroll.runs()),
|
|
2315
|
+
enabled: client !== null && orgId !== void 0
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
function useOrgTreasury(orgId) {
|
|
2319
|
+
const client = useCapxulClientOrNull();
|
|
2320
|
+
return useQuery({
|
|
2321
|
+
queryKey: capxulKeys.orgTreasury(orgId),
|
|
2322
|
+
queryFn: async () => unwrapCapxulResult(await scopedClient(client, "org.treasury", orgId).treasury()),
|
|
2323
|
+
enabled: client !== null && orgId !== void 0
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
/**
|
|
2327
|
+
* The caller's own standing, read for its Budgets alone (ruling Q2 grades
|
|
2328
|
+
* them). Keyed as `CapxulOrgMember` keys it, so a payroll screen inside a
|
|
2329
|
+
* `.Can` wrapper reads `org(id).me()` once.
|
|
2330
|
+
*/
|
|
2331
|
+
function useOrgMe(orgId) {
|
|
2332
|
+
const client = useCapxulClientOrNull();
|
|
2333
|
+
return useQuery({
|
|
2334
|
+
queryKey: capxulKeys.orgMe(orgId),
|
|
2335
|
+
queryFn: async () => unwrapCapxulResult(await scopedClient(client, "org.me", orgId).me()),
|
|
2336
|
+
enabled: client !== null && orgId !== void 0
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
/**
|
|
2340
|
+
* The org-scoped bundle inside a query or mutation function. Every caller gates
|
|
2341
|
+
* on `orgId !== undefined`, so the second throw is only reachable from a
|
|
2342
|
+
* mutation fired while the provider is still bootstrapping.
|
|
2343
|
+
*/
|
|
2344
|
+
function scopedClient(client, method, orgId) {
|
|
2345
|
+
const bootstrapped = requireBootstrappedClient(client, method);
|
|
2346
|
+
if (orgId === void 0) throw Errors.invalidInput("orgId", `required for ${method}`);
|
|
2347
|
+
return bootstrapped.org(orgId);
|
|
2348
|
+
}
|
|
2349
|
+
//#endregion
|
|
2350
|
+
//#region src/headless/payroll/use-payroll.ts
|
|
2351
|
+
/** Money reads the same way everywhere on this screen: amount then code. */
|
|
2352
|
+
const GRAMMAR$1 = { grammar: "code" };
|
|
2353
|
+
function toPayrollGroupRow(group) {
|
|
2354
|
+
const currency = new Set(group.members.map((member) => member.currency)).size === 1 ? group.members[0]?.currency : void 0;
|
|
2355
|
+
return {
|
|
2356
|
+
id: group.id,
|
|
2357
|
+
name: group.name,
|
|
2358
|
+
tone: group.tone,
|
|
2359
|
+
memberCount: group.members.length,
|
|
2360
|
+
totalDisplay: currency === void 0 ? "" : formatMoney(sumTypedAmounts(group.members.map((member) => member.amount), currency), GRAMMAR$1),
|
|
2361
|
+
members: group.members.map((member) => ({
|
|
2362
|
+
recipientId: member.partyId,
|
|
2363
|
+
amount: member.amount,
|
|
2364
|
+
currency: member.currency
|
|
2365
|
+
}))
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* The validity `buildOrganizationPayrollGroupRecord` applied in the browser
|
|
2370
|
+
* (payroll groups hook `:57-82`) — a trimmed name, at least one deduped
|
|
2371
|
+
* recipient — checked here so an invalid group never costs a round trip. The
|
|
2372
|
+
* backend re-checks it; this is honest UX, not the authority.
|
|
2373
|
+
*
|
|
2374
|
+
* ADR-0023 R1: the refusal is a CODE, never a toast sentence.
|
|
2375
|
+
*/
|
|
2376
|
+
function invalidGroup(input) {
|
|
2377
|
+
if (input.name.trim().length === 0) return Errors.invalidInput("name", "must not be empty");
|
|
2378
|
+
const deduped = new Set(input.members.map((member) => member.partyId));
|
|
2379
|
+
deduped.delete("");
|
|
2380
|
+
return deduped.size === 0 ? Errors.invalidInput("members", "must not be empty") : null;
|
|
2381
|
+
}
|
|
2382
|
+
function usePayroll(orgId) {
|
|
2383
|
+
const client = useCapxulClientOrNull();
|
|
2384
|
+
const queryClient = useQueryClient();
|
|
2385
|
+
const treasury = useOrgTreasury(orgId);
|
|
2386
|
+
const runs = usePayrollRuns(orgId);
|
|
2387
|
+
const groups = usePayrollGroups(orgId);
|
|
2388
|
+
const write = useMutation({
|
|
2389
|
+
mutationFn: async (command) => {
|
|
2390
|
+
const payroll = scopedClient(client, `org.payroll.groups.${command.kind}`, orgId).payroll;
|
|
2391
|
+
if (command.kind === "remove") {
|
|
2392
|
+
unwrapCapxulResult(await payroll.groups.remove(command.id));
|
|
2393
|
+
return null;
|
|
2394
|
+
}
|
|
2395
|
+
return unwrapCapxulResult(await payroll.groups.save(command.input));
|
|
2396
|
+
},
|
|
2397
|
+
onSettled: useCallback(() => queryClient.invalidateQueries({ queryKey: capxulKeys.payrollGroups(orgId) }), [orgId, queryClient])
|
|
2398
|
+
});
|
|
2399
|
+
const mutateAsync = write.mutateAsync;
|
|
2400
|
+
const save = useCallback(async (input) => {
|
|
2401
|
+
const refusal = invalidGroup(input);
|
|
2402
|
+
if (refusal !== null) return {
|
|
2403
|
+
ok: false,
|
|
2404
|
+
error: refusal
|
|
2405
|
+
};
|
|
2406
|
+
try {
|
|
2407
|
+
const saved = await mutateAsync({
|
|
2408
|
+
kind: "save",
|
|
2409
|
+
input
|
|
2410
|
+
});
|
|
2411
|
+
return saved === null ? {
|
|
2412
|
+
ok: false,
|
|
2413
|
+
error: Errors.unknown()
|
|
2414
|
+
} : {
|
|
2415
|
+
ok: true,
|
|
2416
|
+
value: toPayrollGroupRow(saved)
|
|
2417
|
+
};
|
|
2418
|
+
} catch (cause) {
|
|
2419
|
+
return {
|
|
2420
|
+
ok: false,
|
|
2421
|
+
error: asCapxulError(cause)
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
}, [mutateAsync]);
|
|
2425
|
+
const remove = useCallback(async (id) => {
|
|
2426
|
+
try {
|
|
2427
|
+
await mutateAsync({
|
|
2428
|
+
kind: "remove",
|
|
2429
|
+
id
|
|
2430
|
+
});
|
|
2431
|
+
return {
|
|
2432
|
+
ok: true,
|
|
2433
|
+
value: void 0
|
|
2434
|
+
};
|
|
2435
|
+
} catch (cause) {
|
|
2436
|
+
return {
|
|
2437
|
+
ok: false,
|
|
2438
|
+
error: asCapxulError(cause)
|
|
2439
|
+
};
|
|
2440
|
+
}
|
|
2441
|
+
}, [mutateAsync]);
|
|
2442
|
+
const rows = useMemo(() => (groups.data ?? []).map(toPayrollGroupRow), [groups.data]);
|
|
2443
|
+
const started = orgId !== void 0;
|
|
2444
|
+
return {
|
|
2445
|
+
summary: {
|
|
2446
|
+
walletBalanceDisplay: treasury.data === void 0 ? null : formatMoney(treasury.data.available, GRAMMAR$1),
|
|
2447
|
+
paidThisMonthDisplay: runs.data === void 0 ? null : formatMoney(runs.data.settledThisMonth, GRAMMAR$1),
|
|
2448
|
+
isLoading: started && (treasury.data === void 0 && treasury.error === null || runs.data === void 0 && runs.error === null),
|
|
2449
|
+
error: treasury.error ?? runs.error
|
|
2450
|
+
},
|
|
2451
|
+
groups: {
|
|
2452
|
+
groups: rows,
|
|
2453
|
+
save,
|
|
2454
|
+
remove,
|
|
2455
|
+
isSaving: write.isPending,
|
|
2456
|
+
isLoading: started && groups.data === void 0 && groups.error === null,
|
|
2457
|
+
error: groups.error
|
|
2458
|
+
}
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
/**
|
|
2462
|
+
* React Query rejects with whatever the query function threw. Every throw on
|
|
2463
|
+
* this path is a `CapxulError` the SDK produced; anything else is a defect and
|
|
2464
|
+
* normalizes once, exactly as the facade boundary does.
|
|
2465
|
+
*/
|
|
2466
|
+
function asCapxulError(cause) {
|
|
2467
|
+
return isCapxulError$1(cause) ? cause : Errors.unknown(cause);
|
|
2468
|
+
}
|
|
2469
|
+
function isCapxulError$1(cause) {
|
|
2470
|
+
return typeof cause === "object" && cause !== null && "code" in cause;
|
|
2471
|
+
}
|
|
2472
|
+
//#endregion
|
|
2473
|
+
//#region src/headless/payroll/payroll.tsx
|
|
2474
|
+
const PayrollContext = createContext(null);
|
|
2475
|
+
function usePayrollContext() {
|
|
2476
|
+
const engine = useContext(PayrollContext);
|
|
2477
|
+
if (engine === null) throw new Error("CapxulPayroll parts must be used inside <CapxulPayroll>");
|
|
2478
|
+
return engine;
|
|
2479
|
+
}
|
|
2480
|
+
function Root$1({ orgId, children }) {
|
|
2481
|
+
const engine = usePayroll(orgId);
|
|
2482
|
+
return /* @__PURE__ */ jsx(PayrollContext.Provider, {
|
|
2483
|
+
value: engine,
|
|
2484
|
+
children
|
|
2485
|
+
});
|
|
2486
|
+
}
|
|
2487
|
+
function Summary$1({ children }) {
|
|
2488
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(usePayrollContext().summary) });
|
|
2489
|
+
}
|
|
2490
|
+
function Groups({ children }) {
|
|
2491
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(usePayrollContext().groups) });
|
|
2492
|
+
}
|
|
2493
|
+
/**
|
|
2494
|
+
* The compound shape the app's own design system already uses (`StatusTabs`).
|
|
2495
|
+
* There is NO `.Runs` part (ruling Q6): no shipped region has the right shape
|
|
2496
|
+
* for a run list, and a part without a production caller may not ship.
|
|
2497
|
+
*/
|
|
2498
|
+
const CapxulPayroll = Object.assign(Root$1, {
|
|
2499
|
+
Summary: Summary$1,
|
|
2500
|
+
Groups
|
|
2501
|
+
});
|
|
2502
|
+
//#endregion
|
|
2503
|
+
//#region src/headless/payroll/use-payroll-run.ts
|
|
2504
|
+
const GRAMMAR = { grammar: "code" };
|
|
2505
|
+
/**
|
|
2506
|
+
* The one place the composer states a currency it has not read yet. The token
|
|
2507
|
+
* dropdown holds exactly one option — the treasury's — so before that read
|
|
2508
|
+
* lands there is no currency to name, and naming a wrong one would be a fake
|
|
2509
|
+
* green on the money itself.
|
|
2510
|
+
*/
|
|
2511
|
+
const CURRENCY_UNREAD = "";
|
|
2512
|
+
/**
|
|
2513
|
+
* WHICH run the composer is composing, by MEANING rather than by object shape.
|
|
2514
|
+
*
|
|
2515
|
+
* `recipientIds` order is meaning — it is the order `.Amounts` rows appear in.
|
|
2516
|
+
* The `amounts` order is not, so the key reads the seed `initialAmounts` will
|
|
2517
|
+
* actually build rather than the array it was handed: one collapse rule, in one
|
|
2518
|
+
* place, so the two can never disagree about a repeated recipient. A parent
|
|
2519
|
+
* that rebuilds an equivalent `prefill` therefore keeps its key, and the
|
|
2520
|
+
* employer's in-progress edits with it. An absent `prefill` and one that seeds
|
|
2521
|
+
* nothing open the same empty composer, so they share a key on purpose.
|
|
2522
|
+
*/
|
|
2523
|
+
function composerIdentity(orgId, prefill) {
|
|
2524
|
+
const seeds = Object.entries(initialAmounts(prefill)).map(([recipientId, draft]) => `${recipientId}=${draft.value}`).sort();
|
|
2525
|
+
return [
|
|
2526
|
+
orgId ?? "",
|
|
2527
|
+
...prefill?.recipientIds ?? [],
|
|
2528
|
+
"|",
|
|
2529
|
+
...seeds
|
|
2530
|
+
].join("\0");
|
|
2531
|
+
}
|
|
2532
|
+
/** Seeded from `prefill` on first render; the employer owns it after that. */
|
|
2533
|
+
function initialAmounts(prefill) {
|
|
2534
|
+
const seeded = {};
|
|
2535
|
+
for (const entry of prefill?.amounts ?? []) seeded[entry.recipientId] = {
|
|
2536
|
+
value: entry.amount,
|
|
2537
|
+
prefilled: true
|
|
2538
|
+
};
|
|
2539
|
+
return seeded;
|
|
2540
|
+
}
|
|
2541
|
+
function usePayrollRun(input) {
|
|
2542
|
+
const { orgId, prefill, onRun, onFailed } = input;
|
|
2543
|
+
const client = useCapxulClientOrNull();
|
|
2544
|
+
const queryClient = useQueryClient();
|
|
2545
|
+
const signerStatus = useSignerStatus(client);
|
|
2546
|
+
const roster = useOrgRoster(orgId);
|
|
2547
|
+
const groups = usePayrollGroups(orgId);
|
|
2548
|
+
const treasury = useOrgTreasury(orgId);
|
|
2549
|
+
const me = useOrgMe(orgId);
|
|
2550
|
+
const [selected, setSelected] = useState(() => prefill?.recipientIds ?? []);
|
|
2551
|
+
const [amounts, setAmounts] = useState(() => initialAmounts(prefill));
|
|
2552
|
+
const composerKey = composerIdentity(orgId, prefill);
|
|
2553
|
+
const [seededKey, setSeededKey] = useState(composerKey);
|
|
2554
|
+
if (seededKey !== composerKey) {
|
|
2555
|
+
setSeededKey(composerKey);
|
|
2556
|
+
setSelected(prefill?.recipientIds ?? []);
|
|
2557
|
+
setAmounts(initialAmounts(prefill));
|
|
2558
|
+
}
|
|
2559
|
+
const toggle = useCallback((id) => setSelected((current) => current.includes(id) ? current.filter((held) => held !== id) : [...current, id]), []);
|
|
2560
|
+
const remove = useCallback((id) => setSelected((current) => current.filter((held) => held !== id)), []);
|
|
2561
|
+
const change = useCallback((id, text) => {
|
|
2562
|
+
setAmounts((current) => ({
|
|
2563
|
+
...current,
|
|
2564
|
+
[id]: {
|
|
2565
|
+
value: text,
|
|
2566
|
+
prefilled: false
|
|
2567
|
+
}
|
|
2568
|
+
}));
|
|
2569
|
+
}, []);
|
|
2570
|
+
const account = treasury.data ?? null;
|
|
2571
|
+
const assetOptions = useMemo(() => account === null ? [] : [{
|
|
2572
|
+
id: account.id,
|
|
2573
|
+
symbol: account.available.currency,
|
|
2574
|
+
availableDisplay: formatMoney(account.available, GRAMMAR)
|
|
2575
|
+
}], [account]);
|
|
2576
|
+
const [selectedAssetId, setSelectedAssetId] = useState(null);
|
|
2577
|
+
useEffect(() => {
|
|
2578
|
+
if (!assetOptions.some((option) => option.id === selectedAssetId)) setSelectedAssetId(assetOptions[0]?.id ?? null);
|
|
2579
|
+
}, [assetOptions, selectedAssetId]);
|
|
2580
|
+
const selectedAsset = assetOptions.find((option) => option.id === selectedAssetId) ?? null;
|
|
2581
|
+
const selectAsset = useCallback((id) => {
|
|
2582
|
+
setSelectedAssetId((current) => assetOptions.some((option) => option.id === id) ? id : current);
|
|
2583
|
+
}, [assetOptions]);
|
|
2584
|
+
const assetMoney = selectedAsset !== null && account !== null ? account.available : null;
|
|
2585
|
+
const named = useMemo(() => new Map(roster.options.map((option) => [option.id, option])), [roster.options]);
|
|
2586
|
+
const refs = roster.refs;
|
|
2587
|
+
const payableRefs = useMemo(() => {
|
|
2588
|
+
const resolved = [];
|
|
2589
|
+
for (const recipientId of selected) {
|
|
2590
|
+
const ref = refs.get(recipientId);
|
|
2591
|
+
if (ref === void 0) return null;
|
|
2592
|
+
resolved.push(ref);
|
|
2593
|
+
}
|
|
2594
|
+
return resolved;
|
|
2595
|
+
}, [refs, selected]);
|
|
2596
|
+
const entries = useMemo(() => selected.map((recipientId) => {
|
|
2597
|
+
const draft = amounts[recipientId];
|
|
2598
|
+
const value = draft?.value ?? "";
|
|
2599
|
+
const parsed = assetMoney === null || value.trim() === "" ? null : parseMoney(value, assetMoney);
|
|
2600
|
+
return {
|
|
2601
|
+
recipientId,
|
|
2602
|
+
name: named.get(recipientId)?.name ?? recipientId,
|
|
2603
|
+
value,
|
|
2604
|
+
change: (text) => change(recipientId, text),
|
|
2605
|
+
error: parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null,
|
|
2606
|
+
prefilled: draft?.prefilled ?? false,
|
|
2607
|
+
currency: assetMoney?.currency ?? CURRENCY_UNREAD
|
|
2608
|
+
};
|
|
2609
|
+
}), [
|
|
2610
|
+
amounts,
|
|
2611
|
+
assetMoney,
|
|
2612
|
+
change,
|
|
2613
|
+
named,
|
|
2614
|
+
selected
|
|
2615
|
+
]);
|
|
2616
|
+
const parsedAmounts = useMemo(() => {
|
|
2617
|
+
if (assetMoney === null || selected.length === 0) return null;
|
|
2618
|
+
const money = [];
|
|
2619
|
+
for (const recipientId of selected) {
|
|
2620
|
+
const parsed = parseMoney(amounts[recipientId]?.value ?? "", assetMoney);
|
|
2621
|
+
if (isMoneyParseError(parsed)) return null;
|
|
2622
|
+
money.push(parsed);
|
|
2623
|
+
}
|
|
2624
|
+
return money;
|
|
2625
|
+
}, [
|
|
2626
|
+
amounts,
|
|
2627
|
+
assetMoney,
|
|
2628
|
+
selected
|
|
2629
|
+
]);
|
|
2630
|
+
const total = useMemo(() => parsedAmounts === null || assetMoney === null ? null : fromMinorUnits(parsedAmounts.reduce((sum, money) => sum + toMinorUnits(money), 0n), assetMoney), [assetMoney, parsedAmounts]);
|
|
2631
|
+
const availableRaw = assetMoney === null ? null : toMinorUnits(assetMoney);
|
|
2632
|
+
const totalRaw = total === null ? null : toMinorUnits(total);
|
|
2633
|
+
const funds = availableRaw === null || totalRaw === null ? "unknown" : totalRaw > availableRaw ? "insufficient" : "available";
|
|
2634
|
+
const budgets = me.data?.budgets ?? [];
|
|
2635
|
+
const blockedReason = firstFalseFact({
|
|
2636
|
+
signerReady: signerStatus === "ready",
|
|
2637
|
+
budgetCount: budgets.length,
|
|
2638
|
+
hasAsset: selectedAsset !== null,
|
|
2639
|
+
recipientCount: payableRefs === null ? 0 : selected.length,
|
|
2640
|
+
amountsParse: total !== null,
|
|
2641
|
+
funds
|
|
2642
|
+
});
|
|
2643
|
+
const authorize = useMutation({
|
|
2644
|
+
mutationFn: async (command) => {
|
|
2645
|
+
const runAt = Date.now();
|
|
2646
|
+
return unwrapCapxulResult(await scopedClient(client, "org.payroll.authorizeRun", orgId).payroll.authorizeRun({
|
|
2647
|
+
permissionId: command.permissionId,
|
|
2648
|
+
period: {
|
|
2649
|
+
start: runAt,
|
|
2650
|
+
end: runAt
|
|
2651
|
+
},
|
|
2652
|
+
items: command.items
|
|
2653
|
+
}));
|
|
2654
|
+
},
|
|
2655
|
+
onSettled: () => {
|
|
2656
|
+
queryClient.invalidateQueries({ queryKey: capxulKeys.payrollRuns(orgId) });
|
|
2657
|
+
}
|
|
2658
|
+
});
|
|
2659
|
+
const submitGate = useRef(false);
|
|
2660
|
+
const totalDisplay = total === null ? null : formatMoney(total, GRAMMAR);
|
|
2661
|
+
const authorizeMutate = authorize.mutate;
|
|
2662
|
+
const submit = useCallback(() => {
|
|
2663
|
+
const permissionId = budgets[0]?.id;
|
|
2664
|
+
if (blockedReason !== null || submitGate.current) return;
|
|
2665
|
+
if (permissionId === void 0 || totalDisplay === null || parsedAmounts === null) return;
|
|
2666
|
+
if (payableRefs === null) return;
|
|
2667
|
+
const recipientCount = selected.length;
|
|
2668
|
+
const items = [];
|
|
2669
|
+
for (const [index, recipientId] of selected.entries()) {
|
|
2670
|
+
const to = payableRefs[index];
|
|
2671
|
+
const parsed = parsedAmounts[index];
|
|
2672
|
+
if (to === void 0 || parsed === void 0) return;
|
|
2673
|
+
const raw = toMinorUnits(parsed).toString();
|
|
2674
|
+
items.push({
|
|
2675
|
+
to,
|
|
2676
|
+
partyId: recipientId,
|
|
2677
|
+
gross: raw,
|
|
2678
|
+
net: raw,
|
|
2679
|
+
adjustments: []
|
|
2680
|
+
});
|
|
2681
|
+
}
|
|
2682
|
+
submitGate.current = true;
|
|
2683
|
+
authorizeMutate({
|
|
2684
|
+
permissionId,
|
|
2685
|
+
items
|
|
2686
|
+
}, {
|
|
2687
|
+
onSuccess: (run) => onRun({
|
|
2688
|
+
id: run.id,
|
|
2689
|
+
totalDisplay,
|
|
2690
|
+
recipientCount
|
|
2691
|
+
}),
|
|
2692
|
+
onError: onFailed,
|
|
2693
|
+
onSettled: () => {
|
|
2694
|
+
submitGate.current = false;
|
|
2695
|
+
}
|
|
2696
|
+
});
|
|
2697
|
+
}, [
|
|
2698
|
+
authorizeMutate,
|
|
2699
|
+
blockedReason,
|
|
2700
|
+
budgets,
|
|
2701
|
+
onFailed,
|
|
2702
|
+
onRun,
|
|
2703
|
+
parsedAmounts,
|
|
2704
|
+
payableRefs,
|
|
2705
|
+
selected,
|
|
2706
|
+
totalDisplay
|
|
2707
|
+
]);
|
|
2708
|
+
return {
|
|
2709
|
+
recipients: {
|
|
2710
|
+
options: roster.options,
|
|
2711
|
+
groups: (groups.data ?? []).map((group) => ({
|
|
2712
|
+
id: group.id,
|
|
2713
|
+
label: group.name,
|
|
2714
|
+
badgeLabel: String(group.members.length),
|
|
2715
|
+
recipientIds: group.members.map((member) => member.partyId)
|
|
2716
|
+
})),
|
|
2717
|
+
selected,
|
|
2718
|
+
toggle,
|
|
2719
|
+
remove,
|
|
2720
|
+
isLoading: roster.isLoading,
|
|
2721
|
+
error: roster.error ?? groups.error
|
|
2722
|
+
},
|
|
2723
|
+
amounts: { entries },
|
|
2724
|
+
asset: {
|
|
2725
|
+
options: assetOptions,
|
|
2726
|
+
selected: selectedAsset,
|
|
2727
|
+
select: selectAsset,
|
|
2728
|
+
error: treasury.error === null ? null : "read-failed"
|
|
2729
|
+
},
|
|
2730
|
+
summary: {
|
|
2731
|
+
recipientCount: selected.length,
|
|
2732
|
+
totalDisplay,
|
|
2733
|
+
balanceAfterDisplay: totalRaw === null || availableRaw === null || assetMoney === null ? null : formatMoney(fromMinorUnits(availableRaw - totalRaw, assetMoney), GRAMMAR),
|
|
2734
|
+
funds
|
|
2735
|
+
},
|
|
2736
|
+
actions: {
|
|
2737
|
+
submit,
|
|
2738
|
+
isSubmitting: authorize.isPending,
|
|
2739
|
+
blocked: blockedReason !== null,
|
|
2740
|
+
blockedReason
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
/**
|
|
2745
|
+
* The seven facts, in the ONE order the contract fixes. Exactly one code comes
|
|
2746
|
+
* out, and it is always the first thing that is false — so two problems never
|
|
2747
|
+
* race to name the button.
|
|
2748
|
+
*/
|
|
2749
|
+
function firstFalseFact(facts) {
|
|
2750
|
+
if (!facts.signerReady) return "signer-not-ready";
|
|
2751
|
+
if (facts.budgetCount === 0) return "no-source";
|
|
2752
|
+
if (facts.budgetCount > 1) return "ambiguous-source";
|
|
2753
|
+
if (!facts.hasAsset) return "no-asset";
|
|
2754
|
+
if (facts.recipientCount === 0) return "no-recipients";
|
|
2755
|
+
if (!facts.amountsParse) return "amount-invalid";
|
|
2756
|
+
return facts.funds === "insufficient" ? "insufficient-funds" : null;
|
|
2757
|
+
}
|
|
2758
|
+
//#endregion
|
|
2759
|
+
//#region src/headless/payroll/payroll-run.tsx
|
|
2760
|
+
const PayrollRunContext = createContext(null);
|
|
2761
|
+
function usePayrollRunContext() {
|
|
2762
|
+
const engine = useContext(PayrollRunContext);
|
|
2763
|
+
if (engine === null) throw new Error("CapxulPayrollRun parts must be used inside <CapxulPayrollRun>");
|
|
2764
|
+
return engine;
|
|
2765
|
+
}
|
|
2766
|
+
function Root({ orgId, prefill, onRun, onFailed, children }) {
|
|
2767
|
+
const engine = usePayrollRun({
|
|
2768
|
+
orgId,
|
|
2769
|
+
prefill,
|
|
2770
|
+
onRun,
|
|
2771
|
+
onFailed
|
|
2772
|
+
});
|
|
2773
|
+
return /* @__PURE__ */ jsx(PayrollRunContext.Provider, {
|
|
2774
|
+
value: engine,
|
|
2775
|
+
children
|
|
2776
|
+
});
|
|
2777
|
+
}
|
|
2778
|
+
function Recipients({ children }) {
|
|
2779
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(usePayrollRunContext().recipients) });
|
|
2780
|
+
}
|
|
2781
|
+
function Amounts({ children }) {
|
|
2782
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(usePayrollRunContext().amounts) });
|
|
2783
|
+
}
|
|
2784
|
+
function Asset({ children }) {
|
|
2785
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(usePayrollRunContext().asset) });
|
|
2786
|
+
}
|
|
2787
|
+
function Summary({ children }) {
|
|
2788
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(usePayrollRunContext().summary) });
|
|
2789
|
+
}
|
|
2790
|
+
function Actions({ children }) {
|
|
2791
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(usePayrollRunContext().actions) });
|
|
2792
|
+
}
|
|
2793
|
+
/** The compound shape the app's own design system already uses (`StatusTabs`). */
|
|
2794
|
+
const CapxulPayrollRun = Object.assign(Root, {
|
|
2795
|
+
Recipients,
|
|
2796
|
+
Amounts,
|
|
2797
|
+
Asset,
|
|
2798
|
+
Summary,
|
|
2799
|
+
Actions
|
|
2800
|
+
});
|
|
2801
|
+
//#endregion
|
|
2802
|
+
export { CapxulActivity, CapxulAuthenticationController, 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, useCapxulHoldings, useCapxulIdentity, useCapxulImageUpload, useCapxulInviteMember, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationPay, useCapxulOrganizationPayBatch, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPermission, useCapxulPermissionAssign, useCapxulPermissionChange, useCapxulPermissionCreate, useCapxulPermissionReplace, useCapxulPermissionRevoke, useCapxulPermissions, useCapxulProfile, useCapxulRedirectPayment, useCapxulSend, useCapxulTransitions, useCapxulUsernameAvailability };
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as CapxulOnboardingController, r as CapxulProvider, t as CapxulAuthenticationController } from "../controllers-
|
|
1
|
+
import { n as CapxulOnboardingController, r as CapxulProvider, t as CapxulAuthenticationController } from "../controllers-DpScp8c5.mjs";
|
|
2
2
|
import "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import { createCapxulTestClient } from "@capxul/sdk/testing";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capxul/sdk-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/Xelmar-tech/infrastructure.git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@capxul/sdk": "2.
|
|
29
|
+
"@capxul/sdk": "2.5.1"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@tanstack/react-query": "^5.66.9",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"react-dom": "^19.2.6",
|
|
43
43
|
"vite-plus": "0.1.23",
|
|
44
44
|
"vitest": "npm:@voidzero-dev/vite-plus-test@0.1.23",
|
|
45
|
-
"@capxul/errors": "0.0.
|
|
46
|
-
"@capxul/types": "0.2.
|
|
45
|
+
"@capxul/errors": "0.0.2",
|
|
46
|
+
"@capxul/types": "0.2.1",
|
|
47
47
|
"@capxul/typescript-config": "0.0.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|