@base316/react 0.1.0 → 0.3.0
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/CHANGELOG.md +53 -0
- package/README.md +29 -15
- package/dist/index.cjs +4361 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4344 -164
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1176 -0
- package/dist/src/AcceptInvitation.d.ts +27 -0
- package/dist/src/AcceptInvitation.d.ts.map +1 -0
- package/dist/src/ActiveSessions.d.ts +21 -0
- package/dist/src/ActiveSessions.d.ts.map +1 -0
- package/dist/src/ApiKeyManager.d.ts +22 -0
- package/dist/src/ApiKeyManager.d.ts.map +1 -0
- package/dist/src/Chart.d.ts +42 -0
- package/dist/src/Chart.d.ts.map +1 -0
- package/dist/src/DataTable.d.ts +56 -0
- package/dist/src/DataTable.d.ts.map +1 -0
- package/dist/src/DeviceApproval.d.ts +30 -0
- package/dist/src/DeviceApproval.d.ts.map +1 -0
- package/dist/src/ForgotPassword.d.ts +24 -0
- package/dist/src/ForgotPassword.d.ts.map +1 -0
- package/dist/src/Invoices.d.ts.map +1 -1
- package/dist/src/Kanban.d.ts +40 -0
- package/dist/src/Kanban.d.ts.map +1 -0
- package/dist/src/PasskeyManager.d.ts.map +1 -1
- package/dist/src/PayInvoice.d.ts +41 -0
- package/dist/src/PayInvoice.d.ts.map +1 -0
- package/dist/src/PricingTable.d.ts +43 -11
- package/dist/src/PricingTable.d.ts.map +1 -1
- package/dist/src/PrivacyControls.d.ts +28 -0
- package/dist/src/PrivacyControls.d.ts.map +1 -0
- package/dist/src/RecordDetail.d.ts +31 -0
- package/dist/src/RecordDetail.d.ts.map +1 -0
- package/dist/src/RecordForm.d.ts +32 -0
- package/dist/src/RecordForm.d.ts.map +1 -0
- package/dist/src/RelationPicker.d.ts +23 -0
- package/dist/src/RelationPicker.d.ts.map +1 -0
- package/dist/src/ResetPassword.d.ts +24 -0
- package/dist/src/ResetPassword.d.ts.map +1 -0
- package/dist/src/SignIn.d.ts +20 -2
- package/dist/src/SignIn.d.ts.map +1 -1
- package/dist/src/SignUp.d.ts.map +1 -1
- package/dist/src/SubscriptionManager.d.ts.map +1 -1
- package/dist/src/TeamMembers.d.ts +49 -0
- package/dist/src/TeamMembers.d.ts.map +1 -0
- package/dist/src/TwoFactorManager.d.ts +38 -0
- package/dist/src/TwoFactorManager.d.ts.map +1 -0
- package/dist/src/VerifyEmail.d.ts +24 -0
- package/dist/src/VerifyEmail.d.ts.map +1 -0
- package/dist/src/appearance.d.ts +2 -2
- package/dist/src/appearance.d.ts.map +1 -1
- package/dist/src/confirm.d.ts +32 -0
- package/dist/src/confirm.d.ts.map +1 -0
- package/dist/src/fields.d.ts +44 -0
- package/dist/src/fields.d.ts.map +1 -0
- package/dist/src/manifest.d.ts +84 -0
- package/dist/src/manifest.d.ts.map +1 -0
- package/dist/src/provider.d.ts +13 -1
- package/dist/src/provider.d.ts.map +1 -1
- package/dist/src/test/harness.d.ts +204 -0
- package/dist/src/test/harness.d.ts.map +1 -0
- package/dist/src/test/setup.d.ts +2 -0
- package/dist/src/test/setup.d.ts.map +1 -0
- package/dist/src/useCollection.d.ts +79 -0
- package/dist/src/useCollection.d.ts.map +1 -0
- package/dist/src/usePaymentLink.d.ts +41 -0
- package/dist/src/usePaymentLink.d.ts.map +1 -0
- package/dist/src/usePlans.d.ts +74 -0
- package/dist/src/usePlans.d.ts.map +1 -0
- package/package.json +36 -28
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { type RenderResult } from "@testing-library/react";
|
|
2
|
+
import type { ReactElement } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Rendering a component the way a customer's page does.
|
|
5
|
+
*
|
|
6
|
+
* `fetch` is stubbed rather than the client injected, and that is deliberate:
|
|
7
|
+
* adding a `client` prop to the provider purely so tests could reach inside it
|
|
8
|
+
* would widen the public API for the convenience of this file. Stubbing the
|
|
9
|
+
* network instead exercises the real client and the real transport, so a test
|
|
10
|
+
* fails when the SDK builds the wrong URL — which is most of what these
|
|
11
|
+
* components do wrong.
|
|
12
|
+
*/
|
|
13
|
+
/** Parseable by `requireKey`: four parts, `b316`, a known type, a band, base62. */
|
|
14
|
+
export declare const TEST_KEY = "b316_pk_sandbox_0123456789abcdefgh";
|
|
15
|
+
/** Any non-empty string. The stubbed API never checks it. */
|
|
16
|
+
export declare const TEST_SESSION = "test-session";
|
|
17
|
+
export interface Route {
|
|
18
|
+
/** Matched against the path and query, e.g. "/v1/collections/jobs/records". */
|
|
19
|
+
match: (url: string, method: string) => boolean;
|
|
20
|
+
/** The JSON body, or a function of the request for a route that varies. */
|
|
21
|
+
respond: (url: string, init: RequestInit | undefined) => unknown;
|
|
22
|
+
status?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface StubbedFetch {
|
|
25
|
+
/** Every request made, in order. Assert on what a component asked for. */
|
|
26
|
+
calls: {
|
|
27
|
+
url: string;
|
|
28
|
+
method: string;
|
|
29
|
+
body: unknown;
|
|
30
|
+
}[];
|
|
31
|
+
restore: () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Answer only what a test declares, and fail loudly on anything else.
|
|
35
|
+
*
|
|
36
|
+
* An unmatched request resolves to a 404 rather than hanging or throwing, so a
|
|
37
|
+
* component that fetches something unexpected renders its error state and the
|
|
38
|
+
* test says which URL was not covered instead of timing out.
|
|
39
|
+
*/
|
|
40
|
+
export declare function stubFetch(routes: Route[]): StubbedFetch;
|
|
41
|
+
/**
|
|
42
|
+
* Render inside a provider holding a session.
|
|
43
|
+
*
|
|
44
|
+
* `token` is passed as a controlled prop rather than written to localStorage:
|
|
45
|
+
* the provider treats a supplied token as owned by the host application, which
|
|
46
|
+
* skips the storage read and the session refresh a test does not care about.
|
|
47
|
+
*/
|
|
48
|
+
export declare function renderWithProvider(ui: ReactElement, { token }?: {
|
|
49
|
+
token?: string | null;
|
|
50
|
+
}): RenderResult;
|
|
51
|
+
/**
|
|
52
|
+
* A collection resembling something somebody would define.
|
|
53
|
+
*
|
|
54
|
+
* Deliberately contains a field of each awkward kind: one that cannot be
|
|
55
|
+
* sorted, one that cannot be edited, and one whose value lives elsewhere.
|
|
56
|
+
*/
|
|
57
|
+
export declare const JOB_SCHEMA: {
|
|
58
|
+
key: string;
|
|
59
|
+
name: string;
|
|
60
|
+
namePlural: string;
|
|
61
|
+
description: null;
|
|
62
|
+
scope: "TENANT";
|
|
63
|
+
canWrite: boolean;
|
|
64
|
+
fields: ({
|
|
65
|
+
key: string;
|
|
66
|
+
label: string;
|
|
67
|
+
type: "TEXT";
|
|
68
|
+
required: boolean;
|
|
69
|
+
position: number;
|
|
70
|
+
options?: undefined;
|
|
71
|
+
relationTo?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
required?: undefined;
|
|
74
|
+
key: string;
|
|
75
|
+
label: string;
|
|
76
|
+
type: "SELECT";
|
|
77
|
+
options: string[];
|
|
78
|
+
position: number;
|
|
79
|
+
relationTo?: undefined;
|
|
80
|
+
} | {
|
|
81
|
+
required?: undefined;
|
|
82
|
+
options?: undefined;
|
|
83
|
+
key: string;
|
|
84
|
+
label: string;
|
|
85
|
+
type: "MONEY";
|
|
86
|
+
position: number;
|
|
87
|
+
relationTo?: undefined;
|
|
88
|
+
} | {
|
|
89
|
+
required?: undefined;
|
|
90
|
+
options?: undefined;
|
|
91
|
+
key: string;
|
|
92
|
+
label: string;
|
|
93
|
+
type: "LONGTEXT";
|
|
94
|
+
position: number;
|
|
95
|
+
relationTo?: undefined;
|
|
96
|
+
} | {
|
|
97
|
+
required?: undefined;
|
|
98
|
+
options?: undefined;
|
|
99
|
+
key: string;
|
|
100
|
+
label: string;
|
|
101
|
+
type: "RELATION";
|
|
102
|
+
relationTo: string;
|
|
103
|
+
position: number;
|
|
104
|
+
} | {
|
|
105
|
+
required?: undefined;
|
|
106
|
+
options?: undefined;
|
|
107
|
+
relationTo?: undefined;
|
|
108
|
+
key: string;
|
|
109
|
+
label: string;
|
|
110
|
+
type: "AUTONUMBER";
|
|
111
|
+
position: number;
|
|
112
|
+
})[];
|
|
113
|
+
};
|
|
114
|
+
export declare function jobRecord(id: string, fields: Record<string, unknown>, related?: Record<string, {
|
|
115
|
+
id: string;
|
|
116
|
+
title: string | null;
|
|
117
|
+
}>): {
|
|
118
|
+
id: string;
|
|
119
|
+
title: string;
|
|
120
|
+
fields: Record<string, unknown>;
|
|
121
|
+
related: Record<string, {
|
|
122
|
+
id: string;
|
|
123
|
+
title: string | null;
|
|
124
|
+
}>;
|
|
125
|
+
createdAt: string;
|
|
126
|
+
updatedAt: string;
|
|
127
|
+
createdBy: null;
|
|
128
|
+
updatedBy: null;
|
|
129
|
+
};
|
|
130
|
+
/** The two routes every collection component needs before it renders anything. */
|
|
131
|
+
export declare function collectionRoutes({ schema, records, extra, }?: {
|
|
132
|
+
extra?: Route[] | undefined;
|
|
133
|
+
records?: {
|
|
134
|
+
id: string;
|
|
135
|
+
title: string;
|
|
136
|
+
fields: Record<string, unknown>;
|
|
137
|
+
related: Record<string, {
|
|
138
|
+
id: string;
|
|
139
|
+
title: string | null;
|
|
140
|
+
}>;
|
|
141
|
+
createdAt: string;
|
|
142
|
+
updatedAt: string;
|
|
143
|
+
createdBy: null;
|
|
144
|
+
updatedBy: null;
|
|
145
|
+
}[] | undefined;
|
|
146
|
+
schema?: {
|
|
147
|
+
key: string;
|
|
148
|
+
name: string;
|
|
149
|
+
namePlural: string;
|
|
150
|
+
description: null;
|
|
151
|
+
scope: "TENANT";
|
|
152
|
+
canWrite: boolean;
|
|
153
|
+
fields: ({
|
|
154
|
+
key: string;
|
|
155
|
+
label: string;
|
|
156
|
+
type: "TEXT";
|
|
157
|
+
required: boolean;
|
|
158
|
+
position: number;
|
|
159
|
+
options?: undefined;
|
|
160
|
+
relationTo?: undefined;
|
|
161
|
+
} | {
|
|
162
|
+
required?: undefined;
|
|
163
|
+
key: string;
|
|
164
|
+
label: string;
|
|
165
|
+
type: "SELECT";
|
|
166
|
+
options: string[];
|
|
167
|
+
position: number;
|
|
168
|
+
relationTo?: undefined;
|
|
169
|
+
} | {
|
|
170
|
+
required?: undefined;
|
|
171
|
+
options?: undefined;
|
|
172
|
+
key: string;
|
|
173
|
+
label: string;
|
|
174
|
+
type: "MONEY";
|
|
175
|
+
position: number;
|
|
176
|
+
relationTo?: undefined;
|
|
177
|
+
} | {
|
|
178
|
+
required?: undefined;
|
|
179
|
+
options?: undefined;
|
|
180
|
+
key: string;
|
|
181
|
+
label: string;
|
|
182
|
+
type: "LONGTEXT";
|
|
183
|
+
position: number;
|
|
184
|
+
relationTo?: undefined;
|
|
185
|
+
} | {
|
|
186
|
+
required?: undefined;
|
|
187
|
+
options?: undefined;
|
|
188
|
+
key: string;
|
|
189
|
+
label: string;
|
|
190
|
+
type: "RELATION";
|
|
191
|
+
relationTo: string;
|
|
192
|
+
position: number;
|
|
193
|
+
} | {
|
|
194
|
+
required?: undefined;
|
|
195
|
+
options?: undefined;
|
|
196
|
+
relationTo?: undefined;
|
|
197
|
+
key: string;
|
|
198
|
+
label: string;
|
|
199
|
+
type: "AUTONUMBER";
|
|
200
|
+
position: number;
|
|
201
|
+
})[];
|
|
202
|
+
} | undefined;
|
|
203
|
+
}): Route[];
|
|
204
|
+
//# sourceMappingURL=harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/test/harness.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAI1C;;;;;;;;;GASG;AAEH,mFAAmF;AACnF,eAAO,MAAM,QAAQ,uCAAuC,CAAC;AAC7D,6DAA6D;AAC7D,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,MAAM,WAAW,KAAK;IACrB,+EAA+E;IAC/E,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,2EAA2E;IAC3E,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,SAAS,KAAK,OAAO,CAAC;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,0EAA0E;IAC1E,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IACxD,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,YAAY,CAwCvD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,EAAE,EAAE,YAAY,EAGhB,EAAE,KAAoB,EAAE,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GACtD,YAAY,CAMd;AAID;;;;;GAKG;AACH,eAAO,MAAM,UAAU;;;;;WAKf,QAAQ;;;;;cAGwB,MAAM;;;;;;;;;cAIrC,QAAQ;;;;;;;;;cAIuB,OAAO;;;;;;;;cACP,UAAU;;;;;;;;cAIzC,UAAU;;;;;;;;;cAI6B,YAAY;;;CAE3D,CAAC;AAEF,wBAAgB,SAAS,CACxB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAM;;;;;YAApC,MAAM;eAAS,MAAM,GAAG,IAAI;;;;;;EAY1D;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,EAChC,MAAmB,EACnB,OAA8C,EAC9C,KAAqB,GACrB;;;;;;;gBAnB8B,MAAM;mBAAS,MAAM,GAAG,IAAI;;;;;;;;;;;;eA3BnD,QAAQ;;;;;kBAGwB,MAAM;;;;;;;;;kBAIrC,QAAQ;;;;;;;;;kBAIuB,OAAO;;;;;;;;kBACP,UAAU;;;;;;;;kBAIzC,UAAU;;;;;;;;;kBAI6B,YAAY;;;;CA0BtD,GAAG,KAAK,EAAE,CAYf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/test/setup.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { CollectionRecord, CollectionSchema, FieldValue, RecordFilterInput } from "@base316/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* One collection: its shape, its rows, and how to change them.
|
|
4
|
+
*
|
|
5
|
+
* The headless half of every data component. `DataTable`, `RecordForm` and
|
|
6
|
+
* `RecordDetail` are markup over this, and an application with its own design
|
|
7
|
+
* system calls it directly and writes its own — which is the escape hatch that
|
|
8
|
+
* stops a component library from being forked the first time somebody wants a
|
|
9
|
+
* different table.
|
|
10
|
+
*
|
|
11
|
+
* What it does that is worth not rewriting:
|
|
12
|
+
*
|
|
13
|
+
* - loads the schema before the rows, because nothing can render a column
|
|
14
|
+
* without knowing the field types;
|
|
15
|
+
* - keeps filters, sort, search and paging in one place, so they compose
|
|
16
|
+
* instead of fighting;
|
|
17
|
+
* - refetches after a write, so a saved row appears where it belongs rather
|
|
18
|
+
* than where it was;
|
|
19
|
+
* - keeps per-field errors from the server, so a form can put each message
|
|
20
|
+
* under the input that caused it.
|
|
21
|
+
*/
|
|
22
|
+
export interface UseCollectionOptions {
|
|
23
|
+
/** Applied on top of whatever the UI sets. For a fixed view of one tenant's data. */
|
|
24
|
+
filters?: RecordFilterInput[];
|
|
25
|
+
sort?: {
|
|
26
|
+
field: string;
|
|
27
|
+
direction?: "asc" | "desc";
|
|
28
|
+
};
|
|
29
|
+
limit?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Load one record instead of a page.
|
|
32
|
+
*
|
|
33
|
+
* Fetched by id rather than filtered for. `id` is not a schema field, so a
|
|
34
|
+
* filter naming it is dropped on the server and the caller quietly gets the
|
|
35
|
+
* first page instead — which works until the record is not on it.
|
|
36
|
+
*/
|
|
37
|
+
recordId?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Which tenant to read and write as.
|
|
40
|
+
*
|
|
41
|
+
* Only needed when somebody belongs to several organizations. A user in one
|
|
42
|
+
* is scoped to it automatically.
|
|
43
|
+
*/
|
|
44
|
+
tenant?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface UseCollectionResult {
|
|
47
|
+
/** Null until loaded. */
|
|
48
|
+
schema: CollectionSchema | null;
|
|
49
|
+
records: CollectionRecord[];
|
|
50
|
+
loading: boolean;
|
|
51
|
+
/** Set when the collection could not be read at all — missing, or forbidden. */
|
|
52
|
+
error: string | null;
|
|
53
|
+
/** Total matching rows, for "1–25 of 340". */
|
|
54
|
+
total: number;
|
|
55
|
+
/** Whether a further page exists. */
|
|
56
|
+
hasMore: boolean;
|
|
57
|
+
/** Whether this viewer may create, edit and delete. False until the schema loads. */
|
|
58
|
+
canWrite: boolean;
|
|
59
|
+
search: string;
|
|
60
|
+
setSearch: (search: string) => void;
|
|
61
|
+
filters: RecordFilterInput[];
|
|
62
|
+
setFilters: (filters: RecordFilterInput[]) => void;
|
|
63
|
+
sort: {
|
|
64
|
+
field: string;
|
|
65
|
+
direction?: "asc" | "desc";
|
|
66
|
+
} | null;
|
|
67
|
+
/** Ascending, then descending, then off — the order a table header cycles. */
|
|
68
|
+
toggleSort: (field: string) => void;
|
|
69
|
+
loadMore: () => void;
|
|
70
|
+
reload: () => void;
|
|
71
|
+
/** Per-field messages from the last rejected write. */
|
|
72
|
+
fieldErrors: Record<string, string>;
|
|
73
|
+
saving: boolean;
|
|
74
|
+
create: (fields: Record<string, FieldValue>) => Promise<CollectionRecord | null>;
|
|
75
|
+
update: (id: string, fields: Record<string, FieldValue>) => Promise<CollectionRecord | null>;
|
|
76
|
+
remove: (id: string) => Promise<boolean>;
|
|
77
|
+
}
|
|
78
|
+
export declare function useCollection(collection: string, options?: UseCollectionOptions): UseCollectionResult;
|
|
79
|
+
//# sourceMappingURL=useCollection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCollection.d.ts","sourceRoot":"","sources":["../../src/useCollection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,MAAM,cAAc,CAAC;AAKtB;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,oBAAoB;IACpC,qFAAqF;IACrF,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAC;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IACnC,yBAAyB;IACzB,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,QAAQ,EAAE,OAAO,CAAC;IAElB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,UAAU,EAAE,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,IAAI,CAAC;IACnD,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3D,8EAA8E;IAC9E,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEpC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACjF,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC7F,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACzC;AAYD,wBAAgB,aAAa,CAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,oBAAyB,GAChC,mBAAmB,CAiTrB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watch a payment link until it is paid.
|
|
3
|
+
*
|
|
4
|
+
* For the merchant's *screen*, not their server. A webhook tells a backend that
|
|
5
|
+
* a walk-up customer paid; it cannot tell the tablet propped on the counter with
|
|
6
|
+
* the QR code on it. This is what flips that screen over.
|
|
7
|
+
*
|
|
8
|
+
* A poll rather than a socket, matching the rest of the platform: there is no
|
|
9
|
+
* streaming endpoint, and a poll is what survives a tablet sleeping and a phone
|
|
10
|
+
* hopping between wifi and cellular mid-payment.
|
|
11
|
+
*
|
|
12
|
+
* Use it with the token you got back from `invoices.createPaymentLink` on your
|
|
13
|
+
* server. Polling stops on its own once the invoice settles, so a forgotten
|
|
14
|
+
* component does not keep asking for ever.
|
|
15
|
+
*/
|
|
16
|
+
export type PaymentLinkStatus = {
|
|
17
|
+
state: "loading";
|
|
18
|
+
} | {
|
|
19
|
+
state: "payable";
|
|
20
|
+
number: string;
|
|
21
|
+
due: string;
|
|
22
|
+
currency: string;
|
|
23
|
+
} | {
|
|
24
|
+
state: "settled";
|
|
25
|
+
number: string;
|
|
26
|
+
}
|
|
27
|
+
/** Unknown, expired, or withdrawn — the API does not say which. */
|
|
28
|
+
| {
|
|
29
|
+
state: "invalid";
|
|
30
|
+
};
|
|
31
|
+
export declare function usePaymentLink({ token, environmentId, intervalMs, onPaid, }: {
|
|
32
|
+
token: string;
|
|
33
|
+
environmentId: string;
|
|
34
|
+
/**
|
|
35
|
+
* How often to ask. The default is chosen against the thing being waited for
|
|
36
|
+
* — somebody tapping a phone a metre away — rather than against server load.
|
|
37
|
+
*/
|
|
38
|
+
intervalMs?: number;
|
|
39
|
+
onPaid?: () => void;
|
|
40
|
+
}): PaymentLinkStatus;
|
|
41
|
+
//# sourceMappingURL=usePaymentLink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePaymentLink.d.ts","sourceRoot":"","sources":["../../src/usePaymentLink.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,iBAAiB,GAC1B;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,GACpB;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACtC,mEAAmE;GACjE;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB,wBAAgB,cAAc,CAAC,EAC9B,KAAK,EACL,aAAa,EACb,UAAiB,EACjB,MAAM,GACN,EAAE;IACF,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACpB,GAAG,iBAAiB,CA+EpB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { PublicPlan } from "@base316/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* The plans, and what happens when somebody picks one — without any markup.
|
|
4
|
+
*
|
|
5
|
+
* This is the escape hatch that keeps a component library from being forked.
|
|
6
|
+
* `PricingTable` is a few dozen lines of JSX over this hook; an application
|
|
7
|
+
* with its own design system calls the hook directly, renders whatever it
|
|
8
|
+
* likes, and still gets the part that is easy to get wrong:
|
|
9
|
+
*
|
|
10
|
+
* - which plan the signed-in user is actually on,
|
|
11
|
+
* - whether picking one should prorate in place or collect a card,
|
|
12
|
+
* - resolving the customer from the session rather than trusting the browser.
|
|
13
|
+
*
|
|
14
|
+
* The rule the whole library follows: logic lives in a hook, presentation
|
|
15
|
+
* lives in a component, and the component is thin enough to be worth
|
|
16
|
+
* replacing. Fusing the two is what forces a fork the first time somebody
|
|
17
|
+
* wants a different table.
|
|
18
|
+
*/
|
|
19
|
+
/** What picking a plan did. Returned so a custom UI can respond in kind. */
|
|
20
|
+
export type SelectPlanOutcome = {
|
|
21
|
+
status: "changed";
|
|
22
|
+
planVersionId: string;
|
|
23
|
+
} | {
|
|
24
|
+
status: "checkout";
|
|
25
|
+
url: string;
|
|
26
|
+
} | {
|
|
27
|
+
status: "needs-sign-in";
|
|
28
|
+
} | {
|
|
29
|
+
status: "failed";
|
|
30
|
+
reason: string;
|
|
31
|
+
};
|
|
32
|
+
export interface UsePlansOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Override the plan shown as current.
|
|
35
|
+
*
|
|
36
|
+
* Discovered from the signed-in user's subscription when omitted.
|
|
37
|
+
*/
|
|
38
|
+
currentPlanVersionId?: string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Where a checkout URL should send the browser.
|
|
41
|
+
*
|
|
42
|
+
* Defaults to navigating. Pass `false` to keep `selectPlan` free of side
|
|
43
|
+
* effects and handle the URL from the outcome — a modal checkout, a native
|
|
44
|
+
* shell, or a test.
|
|
45
|
+
*/
|
|
46
|
+
navigate?: boolean | ((url: string) => void);
|
|
47
|
+
}
|
|
48
|
+
export interface UsePlansResult {
|
|
49
|
+
/** Null until loaded. Empty means nothing is published yet. */
|
|
50
|
+
plans: PublicPlan[] | null;
|
|
51
|
+
loading: boolean;
|
|
52
|
+
/** True when the catalog could not be read at all. */
|
|
53
|
+
error: boolean;
|
|
54
|
+
/** The plan the viewer is on, discovered or supplied. */
|
|
55
|
+
currentPlanVersionId: string | null;
|
|
56
|
+
/** The plan version currently being acted on, for a pending state. */
|
|
57
|
+
busyPlanVersionId: string | null;
|
|
58
|
+
/** Why the last selection failed, if it did. Cleared on the next attempt. */
|
|
59
|
+
lastError: string | null;
|
|
60
|
+
/** Whether there is a session to bill. */
|
|
61
|
+
signedIn: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Who provides this account's plan, when it is not the account itself — a
|
|
64
|
+
* client company billed through the firm that manages it. Plans cannot be
|
|
65
|
+
* bought or changed from here then; `selectPlan` refuses without calling.
|
|
66
|
+
*/
|
|
67
|
+
managedBy: {
|
|
68
|
+
name: string | null;
|
|
69
|
+
} | null;
|
|
70
|
+
selectPlan: (plan: PublicPlan) => Promise<SelectPlanOutcome>;
|
|
71
|
+
reload: () => void;
|
|
72
|
+
}
|
|
73
|
+
export declare function usePlans(options?: UsePlansOptions): UsePlansResult;
|
|
74
|
+
//# sourceMappingURL=usePlans.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePlans.d.ts","sourceRoot":"","sources":["../../src/usePlans.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK/C;;;;;;;;;;;;;;;;GAgBG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,iBAAiB,GAC1B;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,MAAM,EAAE,eAAe,CAAA;CAAE,GAC3B;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,WAAW,eAAe;IAC/B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC9B,+DAA+D;IAC/D,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,KAAK,EAAE,OAAO,CAAC;IACf,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,sEAAsE;IACtE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,6EAA6E;IAC7E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1C,UAAU,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC7D,MAAM,EAAE,MAAM,IAAI,CAAC;CACnB;AAaD,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,cAAc,CAwJtE"}
|
package/package.json
CHANGED
|
@@ -1,52 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@base316/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Drop-in React components for Base316 authentication and billing.",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"dependencies": {
|
|
9
|
-
"@base316/sdk": "0.1.0"
|
|
10
|
-
},
|
|
11
|
-
"devDependencies": {
|
|
12
|
-
"@types/react": "19.2.18",
|
|
13
|
-
"react": "19.2.8",
|
|
14
|
-
"tsup": "^8.5.0",
|
|
15
|
-
"typescript": "7.0.2",
|
|
16
|
-
"vitest": "^5.0.0",
|
|
17
|
-
"@repo/tsconfig": "0.0.0"
|
|
18
|
-
},
|
|
19
|
-
"peerDependencies": {
|
|
20
|
-
"react": ">=18"
|
|
21
|
-
},
|
|
5
|
+
"homepage": "https://base316.com",
|
|
6
|
+
"bugs": "https://github.com/ThinkfleetAI/base316/issues",
|
|
22
7
|
"license": "MIT",
|
|
23
8
|
"repository": {
|
|
24
9
|
"type": "git",
|
|
25
10
|
"url": "git+https://github.com/ThinkfleetAI/base316.git",
|
|
26
11
|
"directory": "packages/react"
|
|
27
12
|
},
|
|
28
|
-
"
|
|
29
|
-
|
|
13
|
+
"files": [
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"CHANGELOG.md",
|
|
16
|
+
"README.md",
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"main": "./dist/index.cjs",
|
|
30
21
|
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
31
23
|
"exports": {
|
|
32
24
|
".": {
|
|
33
25
|
"types": "./dist/index.d.ts",
|
|
34
26
|
"import": "./dist/index.js",
|
|
35
27
|
"require": "./dist/index.cjs"
|
|
36
|
-
}
|
|
28
|
+
},
|
|
29
|
+
"./manifest.json": "./dist/manifest.json"
|
|
37
30
|
},
|
|
38
|
-
"files": [
|
|
39
|
-
"LICENSE",
|
|
40
|
-
"README.md",
|
|
41
|
-
"dist"
|
|
42
|
-
],
|
|
43
31
|
"publishConfig": {
|
|
44
32
|
"access": "public"
|
|
45
33
|
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@base316/sdk": "0.3.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@testing-library/dom": "^10.4.2",
|
|
39
|
+
"@testing-library/react": "^16.3.3",
|
|
40
|
+
"@testing-library/user-event": "^14.6.7",
|
|
41
|
+
"@types/react": "19.2.18",
|
|
42
|
+
"jsdom": "^30.0.1",
|
|
43
|
+
"react": "19.2.8",
|
|
44
|
+
"react-dom": "19.2.8",
|
|
45
|
+
"tsup": "^8.5.0",
|
|
46
|
+
"tsx": "^4.23.13",
|
|
47
|
+
"typescript": "7.0.2",
|
|
48
|
+
"vitest": "^5.0.0",
|
|
49
|
+
"@repo/tsconfig": "0.0.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react": ">=18"
|
|
53
|
+
},
|
|
46
54
|
"scripts": {
|
|
47
|
-
"build": "tsup && tsc -p tsconfig.build.json",
|
|
55
|
+
"build": "tsup && tsc -p tsconfig.build.json && tsx scripts/emit-manifest.ts",
|
|
48
56
|
"clean": "git clean -xdf .cache .turbo dist node_modules",
|
|
49
|
-
"test": "vitest run
|
|
57
|
+
"test": "vitest run",
|
|
50
58
|
"type-check": "tsc --noEmit"
|
|
51
59
|
}
|
|
52
60
|
}
|