@coinlist-co/react 0.3.0 → 0.5.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.
Files changed (71) hide show
  1. package/dist/chunk-CRACFEJ4.js +17 -0
  2. package/dist/chunk-CRACFEJ4.js.map +1 -0
  3. package/dist/{chunk-GRVV7NLV.js → chunk-N3WBC2VS.js} +218 -28
  4. package/dist/chunk-N3WBC2VS.js.map +1 -0
  5. package/dist/chunk-V6WO67RO.js +311 -0
  6. package/dist/chunk-V6WO67RO.js.map +1 -0
  7. package/dist/client/index.cjs +1393 -372
  8. package/dist/client/index.cjs.map +1 -1
  9. package/dist/client/index.d.cts +526 -10
  10. package/dist/client/index.d.ts +526 -10
  11. package/dist/client/index.js +1464 -88
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/requirement-BEO42QOr.d.cts +387 -0
  14. package/dist/requirement-BEO42QOr.d.ts +387 -0
  15. package/dist/server/index.cjs +459 -36
  16. package/dist/server/index.cjs.map +1 -1
  17. package/dist/server/index.d.cts +56 -4
  18. package/dist/server/index.d.ts +56 -4
  19. package/dist/server/index.js +69 -37
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/shared/index.cjs +375 -0
  22. package/dist/shared/index.cjs.map +1 -0
  23. package/dist/shared/index.d.cts +35 -0
  24. package/dist/shared/index.d.ts +35 -0
  25. package/dist/{client/core → shared}/index.js +38 -19
  26. package/dist/shared/index.js.map +1 -0
  27. package/package.json +7 -22
  28. package/dist/RequirementItem-BpkJQep8.d.ts +0 -37
  29. package/dist/RequirementItem-BvwsFu9e.d.cts +0 -37
  30. package/dist/chunk-GRVV7NLV.js.map +0 -1
  31. package/dist/chunk-IADBIDY6.js +0 -76
  32. package/dist/chunk-IADBIDY6.js.map +0 -1
  33. package/dist/chunk-IDCROXPS.js +0 -412
  34. package/dist/chunk-IDCROXPS.js.map +0 -1
  35. package/dist/chunk-P4DTIPEX.js +0 -623
  36. package/dist/chunk-P4DTIPEX.js.map +0 -1
  37. package/dist/chunk-V5M7SA3C.js +0 -161
  38. package/dist/chunk-V5M7SA3C.js.map +0 -1
  39. package/dist/chunk-YJQMJFA7.js +0 -16
  40. package/dist/chunk-YJQMJFA7.js.map +0 -1
  41. package/dist/client/components/index.cjs +0 -1107
  42. package/dist/client/components/index.cjs.map +0 -1
  43. package/dist/client/components/index.d.cts +0 -121
  44. package/dist/client/components/index.d.ts +0 -121
  45. package/dist/client/components/index.js +0 -586
  46. package/dist/client/components/index.js.map +0 -1
  47. package/dist/client/core/index.cjs +0 -961
  48. package/dist/client/core/index.cjs.map +0 -1
  49. package/dist/client/core/index.d.cts +0 -23
  50. package/dist/client/core/index.d.ts +0 -23
  51. package/dist/client/core/index.js.map +0 -1
  52. package/dist/client/hooks/index.cjs +0 -233
  53. package/dist/client/hooks/index.cjs.map +0 -1
  54. package/dist/client/hooks/index.d.cts +0 -33
  55. package/dist/client/hooks/index.d.ts +0 -33
  56. package/dist/client/hooks/index.js +0 -19
  57. package/dist/client/hooks/index.js.map +0 -1
  58. package/dist/coinlist-client-B_dJkbwB.d.ts +0 -283
  59. package/dist/coinlist-client-DBKa6j3f.d.cts +0 -283
  60. package/dist/newtype-yKTvFdg_.d.cts +0 -6
  61. package/dist/newtype-yKTvFdg_.d.ts +0 -6
  62. package/dist/oauth-session-DgItaMKN.d.cts +0 -56
  63. package/dist/oauth-session-DnKDI5ou.d.ts +0 -56
  64. package/dist/requirement-CxbdY8l4.d.cts +0 -202
  65. package/dist/requirement-fJsQ3f9_.d.ts +0 -202
  66. package/dist/useCoinListOffers-C5lBlzcC.d.ts +0 -27
  67. package/dist/useCoinListOffers-CguwXaCX.d.cts +0 -27
  68. package/dist/useCoinListRequirements-BECx1cIw.d.ts +0 -31
  69. package/dist/useCoinListRequirements-CX2gV9gJ.d.cts +0 -31
  70. package/dist/useCompleteCoinListOAuth-BRhAqD4_.d.cts +0 -55
  71. package/dist/useCompleteCoinListOAuth-DEZpHjd6.d.ts +0 -55
@@ -20,55 +20,112 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/client/index.ts
21
21
  var client_exports = {};
22
22
  __export(client_exports, {
23
- AuthorizationCode: () => AuthorizationCode,
24
- Blockchain: () => Blockchain,
25
23
  ChecklistStatus: () => ChecklistStatus,
26
- ClientId: () => ClientId,
27
- ClientSecret: () => ClientSecret,
28
- CodeChallenge: () => CodeChallenge,
29
- CodeVerifier: () => CodeVerifier,
24
+ CoinListClientInitializationError: () => CoinListClientInitializationError,
25
+ CoinListContext: () => CoinListContext,
30
26
  CoinListProvider: () => CoinListProvider,
31
- CreateParticipationParams: () => CreateParticipationParams,
32
- Cursor: () => Cursor,
33
- FaqItem: () => FaqItem,
34
- Link: () => Link,
35
- Milestone: () => Milestone,
36
- OAuthRefreshToken: () => OAuthRefreshToken,
37
- OAuthSession: () => OAuthSession,
38
- Offer: () => Offer,
39
- OfferDetail: () => OfferDetail,
40
- OfferId: () => OfferId,
41
- OfferOption: () => OfferOption,
42
- OfferOptionId: () => OfferOptionId,
43
- OfferOptionSlug: () => OfferOptionSlug,
44
- OfferSlug: () => OfferSlug,
45
- PaginatedResponse: () => PaginatedResponse,
46
- PaginationParams: () => PaginationParams,
47
- Participation: () => Participation,
48
- ParticipationId: () => ParticipationId,
49
- ParticipationsPaginationParams: () => ParticipationsPaginationParams,
50
- RedirectUri: () => RedirectUri,
51
- Requirement: () => Requirement,
52
- RequirementId: () => RequirementId,
27
+ CoinListSignInCard: () => CoinListSignInCard,
28
+ OAUTH_CODE_VERIFIER_KEY: () => OAUTH_CODE_VERIFIER_KEY,
29
+ OAUTH_STATE_KEY: () => OAUTH_STATE_KEY,
30
+ OfferCard: () => OfferCard,
31
+ OfferCardUi: () => OfferCardUi,
32
+ OffersGrid: () => OffersGrid,
33
+ RequirementItem: () => RequirementItem,
34
+ RequirementItemUi: () => RequirementItemUi,
53
35
  RequirementStatus: () => RequirementStatus,
54
- RequirementStatusInfo: () => RequirementStatusInfo,
55
36
  RequirementVariant: () => RequirementVariant,
56
- TermItem: () => TermItem,
57
- User: () => User,
58
- UserEmail: () => UserEmail,
59
- UserId: () => UserId,
60
- WalletAddress: () => WalletAddress,
37
+ RequirementsChecklist: () => RequirementsChecklist,
61
38
  createCoinListClient: () => createCoinListClient,
62
- fetchAllPages: () => fetchAllPages,
63
39
  useCoinList: () => useCoinList,
64
- useCoinListRequirements: () => useCoinListRequirements,
65
- useCompleteCoinListOAuth: () => useCompleteCoinListOAuth
40
+ useCompleteOAuth: () => useCompleteOAuth,
41
+ useOfferDetails: () => useOfferDetails,
42
+ useOffers: () => useOffers,
43
+ useParticipations: () => useParticipations,
44
+ useRequirements: () => useRequirements
66
45
  });
67
46
  module.exports = __toCommonJS(client_exports);
47
+ var import_styles = require("./styles.css");
68
48
 
69
49
  // src/client/CoinListProvider.tsx
70
50
  var import_react = require("react");
71
51
 
52
+ // src/shared/utils.ts
53
+ async function sha256(data) {
54
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
55
+ const buffer = bytes.buffer.slice(
56
+ bytes.byteOffset,
57
+ bytes.byteOffset + bytes.byteLength
58
+ );
59
+ return crypto.subtle.digest("SHA-256", buffer);
60
+ }
61
+ function arrayBufferToBase64Url(buffer, padding = true) {
62
+ const bytes = new Uint8Array(buffer);
63
+ let binary = "";
64
+ for (let i = 0; i < bytes.length; i++) {
65
+ binary += String.fromCharCode(bytes[i]);
66
+ }
67
+ let base64 = btoa(binary);
68
+ base64 = base64.replace(/\+/g, "-").replace(/\//g, "_");
69
+ if (!padding) {
70
+ base64 = base64.replace(/=+$/, "");
71
+ }
72
+ return base64;
73
+ }
74
+ function generateSecureRandomBase64Url(byteLength) {
75
+ const bytes = new Uint8Array(byteLength);
76
+ crypto.getRandomValues(bytes);
77
+ const buffer = bytes.buffer;
78
+ return arrayBufferToBase64Url(buffer, false);
79
+ }
80
+ function getUUIDv4() {
81
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
82
+ return crypto.randomUUID();
83
+ }
84
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
85
+ const bytes = new Uint8Array(16);
86
+ crypto.getRandomValues(bytes);
87
+ bytes[6] = bytes[6] & 15 | 64;
88
+ bytes[8] = bytes[8] & 63 | 128;
89
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
90
+ return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
91
+ }
92
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 12)}`;
93
+ }
94
+ function notBlankStringOrNull(value) {
95
+ if (value?.trim()) {
96
+ return value;
97
+ } else {
98
+ return null;
99
+ }
100
+ }
101
+
102
+ // src/client/core/api/middleware/device-id.ts
103
+ var DEVICE_ID_KEY = "coinlist.device_id";
104
+ function getDeviceId() {
105
+ let deviceId = localStorage.getItem(DEVICE_ID_KEY);
106
+ if (!deviceId) {
107
+ deviceId = getUUIDv4();
108
+ localStorage.setItem(DEVICE_ID_KEY, deviceId);
109
+ }
110
+ return deviceId;
111
+ }
112
+
113
+ // src/shared/api/frontline/config.ts
114
+ var PUBLIC_API_BASE_URL = "https://api.coinlist.co";
115
+ var HEADER_API_VERSION = "X-API-Version";
116
+ var HEADER_USER_AGENT = "User-Agent";
117
+ var HEADER_IDEMPOTENCY_KEY = "Idempotency-Key";
118
+ var API_VERSION = "2025-10-17";
119
+ var COINLIST_BASE_URL = "https://coinlist.co";
120
+ var OAUTH_PAGE_PATH = "/oauth/authorize";
121
+ var SUPPORT_NEW_TICKET_URL = "https://support.coinlist.co/support/tickets/new";
122
+ var VERIFY_IDENTITY_PATH = "/verify-identity";
123
+ var VERIFY_IDENTITY_VERIFIED_PATH = "/verify-identity/identity_verified";
124
+ var VERIFY_IDENTITY_PROOF_OF_ADDRESS_PATH = "/verify-identity/proof_of_address";
125
+ var VERIFY_IDENTITY_SOURCE_OF_FUNDS_PATH = "/verify-identity/source_of_funds";
126
+ var VERIFY_IDENTITY_ACCREDITATION_PATH = "/verify-identity/accreditation_full";
127
+ var WALLET_PATH = "/wallet";
128
+
72
129
  // src/shared/api/http-attributes.ts
73
130
  var empty = {};
74
131
  var concat = (left, right) => ({
@@ -114,24 +171,28 @@ var Attributes = {
114
171
  wasRenewAttempted
115
172
  };
116
173
 
117
- // src/client/core/api/middleware/attach-session-middleware.ts
118
- function attachSessionMiddleware(fetchAccessToken) {
119
- return async (request) => {
120
- if (!Attributes.isProtected(request.attributes)) {
121
- return request;
122
- }
123
- const accessToken = await fetchAccessToken(false);
124
- if (accessToken === null) {
125
- return request;
174
+ // src/client/core/api/middleware/user-agent.ts
175
+ var userAgentMiddleware = async (request) => {
176
+ if (!Attributes.needUserAgent(request.attributes)) {
177
+ return request;
178
+ }
179
+ const userAgent2 = generateUserAgent();
180
+ return {
181
+ ...request,
182
+ headers: {
183
+ ...request.headers ?? {},
184
+ [HEADER_USER_AGENT]: userAgent2
126
185
  }
127
- return {
128
- ...request,
129
- headers: {
130
- ...request.headers ?? {},
131
- Authorization: `Bearer ${accessToken.value}`
132
- }
133
- };
134
186
  };
187
+ };
188
+ function generateUserAgent() {
189
+ const appInfo = `CoinList/1.0`;
190
+ const deviceInfo = "Web Browser";
191
+ const devicePlatform = "DevicePlatform web";
192
+ const deviceAppId = "DeviceAppID co.coinlist.sdk";
193
+ const deviceId = getDeviceId();
194
+ const finalDeviceId = `DeviceID ${deviceId}`;
195
+ return `${appInfo} (${deviceInfo}; ${devicePlatform}; ${deviceAppId}; ${finalDeviceId})`;
135
196
  }
136
197
 
137
198
  // src/shared/api/http.ts
@@ -232,124 +293,6 @@ var Request = {
232
293
  concatAttributes
233
294
  };
234
295
 
235
- // src/client/core/api/middleware/session-renewal.ts
236
- function renewSessionMiddleware(fetchAccessToken) {
237
- return async ({ request, response, retry }) => {
238
- if (response.status !== 401) {
239
- return response;
240
- }
241
- if (!Attributes.isProtected(request.attributes)) {
242
- return response;
243
- }
244
- if (Attributes.wasRenewAttempted(request.attributes)) {
245
- return response;
246
- }
247
- const newToken = await fetchAccessToken(true);
248
- if (newToken) {
249
- const nextRequest = Request.concatAttributes(
250
- request,
251
- Attributes.renewAttempted(true)
252
- );
253
- return retry(nextRequest);
254
- } else {
255
- return response;
256
- }
257
- };
258
- }
259
-
260
- // src/shared/utils.ts
261
- async function sha256(data) {
262
- const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
263
- const buffer = bytes.buffer.slice(
264
- bytes.byteOffset,
265
- bytes.byteOffset + bytes.byteLength
266
- );
267
- return crypto.subtle.digest("SHA-256", buffer);
268
- }
269
- function arrayBufferToBase64Url(buffer, padding = true) {
270
- const bytes = new Uint8Array(buffer);
271
- let binary = "";
272
- for (let i = 0; i < bytes.length; i++) {
273
- binary += String.fromCharCode(bytes[i]);
274
- }
275
- let base64 = btoa(binary);
276
- base64 = base64.replace(/\+/g, "-").replace(/\//g, "_");
277
- if (!padding) {
278
- base64 = base64.replace(/=+$/, "");
279
- }
280
- return base64;
281
- }
282
- function generateSecureRandomBase64Url(byteLength) {
283
- const bytes = new Uint8Array(byteLength);
284
- crypto.getRandomValues(bytes);
285
- const buffer = bytes.buffer;
286
- return arrayBufferToBase64Url(buffer, false);
287
- }
288
- function getUUIDv4() {
289
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
290
- return crypto.randomUUID();
291
- }
292
- if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
293
- const bytes = new Uint8Array(16);
294
- crypto.getRandomValues(bytes);
295
- bytes[6] = bytes[6] & 15 | 64;
296
- bytes[8] = bytes[8] & 63 | 128;
297
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
298
- return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
299
- }
300
- return `${Date.now()}-${Math.random().toString(36).slice(2, 12)}`;
301
- }
302
- function notBlankStringOrNull(value) {
303
- if (value?.trim()) {
304
- return value;
305
- } else {
306
- return null;
307
- }
308
- }
309
-
310
- // src/client/core/api/middleware/device-id.ts
311
- var DEVICE_ID_KEY = "coinlist.device_id";
312
- function getDeviceId() {
313
- let deviceId = localStorage.getItem(DEVICE_ID_KEY);
314
- if (!deviceId) {
315
- deviceId = getUUIDv4();
316
- localStorage.setItem(DEVICE_ID_KEY, deviceId);
317
- }
318
- return deviceId;
319
- }
320
-
321
- // src/shared/api/frontline.ts
322
- var PUBLIC_API_BASE_URL = "https://api.coinlist.co";
323
- var HEADER_API_VERSION = "X-API-Version";
324
- var HEADER_USER_AGENT = "User-Agent";
325
- var HEADER_IDEMPOTENCY_KEY = "Idempotency-Key";
326
- var API_VERSION = "2025-10-17";
327
- var COINLIST_OAUTH_PAGE_URL = "https://coinlist.co/oauth/authorize";
328
-
329
- // src/client/core/api/middleware/user-agent.ts
330
- var userAgentMiddleware = async (request) => {
331
- if (!Attributes.needUserAgent(request.attributes)) {
332
- return request;
333
- }
334
- const userAgent2 = generateUserAgent();
335
- return {
336
- ...request,
337
- headers: {
338
- ...request.headers ?? {},
339
- [HEADER_USER_AGENT]: userAgent2
340
- }
341
- };
342
- };
343
- function generateUserAgent() {
344
- const appInfo = `CoinList/1.0`;
345
- const deviceInfo = "Web Browser";
346
- const devicePlatform = "DevicePlatform web";
347
- const deviceAppId = "DeviceAppID co.coinlist.sdk";
348
- const deviceId = getDeviceId();
349
- const finalDeviceId = `DeviceID ${deviceId}`;
350
- return `${appInfo} (${deviceInfo}; ${devicePlatform}; ${deviceAppId}; ${finalDeviceId})`;
351
- }
352
-
353
296
  // src/shared/api/http-client.ts
354
297
  var HttpClient = class {
355
298
  constructor(config, middleware = {}) {
@@ -419,6 +362,26 @@ var HttpClient = class {
419
362
  }
420
363
  };
421
364
 
365
+ // src/shared/api/middleware/attach-session-middleware.ts
366
+ function attachSessionMiddleware(fetchAccessToken) {
367
+ return async (request) => {
368
+ if (!Attributes.isProtected(request.attributes)) {
369
+ return request;
370
+ }
371
+ const accessToken = await fetchAccessToken(false);
372
+ if (accessToken === null) {
373
+ return request;
374
+ }
375
+ return {
376
+ ...request,
377
+ headers: {
378
+ ...request.headers ?? {},
379
+ Authorization: `Bearer ${accessToken.value}`
380
+ }
381
+ };
382
+ };
383
+ }
384
+
422
385
  // src/shared/api/middleware/idempotency-key.ts
423
386
  var idempotencyKeyMiddleware = async (request) => {
424
387
  if (!Attributes.isIdempotent(request.attributes)) {
@@ -472,31 +435,66 @@ function defaultDelay(ms) {
472
435
  }
473
436
  var requestRetryMiddleware = createRequestRetryMiddleware();
474
437
 
475
- // src/client/core/api/api-client.ts
476
- var ApiClient = class {
477
- constructor(config, fetchAccessToken) {
478
- this.config = config;
479
- this.fetchAccessToken = fetchAccessToken;
480
- this.httpClient = new HttpClient(this.config, {
481
- beforeRequest: [
482
- attachSessionMiddleware(this.fetchAccessToken),
483
- userAgentMiddleware,
484
- idempotencyKeyMiddleware
485
- ],
486
- afterRequest: [
487
- renewSessionMiddleware(this.fetchAccessToken),
488
- requestRetryMiddleware
489
- ]
490
- });
491
- }
492
- async send(request) {
493
- const response = await this.httpClient.send(request);
494
- if (response.status >= 200 && response.status < 300) {
495
- return response.body;
496
- } else {
497
- throw new HttpError(response);
498
- }
499
- }
438
+ // src/shared/api/middleware/session-renewal.ts
439
+ function renewSessionMiddleware(fetchAccessToken) {
440
+ return async ({ request, response, retry }) => {
441
+ if (response.status !== 401) {
442
+ return response;
443
+ }
444
+ if (!Attributes.isProtected(request.attributes)) {
445
+ return response;
446
+ }
447
+ if (Attributes.wasRenewAttempted(request.attributes)) {
448
+ return response;
449
+ }
450
+ const newToken = await fetchAccessToken(true);
451
+ if (newToken) {
452
+ const nextRequest = Request.concatAttributes(
453
+ request,
454
+ Attributes.renewAttempted(true)
455
+ );
456
+ return retry(nextRequest);
457
+ } else {
458
+ return response;
459
+ }
460
+ };
461
+ }
462
+
463
+ // src/shared/api/authenticated-api-client.ts
464
+ var AuthenticatedApiClient = class {
465
+ constructor(config, fetchAccessToken, additionalBeforeRequest = []) {
466
+ this.httpClient = new HttpClient(config, {
467
+ beforeRequest: [
468
+ attachSessionMiddleware(fetchAccessToken),
469
+ ...additionalBeforeRequest,
470
+ idempotencyKeyMiddleware
471
+ ],
472
+ afterRequest: [
473
+ renewSessionMiddleware(fetchAccessToken),
474
+ requestRetryMiddleware
475
+ ]
476
+ });
477
+ }
478
+ async send(request) {
479
+ const response = await this.httpClient.send(request);
480
+ if (response.status >= 200 && response.status < 300) {
481
+ return response.body;
482
+ } else {
483
+ throw new HttpError(response);
484
+ }
485
+ }
486
+ };
487
+
488
+ // src/client/core/api/api-client.ts
489
+ var ApiClient = class {
490
+ constructor(config, fetchAccessToken) {
491
+ this.client = new AuthenticatedApiClient(config, fetchAccessToken, [
492
+ userAgentMiddleware
493
+ ]);
494
+ }
495
+ async send(request) {
496
+ return this.client.send(request);
497
+ }
500
498
  };
501
499
 
502
500
  // src/shared/api/pagination.ts
@@ -538,22 +536,6 @@ var PaginationParams = {
538
536
  }
539
537
  };
540
538
 
541
- // src/shared/types/errors.ts
542
- var NotAuthenticatedError = class extends Error {
543
- constructor(message = "The user is not authenticated. Go through the OAuth flow first!") {
544
- super(message);
545
- this.name = "NotAuthenticatedError";
546
- }
547
- };
548
-
549
- // src/shared/types/oauth.ts
550
- var AuthorizationCode = (value) => value;
551
- var CodeVerifier = (value) => value;
552
- var CodeChallenge = (value) => value;
553
- var RedirectUri = (value) => value;
554
- var ClientId = (value) => value;
555
- var ClientSecret = (value) => value;
556
-
557
539
  // src/shared/types/offer.ts
558
540
  var OfferId = (value) => value;
559
541
  var OfferSlug = (value) => value;
@@ -663,6 +645,29 @@ var Milestone = {
663
645
  })
664
646
  };
665
647
 
648
+ // src/shared/api/frontline/offers.ts
649
+ async function fetchOffers(api) {
650
+ return fetchAllPages((params) => fetchOffersPage(api, params));
651
+ }
652
+ async function fetchOffersPage(api, params) {
653
+ const queryParams = PaginationParams.toQueryParams(params);
654
+ const pageDto = await api.send({
655
+ method: "GET",
656
+ url: "/v1/offers",
657
+ queryParams,
658
+ attributes: Attributes.protected()
659
+ });
660
+ return PaginatedResponse.fromDto(pageDto, Offer.fromDto);
661
+ }
662
+ async function fetchOfferDetails(api, id) {
663
+ const dto = await api.send({
664
+ method: "GET",
665
+ url: `/v1/offers/${id}`,
666
+ attributes: Attributes.protected()
667
+ });
668
+ return OfferDetail.fromDto(dto);
669
+ }
670
+
666
671
  // src/shared/types/participation.ts
667
672
  var ParticipationId = (value) => value;
668
673
  var Blockchain = (value) => value;
@@ -710,6 +715,40 @@ var CreateParticipationParams = {
710
715
  })
711
716
  };
712
717
 
718
+ // src/shared/api/frontline/participations.ts
719
+ async function fetchParticipations(api, offerId) {
720
+ return fetchAllPages(
721
+ (params) => fetchParticipationsPage(api, params),
722
+ { offerId }
723
+ );
724
+ }
725
+ async function fetchParticipationsPage(api, params) {
726
+ const pageDto = await api.send({
727
+ method: "GET",
728
+ url: "/v1/participations",
729
+ queryParams: ParticipationsPaginationParams.toQueryParams(params),
730
+ attributes: Attributes.protected()
731
+ });
732
+ return PaginatedResponse.fromDto(pageDto, Participation.fromDto);
733
+ }
734
+ async function fetchParticipation(api, id) {
735
+ const dto = await api.send({
736
+ method: "GET",
737
+ url: `/v1/participations/${id}`,
738
+ attributes: Attributes.protected()
739
+ });
740
+ return Participation.fromDto(dto);
741
+ }
742
+ async function createParticipation(api, params) {
743
+ const dto = await api.send({
744
+ method: "POST",
745
+ url: "/v1/participations",
746
+ body: CreateParticipationParams.toDto(params),
747
+ attributes: Attributes.protected()
748
+ });
749
+ return Participation.fromDto(dto);
750
+ }
751
+
713
752
  // src/shared/types/requirement.ts
714
753
  var RequirementId = (value) => value;
715
754
  var Requirement = {
@@ -726,6 +765,41 @@ var RequirementStatusInfo = {
726
765
  }))
727
766
  };
728
767
 
768
+ // src/shared/api/frontline/requirements.ts
769
+ async function fetchOfferRequirements(api, offerId) {
770
+ const response = await api.send({
771
+ method: "GET",
772
+ url: `/v1/offers/${offerId}/requirements`,
773
+ attributes: Attributes.protected()
774
+ });
775
+ return Object.fromEntries(
776
+ Object.entries(response.options).map(([optionId, list]) => [
777
+ optionId,
778
+ list.data.map(Requirement.fromDto)
779
+ ])
780
+ );
781
+ }
782
+ async function fetchRequirementStatuses(api, offerId) {
783
+ const response = await api.send({
784
+ method: "GET",
785
+ url: `/v1/offers/${offerId}/requirements/statuses`,
786
+ attributes: Attributes.protected()
787
+ });
788
+ return RequirementStatusInfo.fromStatusesDto(response);
789
+ }
790
+
791
+ // src/shared/types/errors.ts
792
+ var NotAuthenticatedError = class extends Error {
793
+ constructor(message = "The user is not authenticated. Go through the OAuth flow first!") {
794
+ super(message);
795
+ this.name = "NotAuthenticatedError";
796
+ }
797
+ };
798
+
799
+ // src/shared/types/oauth.ts
800
+ var AuthorizationCode = (value) => value;
801
+ var CodeVerifier = (value) => value;
802
+
729
803
  // src/client/core/coinlist-client.ts
730
804
  var OAUTH_STATE_KEY = "coinlist.oauth_state";
731
805
  var OAUTH_CODE_VERIFIER_KEY = "coinlist.oauth_code_verifier";
@@ -778,8 +852,8 @@ var CoinListClientImpl = class {
778
852
  code_challenge_method: "S256",
779
853
  state
780
854
  });
781
- const oauthPageUrl = this._config.authorizationPageUrl ?? COINLIST_OAUTH_PAGE_URL;
782
- const url = `${oauthPageUrl}?${params.toString()}`;
855
+ const baseUrl = this._config.coinlistBaseUrl ?? COINLIST_BASE_URL;
856
+ const url = `${baseUrl}${OAUTH_PAGE_PATH}?${params.toString()}`;
783
857
  window.location.href = url;
784
858
  }
785
859
  completeOAuth() {
@@ -813,93 +887,78 @@ var CoinListClientImpl = class {
813
887
  logout() {
814
888
  this._accessToken = null;
815
889
  }
816
- async fetchAllOffers() {
890
+ async fetchOffers() {
817
891
  this.ensureAuthenticated();
818
- return await fetchAllPages(this.fetchOffersPage.bind(this));
892
+ return fetchOffers(this.api);
819
893
  }
820
894
  async fetchOffersPage(params) {
821
895
  this.ensureAuthenticated();
822
- const queryParams = PaginationParams.toQueryParams(params);
823
- if (this._config.sandbox) {
824
- queryParams.sandbox = true;
825
- }
826
- const pageDto = await this.api.send({
827
- method: "GET",
828
- url: "/v1/offers",
829
- queryParams,
830
- attributes: Attributes.protected()
831
- });
832
- return PaginatedResponse.fromDto(pageDto, Offer.fromDto);
896
+ return fetchOffersPage(this.api, params);
833
897
  }
834
898
  async fetchOfferDetails(id) {
835
899
  this.ensureAuthenticated();
836
- const offerDetailDto = await this.api.send({
837
- method: "GET",
838
- url: `/v1/offers/${id}`,
839
- attributes: Attributes.protected()
840
- });
841
- return OfferDetail.fromDto(offerDetailDto);
900
+ return fetchOfferDetails(this.api, id);
842
901
  }
843
- async fetchAllParticipations(offerId) {
902
+ async fetchParticipations(offerId) {
844
903
  this.ensureAuthenticated();
845
- return await fetchAllPages(
846
- this.fetchParticipationsPage.bind(this),
847
- { offerId }
848
- );
904
+ return fetchParticipations(this.api, offerId);
849
905
  }
850
906
  async fetchParticipationsPage(params) {
851
907
  this.ensureAuthenticated();
852
- const pageDto = await this.api.send(
853
- {
854
- method: "GET",
855
- url: "/v1/participations",
856
- queryParams: ParticipationsPaginationParams.toQueryParams(params),
857
- attributes: Attributes.protected()
858
- }
859
- );
860
- return PaginatedResponse.fromDto(pageDto, Participation.fromDto);
908
+ return fetchParticipationsPage(this.api, params);
861
909
  }
862
910
  async fetchParticipation(id) {
863
911
  this.ensureAuthenticated();
864
- const participationDto = await this.api.send({
865
- method: "GET",
866
- url: `/v1/participations/${id}`,
867
- attributes: Attributes.protected()
868
- });
869
- return Participation.fromDto(participationDto);
912
+ return fetchParticipation(this.api, id);
870
913
  }
871
914
  async createParticipation(params) {
872
915
  this.ensureAuthenticated();
873
- const participationDto = await this.api.send({
874
- method: "POST",
875
- url: "/v1/participations",
876
- body: CreateParticipationParams.toDto(params),
877
- attributes: Attributes.protected()
878
- });
879
- return Participation.fromDto(participationDto);
916
+ return createParticipation(this.api, params);
880
917
  }
881
918
  async fetchOfferRequirements(offerId) {
882
919
  this.ensureAuthenticated();
883
- const response = await this.api.send({
884
- method: "GET",
885
- url: `/v1/offers/${offerId}/requirements`,
886
- attributes: Attributes.protected()
887
- });
888
- return Object.fromEntries(
889
- Object.entries(response.options).map(([optionId, list]) => [
890
- optionId,
891
- list.data.map(Requirement.fromDto)
892
- ])
893
- );
920
+ return fetchOfferRequirements(this.api, offerId);
894
921
  }
895
922
  async fetchRequirementStatuses(offerId) {
896
923
  this.ensureAuthenticated();
897
- const response = await this.api.send({
898
- method: "GET",
899
- url: `/v1/offers/${offerId}/requirements/statuses`,
900
- attributes: Attributes.protected()
901
- });
902
- return RequirementStatusInfo.fromStatusesDto(response);
924
+ return fetchRequirementStatuses(this.api, offerId);
925
+ }
926
+ handleRequirement(requirement) {
927
+ const baseUrl = this._config.coinlistBaseUrl ?? COINLIST_BASE_URL;
928
+ let path;
929
+ switch (requirement.type) {
930
+ case "kyc_approved":
931
+ path = VERIFY_IDENTITY_PATH;
932
+ break;
933
+ case "identity_verified":
934
+ path = VERIFY_IDENTITY_VERIFIED_PATH;
935
+ break;
936
+ case "proof_of_address":
937
+ path = VERIFY_IDENTITY_PROOF_OF_ADDRESS_PATH;
938
+ break;
939
+ case "source_of_funds":
940
+ path = VERIFY_IDENTITY_SOURCE_OF_FUNDS_PATH;
941
+ break;
942
+ case "accreditation":
943
+ path = VERIFY_IDENTITY_ACCREDITATION_PATH;
944
+ break;
945
+ case "external_wallet":
946
+ case "whitelisted_wallet":
947
+ path = WALLET_PATH;
948
+ break;
949
+ case "jurisdiction":
950
+ path = null;
951
+ break;
952
+ default: {
953
+ const _exhaustive = requirement.type;
954
+ return;
955
+ }
956
+ }
957
+ if (path === null) return;
958
+ openInNewTab(new URL(path, baseUrl).toString());
959
+ }
960
+ contactSupport() {
961
+ openInNewTab(SUPPORT_NEW_TICKET_URL);
903
962
  }
904
963
  ensureAuthenticated() {
905
964
  if (this.getAuthState() !== "logged-in") {
@@ -907,6 +966,9 @@ var CoinListClientImpl = class {
907
966
  }
908
967
  }
909
968
  };
969
+ function openInNewTab(url) {
970
+ window.open(url, "_blank", "noopener,noreferrer");
971
+ }
910
972
  function createCoinListClient(config) {
911
973
  return new CoinListClientImpl(config);
912
974
  }
@@ -940,9 +1002,649 @@ function CoinListProvider({ config, children }) {
940
1002
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CoinListContext.Provider, { value, children });
941
1003
  }
942
1004
 
943
- // src/client/components/requirements/RequirementItem.tsx
1005
+ // src/client/components/design-system/AlertBanner.tsx
944
1006
  var import_lucide_react = require("lucide-react");
1007
+
1008
+ // src/client/components/design-system/tokens/typography.ts
1009
+ var typeClasses = {
1010
+ hero: "text-[64px] leading-[110%] font-semibold",
1011
+ display: "text-[48px] leading-[110%] font-semibold",
1012
+ h1: "text-[36px] leading-[120%] font-semibold",
1013
+ h2: "text-[28px] leading-[125%] font-semibold",
1014
+ h3: "text-[22px] leading-[130%] font-semibold",
1015
+ h4: "text-[18px] leading-[135%] font-medium",
1016
+ body: "text-[16px] leading-[150%] font-normal",
1017
+ subhead: "text-[14px] leading-[150%] font-normal",
1018
+ caption: "text-[12px] leading-[140%] font-normal",
1019
+ label: "text-[14px] leading-[140%] font-medium",
1020
+ data: "text-[14px] leading-[140%] font-normal"
1021
+ };
1022
+
1023
+ // src/client/components/design-system/utils.ts
1024
+ var import_clsx = require("clsx");
1025
+ function cn(...inputs) {
1026
+ return (0, import_clsx.clsx)(inputs);
1027
+ }
1028
+
1029
+ // src/client/components/design-system/AlertBanner.tsx
945
1030
  var import_jsx_runtime2 = require("react/jsx-runtime");
1031
+ var variantConfig = {
1032
+ info: {
1033
+ container: "bg-surface border-border",
1034
+ text: "text-text-secondary",
1035
+ Icon: import_lucide_react.Info
1036
+ },
1037
+ success: {
1038
+ container: "bg-success-subtle border-success-border",
1039
+ text: "text-success",
1040
+ Icon: import_lucide_react.CircleCheck
1041
+ },
1042
+ warning: {
1043
+ container: "bg-warning-subtle border-warning-border",
1044
+ text: "text-warning",
1045
+ Icon: import_lucide_react.Info
1046
+ },
1047
+ error: {
1048
+ container: "bg-error-subtle border-error-border",
1049
+ text: "text-error",
1050
+ Icon: import_lucide_react.Info
1051
+ }
1052
+ };
1053
+ function AlertBanner({
1054
+ variant,
1055
+ content,
1056
+ icon = false,
1057
+ className
1058
+ }) {
1059
+ const { container, text, Icon } = variantConfig[variant];
1060
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1061
+ "div",
1062
+ {
1063
+ className: cn(
1064
+ "flex items-center rounded-btn border p-3",
1065
+ icon && "gap-2",
1066
+ container,
1067
+ className
1068
+ ),
1069
+ children: [
1070
+ icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { className: cn("h-4 w-4 shrink-0", text), "aria-hidden": "true" }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: cn(typeClasses.subhead, text), children: content })
1072
+ ]
1073
+ }
1074
+ );
1075
+ }
1076
+
1077
+ // src/client/components/design-system/CloseButton.tsx
1078
+ var import_lucide_react2 = require("lucide-react");
1079
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1080
+ function CloseButton({
1081
+ onClick,
1082
+ "aria-label": ariaLabel = "Close",
1083
+ className
1084
+ }) {
1085
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1086
+ "button",
1087
+ {
1088
+ type: "button",
1089
+ onClick,
1090
+ "aria-label": ariaLabel,
1091
+ className: cn(
1092
+ "cursor-pointer rounded-full p-1.5 text-text-secondary transition-colors hover:bg-surface hover:text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-400 focus-visible:ring-offset-2",
1093
+ className
1094
+ ),
1095
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { className: "h-5 w-5" })
1096
+ }
1097
+ );
1098
+ }
1099
+
1100
+ // src/client/components/design-system/logos/CoinListWordmark.tsx
1101
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1102
+ function CoinListWordmark(props) {
1103
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1104
+ "svg",
1105
+ {
1106
+ width: "638",
1107
+ height: "125",
1108
+ viewBox: "0 0 638 125",
1109
+ fill: "none",
1110
+ xmlns: "http://www.w3.org/2000/svg",
1111
+ "aria-hidden": "true",
1112
+ ...props,
1113
+ children: [
1114
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1115
+ "path",
1116
+ {
1117
+ d: "M68.7724 123.966C74.8226 124.172 80.7696 123.519 86.5104 121.799C97.4076 118.531 106.551 112.58 113.942 103.947C113.942 103.947 113.942 103.947 113.908 103.912C111.879 102.193 109.851 100.507 107.857 98.8218L105.829 97.1363C105.451 96.8268 105.107 96.5172 104.729 96.2076C103.939 95.5197 103.148 94.8662 102.323 94.2126C95.3107 98.8562 87.6104 101.539 79.1196 101.883C74.8569 102.055 70.6287 101.642 66.5036 100.507C59.5252 98.6154 53.2688 95.1757 48.2499 89.7755C39.1059 79.938 35.8745 68.1743 38.2465 55.0348C39.7246 46.9172 43.4373 40.0723 49.1093 34.5C47.9061 34.7408 46.703 35.016 45.5342 35.3943C36.5277 38.2148 29.6181 43.6839 24.6336 51.6639C20.6803 58.0273 18.5834 64.9411 18.3428 72.4739C18.2053 76.7047 18.4803 80.7979 19.4428 84.7879C22.5023 97.2395 29.3087 107.043 39.6903 114.335H39.7934V114.403C40.1372 114.644 40.4809 114.919 40.8247 115.16C49.2812 120.801 58.5971 123.691 68.7724 124.035V123.966Z",
1118
+ fill: "currentColor"
1119
+ }
1120
+ ),
1121
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1122
+ "path",
1123
+ {
1124
+ d: "M72.2101 89.052C68.1881 90.8406 64.0286 91.8725 59.6973 92.2165C64.9912 95.209 70.8007 96.688 77.1259 96.8256C79.1885 96.86 81.251 96.6536 83.4167 96.3097C88.745 95.4841 93.6951 93.5579 98.3015 90.7374C93.9358 87.057 89.6044 83.4109 85.2386 79.7305C81.526 83.6861 77.1946 86.8506 72.2101 89.052Z",
1125
+ fill: "currentColor"
1126
+ }
1127
+ ),
1128
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1129
+ "path",
1130
+ {
1131
+ d: "M66.9848 0.241609C66.4348 0.276006 65.8848 0.344799 65.3691 0.413592C70.663 1.4111 75.7851 3.13093 80.6321 5.71068C90.0511 10.7326 97.3732 17.9215 102.289 27.4494L104.936 25.1448C107.652 22.7714 110.368 20.3981 113.084 18.0247C106.655 11.1797 99.092 6.15784 90.223 3.06214C84.3103 0.998336 78.2601 -0.0335643 72.0381 0.000832319C70.388 0.000832319 68.7036 0.138419 66.9848 0.241609Z",
1132
+ fill: "currentColor"
1133
+ }
1134
+ ),
1135
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1136
+ "path",
1137
+ {
1138
+ d: "M25.4242 107.871C17.4833 97.9988 13.1519 86.7167 13.2207 73.818C13.2207 67.317 14.5614 61.0568 17.1396 55.1062C20.9553 46.2662 26.9711 39.1805 35.4276 34.2962C43.3685 29.687 51.9968 27.8984 61.1752 29.2743C70.5943 30.7189 78.3976 35.2249 84.6541 42.2762C84.6541 42.2762 84.8603 42.5514 84.9634 42.6546C85.8916 41.8291 86.8541 41.0035 87.7823 40.178L88.9167 39.1805C89.8792 38.355 90.8417 37.4951 91.8043 36.6696C94.0043 34.7434 96.17 32.8515 98.3357 30.9253C94.1075 22.223 87.7479 15.55 79.3258 10.8377C69.3911 5.26541 58.7002 3.54557 47.3904 5.02463C41.7528 5.78136 36.3557 7.50119 31.1993 10.0465C13.702 18.7145 1.08597 36.532 0.0546833 58.4771C-0.117196 62.0887 0.123436 65.7348 0.673452 69.3464C2.04849 78.3239 5.34859 86.4759 10.6425 93.8368C14.7676 99.5123 19.6834 104.19 25.3898 107.871H25.4242Z",
1139
+ fill: "currentColor"
1140
+ }
1141
+ ),
1142
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1143
+ "path",
1144
+ {
1145
+ fillRule: "evenodd",
1146
+ clipRule: "evenodd",
1147
+ d: "M366.034 58.1654C366.034 46.8145 375.178 37.665 386.419 37.665C397.66 37.665 406.804 46.8145 406.804 58.1654V99.3727H420.727V58.1654C420.727 39.1785 405.395 23.7344 386.419 23.7344C367.444 23.7344 352.112 39.1785 352.112 58.1654V99.3727H366.034V58.1654ZM278.616 38.1466C265.141 38.1466 254.244 49.0503 254.244 62.5338C254.244 76.0173 265.141 86.9211 278.616 86.9211C292.092 86.9211 302.989 76.0173 302.989 62.5338C302.989 49.0503 292.092 38.1466 278.616 38.1466ZM240.321 62.5338C240.321 41.3799 257.475 24.2159 278.616 24.2159C299.757 24.2159 316.911 41.3799 316.911 62.5338C316.911 83.6878 299.757 100.852 278.616 100.852C257.475 100.852 240.321 83.6878 240.321 62.5338ZM208.386 38.2154C195.082 37.9402 184.048 48.5 183.738 61.8115C183.463 75.123 194.017 86.1643 207.32 86.4739C215.192 86.6459 222.274 82.9998 226.812 77.2212L237.777 85.8204C230.662 94.9011 219.455 100.645 207.011 100.37C186.007 99.923 169.335 82.5183 169.782 61.5019C170.229 40.4856 187.623 23.8032 208.627 24.2503C219.524 24.4911 229.287 29.3066 236.059 36.8051L225.712 46.1266C221.415 41.3455 215.227 38.3186 208.317 38.1466L208.386 38.2154ZM340.527 25.2134V99.3727H326.605V25.2134H340.527ZM508.282 25.2134H494.36V99.8542H508.282V25.2134ZM483.154 99.8542H448.846C439.221 99.8542 431.452 92.0462 431.452 82.4495V25.2134H445.374V82.4495C445.374 84.3757 446.921 85.9236 448.846 85.9236H483.154V99.8542ZM583.772 25.2134H567.856H542.83C530.661 25.2134 520.933 35.1885 520.933 47.2273C520.933 59.2661 530.661 69.2412 542.83 69.2412H552.421C556.821 69.2412 560.396 72.8184 560.396 77.1868V77.9435C560.396 82.3463 556.821 85.8892 552.421 85.8892H520.451V99.8198H552.421C564.522 99.8198 574.319 90.0168 574.319 77.9091V77.1524C574.319 65.0448 564.522 55.2417 552.421 55.2417H542.83C538.499 55.2417 534.855 51.6301 534.855 47.1585C534.855 42.6869 538.499 39.0753 542.83 39.0753H574.319H583.772C591.472 39.0753 597.694 45.3011 597.694 53.0059V99.3039H611.617V53.0059C611.617 37.6306 599.138 25.1446 583.772 25.1446V25.2134ZM637.021 39.1441H609.038V25.2134H637.021V39.1441Z",
1148
+ fill: "currentColor"
1149
+ }
1150
+ )
1151
+ ]
1152
+ }
1153
+ );
1154
+ }
1155
+
1156
+ // src/client/components/design-system/PoweredByCoinList.tsx
1157
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1158
+ function PoweredByCoinList({ className }) {
1159
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1160
+ "div",
1161
+ {
1162
+ className: cn(
1163
+ "flex items-center justify-center gap-[5px] text-text-secondary",
1164
+ className
1165
+ ),
1166
+ children: [
1167
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: cn(typeClasses.caption, "tracking-[0.12px]"), children: "Powered by" }),
1168
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CoinListWordmark, { className: "h-3 w-auto" })
1169
+ ]
1170
+ }
1171
+ );
1172
+ }
1173
+
1174
+ // src/client/components/design-system/ClButton.tsx
1175
+ var import_class_variance_authority = require("class-variance-authority");
1176
+ var import_react2 = require("react");
1177
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1178
+ var buttonVariants = (0, import_class_variance_authority.cva)(
1179
+ "inline-flex items-center justify-center gap-3 whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-40 cursor-pointer",
1180
+ {
1181
+ variants: {
1182
+ variant: {
1183
+ // Primary — tokens auto-switch: dark bg (#171717) → light bg (#f4f4f5) in dark mode
1184
+ primary: "bg-brand-primary text-text-inverse font-semibold hover:bg-brand-primary-hover active:bg-brand-primary-active focus-visible:ring-gray-500",
1185
+ // Secondary — bg-section, border-border, text-text-primary all auto-switch
1186
+ // hover maps to border token value; active has no token so uses dark: override
1187
+ secondary: "bg-section border border-border text-text-primary font-medium hover:bg-border active:bg-[#d9d9d9] dark:active:bg-divider focus-visible:ring-gray-400",
1188
+ // Destructive — error tokens auto-switch to translucent dark equivalents
1189
+ // hover/active have no tokens so use dark: overrides
1190
+ destructive: "bg-error-subtle border border-error-border text-error font-semibold hover:bg-[#fcdcd8] active:bg-[#f9c9c4] dark:hover:bg-[rgba(248,113,113,0.20)] dark:active:bg-[rgba(248,113,113,0.30)] focus-visible:ring-red-400",
1191
+ // Ghost — transparent base; hover/active use section/border tokens (auto-switch)
1192
+ ghost: "bg-transparent text-text-primary font-medium hover:bg-section active:bg-border focus-visible:ring-gray-400",
1193
+ // Link — text-text-primary auto-switches light/dark
1194
+ link: "h-auto p-0 text-text-primary font-medium underline-offset-4 hover:underline focus-visible:ring-gray-500",
1195
+ // Destructive link — text-error auto-switches (#b42318 → #f87171 in dark)
1196
+ destructiveLink: "h-auto p-0 text-error font-medium hover:text-[#8b1a13] active:text-[#6d1510] dark:hover:text-[#fca5a5] dark:active:text-[#f87171] focus-visible:ring-red-500"
1197
+ },
1198
+ size: {
1199
+ sm: "h-9 px-4 text-sm tracking-[0.28px]",
1200
+ default: "h-11 px-5 text-sm tracking-[0.28px]",
1201
+ lg: "h-14 px-6 text-base",
1202
+ icon: "h-12 w-12"
1203
+ },
1204
+ radius: {
1205
+ pill: "rounded-full",
1206
+ // compound variants below handle size-specific radii
1207
+ rounded: ""
1208
+ },
1209
+ fullWidth: {
1210
+ true: "w-full",
1211
+ false: ""
1212
+ }
1213
+ },
1214
+ compoundVariants: [
1215
+ { size: "sm", radius: "rounded", className: "rounded-lg" },
1216
+ { size: "default", radius: "rounded", className: "rounded-2xl" },
1217
+ { size: "lg", radius: "rounded", className: "rounded-2xl" },
1218
+ { size: "icon", radius: "rounded", className: "rounded-md" }
1219
+ ],
1220
+ defaultVariants: {
1221
+ variant: "primary",
1222
+ size: "default",
1223
+ radius: "pill",
1224
+ fullWidth: false
1225
+ }
1226
+ }
1227
+ );
1228
+ var ClButton = (0, import_react2.forwardRef)(
1229
+ ({
1230
+ className,
1231
+ variant,
1232
+ size,
1233
+ radius,
1234
+ fullWidth,
1235
+ children,
1236
+ isLoading = false,
1237
+ loadingText,
1238
+ startIcon,
1239
+ endIcon,
1240
+ disabled,
1241
+ ...props
1242
+ }, ref) => {
1243
+ const isDisabled = disabled || isLoading;
1244
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1245
+ "button",
1246
+ {
1247
+ className: cn(
1248
+ buttonVariants({ variant, size, radius, fullWidth }),
1249
+ className
1250
+ ),
1251
+ ref,
1252
+ disabled: isDisabled,
1253
+ ...props,
1254
+ children: [
1255
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1256
+ "span",
1257
+ {
1258
+ className: "h-4 w-4 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent opacity-60",
1259
+ "aria-hidden": "true"
1260
+ }
1261
+ ),
1262
+ startIcon && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "shrink-0", children: startIcon }),
1263
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: cn(isLoading && "opacity-60"), children: isLoading && loadingText ? loadingText : children }),
1264
+ endIcon && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "shrink-0", children: endIcon })
1265
+ ]
1266
+ }
1267
+ );
1268
+ }
1269
+ );
1270
+ ClButton.displayName = "ClButton";
1271
+
1272
+ // src/client/components/design-system/logos/CoinListLogomark.tsx
1273
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1274
+ function CoinListLogomark(props) {
1275
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1276
+ "svg",
1277
+ {
1278
+ width: "115",
1279
+ height: "125",
1280
+ viewBox: "0 0 115 125",
1281
+ fill: "none",
1282
+ xmlns: "http://www.w3.org/2000/svg",
1283
+ "aria-hidden": "true",
1284
+ ...props,
1285
+ children: [
1286
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1287
+ "path",
1288
+ {
1289
+ d: "M69.7519 123.966C75.8021 124.172 81.7491 123.519 87.4899 121.799C98.3871 118.531 107.531 112.58 114.922 103.947C114.922 103.947 114.922 103.947 114.888 103.912C112.859 102.193 110.831 100.507 108.837 98.8218L106.809 97.1363C106.431 96.8268 106.087 96.5172 105.709 96.2076C104.919 95.5197 104.128 94.8662 103.303 94.2126C96.2902 98.8562 88.5899 101.539 80.0991 101.883C75.8364 102.055 71.6082 101.642 67.4831 100.507C60.5047 98.6154 54.2483 95.1757 49.2294 89.7755C40.0854 79.938 36.854 68.1743 39.226 55.0348C40.7041 46.9172 44.4168 40.0723 50.0888 34.5C48.8856 34.7408 47.6825 35.016 46.5137 35.3943C37.5072 38.2148 30.5976 43.6839 25.6131 51.6639C21.6598 58.0273 19.5629 64.9411 19.3223 72.4739C19.1848 76.7047 19.4598 80.7979 20.4223 84.7879C23.4818 97.2395 30.2882 107.043 40.6698 114.335H40.7729V114.403C41.1167 114.644 41.4604 114.919 41.8042 115.16C50.2607 120.801 59.5766 123.691 69.7519 124.035V123.966Z",
1290
+ fill: "currentColor"
1291
+ }
1292
+ ),
1293
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1294
+ "path",
1295
+ {
1296
+ d: "M73.1896 89.052C69.1676 90.8406 65.0081 91.8725 60.6768 92.2165C65.9707 95.209 71.7802 96.688 78.1054 96.8256C80.168 96.86 82.2305 96.6536 84.3962 96.3097C89.7245 95.4841 94.6746 93.5579 99.281 90.7374C94.9153 87.057 90.5839 83.4109 86.2181 79.7305C82.5055 83.6861 78.1741 86.8506 73.1896 89.052Z",
1297
+ fill: "currentColor"
1298
+ }
1299
+ ),
1300
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1301
+ "path",
1302
+ {
1303
+ d: "M67.9643 0.241609C67.4143 0.276006 66.8643 0.344799 66.3486 0.413592C71.6425 1.4111 76.7646 3.13093 81.6116 5.71068C91.0306 10.7326 98.3527 17.9215 103.268 27.4494L105.915 25.1448C108.631 22.7714 111.347 20.3981 114.063 18.0247C107.634 11.1797 100.072 6.15784 91.2025 3.06214C85.2898 0.998336 79.2396 -0.0335643 73.0176 0.000832322C71.3675 0.000832322 69.6831 0.138419 67.9643 0.241609Z",
1304
+ fill: "currentColor"
1305
+ }
1306
+ ),
1307
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1308
+ "path",
1309
+ {
1310
+ d: "M26.4037 107.871C18.4628 97.9988 14.1314 86.7167 14.2002 73.818C14.2002 67.317 15.5409 61.0568 18.1191 55.1062C21.9348 46.2662 27.9506 39.1805 36.4071 34.2962C44.348 29.687 52.9763 27.8984 62.1547 29.2743C71.5738 30.7189 79.3771 35.2249 85.6336 42.2762C85.6336 42.2762 85.8398 42.5514 85.9429 42.6546C86.8711 41.8291 87.8336 41.0035 88.7618 40.178L89.8962 39.1805C90.8587 38.355 91.8212 37.4951 92.7838 36.6696C94.9838 34.7434 97.1495 32.8515 99.3152 30.9253C95.087 22.223 88.7274 15.55 80.3053 10.8377C70.3706 5.26541 59.6797 3.54557 48.37 5.02463C42.7323 5.78136 37.3352 7.50119 32.1788 10.0465C14.6815 18.7145 2.06546 36.532 1.03418 58.4771C0.862296 62.0887 1.10293 65.7348 1.65294 69.3464C3.02798 78.3239 6.32808 86.4759 11.622 93.8368C15.7471 99.5123 20.6629 104.19 26.3693 107.871H26.4037Z",
1311
+ fill: "currentColor"
1312
+ }
1313
+ )
1314
+ ]
1315
+ }
1316
+ );
1317
+ }
1318
+
1319
+ // src/client/components/oauth/CoinListSignInButton.tsx
1320
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1321
+ function CoinListSignInButton({
1322
+ onClick,
1323
+ disabled = false,
1324
+ fullWidth = false,
1325
+ isLoading = false,
1326
+ className
1327
+ }) {
1328
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1329
+ ClButton,
1330
+ {
1331
+ variant: "primary",
1332
+ size: "lg",
1333
+ fullWidth,
1334
+ onClick,
1335
+ disabled,
1336
+ isLoading,
1337
+ loadingText: "Signing in...",
1338
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CoinListLogomark, { className: "h-[18px] w-[16px] shrink-0" }),
1339
+ className,
1340
+ children: "Continue with CoinList"
1341
+ }
1342
+ );
1343
+ }
1344
+
1345
+ // src/client/hooks/useCoinList.ts
1346
+ var import_react3 = require("react");
1347
+ function useCoinList() {
1348
+ const context = (0, import_react3.useContext)(CoinListContext);
1349
+ if (context === null) {
1350
+ throw new CoinListClientInitializationError();
1351
+ }
1352
+ return {
1353
+ isReady: context.isReady,
1354
+ coinlist: context.coinlist
1355
+ };
1356
+ }
1357
+ var CoinListClientInitializationError = class extends Error {
1358
+ constructor(message = "useCoinList() must be used within CoinListProvider. Wrap your app with <CoinListProvider config={...}>.") {
1359
+ super(message);
1360
+ this.name = "CoinListClientInitializationError";
1361
+ }
1362
+ };
1363
+
1364
+ // src/client/components/oauth/CoinListSignInCard.tsx
1365
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1366
+ function CoinListSignInCard({
1367
+ state = "idle",
1368
+ onSignIn,
1369
+ onClose,
1370
+ className
1371
+ } = {}) {
1372
+ const { coinlist } = useCoinList();
1373
+ const handleSignIn = onSignIn ?? (() => {
1374
+ void coinlist.startOAuth();
1375
+ });
1376
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1377
+ "div",
1378
+ {
1379
+ className: cn(
1380
+ "relative flex w-full max-w-md flex-col rounded-card border border-border bg-background p-6 shadow-card",
1381
+ className
1382
+ ),
1383
+ children: [
1384
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CloseButton, { onClick: onClose, className: "absolute right-4 top-4" }),
1385
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-2", children: [
1386
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: cn(typeClasses.h3, "text-text-primary"), children: "Sign in to participate" }),
1387
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: cn(typeClasses.body, "text-text-secondary"), children: "A CoinList account is required to participate in this sale." })
1388
+ ] }),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CoinListSignInButton, { onClick: handleSignIn, fullWidth: true, className: "mt-4" }),
1390
+ state === "error" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1391
+ AlertBanner,
1392
+ {
1393
+ variant: "error",
1394
+ icon: true,
1395
+ content: "Error signing in. Please try again.",
1396
+ className: "mt-4"
1397
+ }
1398
+ ),
1399
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PoweredByCoinList, { className: "mt-4" })
1400
+ ]
1401
+ }
1402
+ );
1403
+ }
1404
+
1405
+ // src/client/components/offers/OfferCard.tsx
1406
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1407
+ var monthYearFormatter = new Intl.DateTimeFormat("en-US", {
1408
+ month: "short",
1409
+ year: "numeric"
1410
+ });
1411
+ var OfferCardUi = {
1412
+ fromDomain: (offer) => ({
1413
+ tagline: offer.tagline,
1414
+ bannerUrl: offer.bannerUrl,
1415
+ logoUrl: offer.logoUrl,
1416
+ formattedStartsAt: monthYearFormatter.format(offer.startsAt),
1417
+ formattedEndsAt: monthYearFormatter.format(offer.endsAt)
1418
+ })
1419
+ };
1420
+ function OfferCard({
1421
+ offer,
1422
+ onClick,
1423
+ className,
1424
+ containerClassName
1425
+ }) {
1426
+ const cardLabel = "Open offer details";
1427
+ const cardContent = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1428
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "relative mb-6 h-52 w-full overflow-hidden rounded-2xl border border-border/60 bg-section", children: [
1429
+ offer.bannerUrl ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1430
+ "img",
1431
+ {
1432
+ src: offer.bannerUrl,
1433
+ alt: "Offer banner",
1434
+ className: "h-full w-full object-cover"
1435
+ }
1436
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-full w-full bg-gradient-to-br from-surface via-section to-background" }),
1437
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-overlay/35" }),
1438
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "absolute inset-0 flex items-center justify-center p-6", children: offer.logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1439
+ "img",
1440
+ {
1441
+ src: offer.logoUrl,
1442
+ alt: "Offer logo",
1443
+ className: "h-28 w-28 rounded-full border border-border bg-surface object-cover shadow-[0_10px_24px_rgba(0,0,0,0.25)]"
1444
+ }
1445
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-28 w-28 rounded-full border border-border bg-surface shadow-[0_10px_24px_rgba(0,0,0,0.18)]" }) })
1446
+ ] }),
1447
+ offer.tagline && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: cn(typeClasses.h3, "min-h-[2.4rem] text-text-primary"), children: offer.tagline }),
1448
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mt-4 grid grid-cols-2 gap-5 border-t border-divider pt-4", children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-1", children: [
1450
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn(typeClasses.subhead, "text-text-secondary"), children: "Starts" }),
1451
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn(typeClasses.label, "text-text-primary"), children: offer.formattedStartsAt })
1452
+ ] }),
1453
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-1", children: [
1454
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn(typeClasses.subhead, "text-text-secondary"), children: "Ends" }),
1455
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn(typeClasses.label, "text-text-primary"), children: offer.formattedEndsAt })
1456
+ ] })
1457
+ ] })
1458
+ ] });
1459
+ const baseClasses = cn(
1460
+ "relative flex w-full max-w-[28rem] flex-col rounded-card border border-border bg-background p-6 text-left shadow-card transition-all duration-200",
1461
+ className,
1462
+ onClick && "cursor-pointer hover:-translate-y-0.5 hover:border-divider hover:shadow-[0px_14px_32px_-10px_rgba(0,0,0,0.35)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-focus focus-visible:ring-offset-2"
1463
+ );
1464
+ let cardNode;
1465
+ if (onClick) {
1466
+ cardNode = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1467
+ "button",
1468
+ {
1469
+ type: "button",
1470
+ "aria-label": cardLabel,
1471
+ className: baseClasses,
1472
+ onClick,
1473
+ children: cardContent
1474
+ }
1475
+ );
1476
+ } else {
1477
+ cardNode = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: baseClasses, children: cardContent });
1478
+ }
1479
+ if (!containerClassName) {
1480
+ return cardNode;
1481
+ }
1482
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: containerClassName, children: cardNode });
1483
+ }
1484
+
1485
+ // src/client/hooks/useOffers.ts
1486
+ var import_react4 = require("react");
1487
+ var LOADING_STATE = { type: "LOADING" };
1488
+ function useOffers(options = {}) {
1489
+ const { coinlist, isReady } = useCoinList();
1490
+ const { data: serverOffers } = options;
1491
+ const [offersState, setOffersState] = (0, import_react4.useState)(
1492
+ serverOffers !== void 0 ? { type: "CONTENT", offers: serverOffers } : LOADING_STATE
1493
+ );
1494
+ (0, import_react4.useEffect)(() => {
1495
+ let isCancelled = false;
1496
+ if (!isReady) {
1497
+ if (serverOffers === void 0) {
1498
+ setOffersState(LOADING_STATE);
1499
+ }
1500
+ return () => {
1501
+ isCancelled = true;
1502
+ };
1503
+ }
1504
+ if (serverOffers !== void 0) {
1505
+ return () => {
1506
+ isCancelled = true;
1507
+ };
1508
+ }
1509
+ setOffersState(LOADING_STATE);
1510
+ coinlist.fetchOffers().then((offers) => {
1511
+ if (!isCancelled) {
1512
+ setOffersState({ type: "CONTENT", offers });
1513
+ }
1514
+ }).catch((error) => {
1515
+ if (!isCancelled) {
1516
+ const reason = error instanceof NotAuthenticatedError ? "not-authenticated" : "generic-error";
1517
+ setOffersState({ type: "ERROR", reason });
1518
+ }
1519
+ });
1520
+ return () => {
1521
+ isCancelled = true;
1522
+ };
1523
+ }, [coinlist, isReady, serverOffers]);
1524
+ return { offersState };
1525
+ }
1526
+
1527
+ // src/client/components/offers/OffersGrid.tsx
1528
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1529
+ var DEFAULT_MAX_COLUMNS = 3;
1530
+ function OffersGrid({
1531
+ data,
1532
+ maxColumns,
1533
+ className,
1534
+ containerClassName,
1535
+ loading,
1536
+ error,
1537
+ emptyState,
1538
+ onOfferClick
1539
+ } = {}) {
1540
+ const { offersState } = useOffers({ data });
1541
+ const cols = Math.max(1, maxColumns ?? DEFAULT_MAX_COLUMNS);
1542
+ if (offersState.type === "LOADING") {
1543
+ if (loading) {
1544
+ return loading;
1545
+ }
1546
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1547
+ "output",
1548
+ {
1549
+ "aria-live": "polite",
1550
+ className: "flex min-h-40 items-center justify-center rounded-2xl border border-border bg-background p-6",
1551
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: cn(typeClasses.body, "text-text-secondary"), children: "Loading offers..." })
1552
+ }
1553
+ );
1554
+ }
1555
+ if (offersState.type === "ERROR") {
1556
+ if (error) {
1557
+ return error;
1558
+ }
1559
+ const errorMessage = offersState.reason === "not-authenticated" ? "Sign in to view available offers." : "Unable to load offers right now. Please try again.";
1560
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1561
+ AlertBanner,
1562
+ {
1563
+ variant: "error",
1564
+ icon: true,
1565
+ content: errorMessage,
1566
+ className: "w-full max-w-[28rem]"
1567
+ }
1568
+ );
1569
+ }
1570
+ if (offersState.offers.length === 0) {
1571
+ if (emptyState) {
1572
+ return emptyState;
1573
+ }
1574
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1575
+ "output",
1576
+ {
1577
+ "aria-live": "polite",
1578
+ className: "flex min-h-40 flex-col items-center justify-center rounded-2xl border border-border bg-background p-6 text-center",
1579
+ children: [
1580
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: cn(typeClasses.body, "font-medium text-text-primary"), children: "No offers available" }),
1581
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: cn(typeClasses.subhead, "text-text-secondary"), children: "Check back soon for upcoming opportunities." })
1582
+ ]
1583
+ }
1584
+ );
1585
+ }
1586
+ const maxGridWidth = `calc(${cols} * 28rem + ${Math.max(cols - 1, 0)} * 1.5rem)`;
1587
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("flex w-full justify-center", containerClassName), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1588
+ "div",
1589
+ {
1590
+ className: cn("grid w-full gap-6", className),
1591
+ style: {
1592
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 20rem), 1fr))",
1593
+ maxWidth: maxGridWidth
1594
+ },
1595
+ children: offersState.offers.map((offer) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1596
+ OfferCard,
1597
+ {
1598
+ offer: OfferCardUi.fromDomain(offer),
1599
+ onClick: onOfferClick ? () => onOfferClick(offer) : void 0
1600
+ },
1601
+ offer.id
1602
+ ))
1603
+ }
1604
+ ) });
1605
+ }
1606
+
1607
+ // src/client/components/requirements/RequirementItem.tsx
1608
+ var import_lucide_react4 = require("lucide-react");
1609
+
1610
+ // src/client/components/design-system/StepStatusIcon.tsx
1611
+ var import_lucide_react3 = require("lucide-react");
1612
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1613
+ var statusConfig = {
1614
+ required: {
1615
+ Icon: import_lucide_react3.Info,
1616
+ colorClass: "text-warning",
1617
+ fillClass: "[&_circle]:fill-warning-subtle"
1618
+ },
1619
+ pending: {
1620
+ Icon: import_lucide_react3.Info,
1621
+ colorClass: "text-warning",
1622
+ fillClass: "[&_circle]:fill-warning-subtle"
1623
+ },
1624
+ completed: {
1625
+ Icon: import_lucide_react3.CircleCheck,
1626
+ colorClass: "text-success",
1627
+ fillClass: "[&_circle]:fill-success-subtle"
1628
+ },
1629
+ rejected: {
1630
+ Icon: import_lucide_react3.CircleX,
1631
+ colorClass: "text-error",
1632
+ fillClass: "[&_circle]:fill-error-subtle"
1633
+ }
1634
+ };
1635
+ function StepStatusIcon({ status, className }) {
1636
+ const { Icon, colorClass, fillClass } = statusConfig[status];
1637
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1638
+ Icon,
1639
+ {
1640
+ className: cn("h-6 w-6 shrink-0", colorClass, fillClass, className),
1641
+ "aria-hidden": "true"
1642
+ }
1643
+ );
1644
+ }
1645
+
1646
+ // src/client/components/requirements/RequirementItem.tsx
1647
+ var import_jsx_runtime13 = require("react/jsx-runtime");
946
1648
  var RequirementVariant = {
947
1649
  fromType(type) {
948
1650
  switch (type) {
@@ -976,43 +1678,194 @@ var ChecklistStatus = {
976
1678
  return "inProgress";
977
1679
  }
978
1680
  };
979
-
980
- // src/client/hooks/useCoinList.ts
981
- var import_react2 = require("react");
982
- function useCoinList() {
983
- const context = (0, import_react2.useContext)(CoinListContext);
984
- if (context === null) {
985
- throw new CoinListClientInitializationError();
1681
+ var RequirementItemUi = {
1682
+ fromDomain(req, statusInfo, label, description, walletAddress) {
1683
+ return {
1684
+ variant: RequirementVariant.fromType(req.type),
1685
+ status: statusInfo ? RequirementStatus.fromStatusValue(statusInfo.status) : "required",
1686
+ label,
1687
+ description,
1688
+ walletAddress
1689
+ };
1690
+ }
1691
+ };
1692
+ function VerificationActionZone({
1693
+ status,
1694
+ onAction,
1695
+ onContactSupport
1696
+ }) {
1697
+ switch (status) {
1698
+ case "required":
1699
+ if (onAction === null) return null;
1700
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ClButton, { size: "sm", onClick: onAction, children: "Continue" });
1701
+ case "pending":
1702
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1703
+ AlertBanner,
1704
+ {
1705
+ variant: "warning",
1706
+ content: "Under review. We'll notify you when complete."
1707
+ }
1708
+ );
1709
+ case "completed":
1710
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AlertBanner, { variant: "success", content: "Identity verified." });
1711
+ case "rejected":
1712
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1713
+ AlertBanner,
1714
+ {
1715
+ variant: "error",
1716
+ content: onContactSupport === null ? "Verification rejected." : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1717
+ "Verification rejected.",
1718
+ " ",
1719
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1720
+ "button",
1721
+ {
1722
+ type: "button",
1723
+ onClick: onContactSupport,
1724
+ className: "underline",
1725
+ children: "Contact support"
1726
+ }
1727
+ ),
1728
+ "."
1729
+ ] })
1730
+ }
1731
+ );
986
1732
  }
987
- return {
988
- isReady: context.isReady,
989
- coinlist: context.coinlist
990
- };
991
1733
  }
992
- var CoinListClientInitializationError = class extends Error {
993
- constructor(message = "useCoinList() must be used within CoinListProvider. Wrap your app with <CoinListProvider config={...}>.") {
994
- super(message);
995
- this.name = "CoinListClientInitializationError";
1734
+ function WalletActionZone({
1735
+ status,
1736
+ walletAddress,
1737
+ onAction
1738
+ }) {
1739
+ if (status === "completed") {
1740
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1741
+ onAction !== null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ClButton, { size: "sm", variant: "secondary", onClick: onAction, children: "Change wallet" }),
1742
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1743
+ AlertBanner,
1744
+ {
1745
+ variant: "success",
1746
+ content: walletAddress ?? "Connected: 0xBa4b...9f2c"
1747
+ }
1748
+ )
1749
+ ] });
996
1750
  }
997
- };
1751
+ if (status === "rejected") {
1752
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1753
+ onAction !== null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ClButton, { size: "sm", onClick: onAction, children: "Connect wallet" }),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1755
+ AlertBanner,
1756
+ {
1757
+ variant: "error",
1758
+ content: "This wallet is not eligible for this sale."
1759
+ }
1760
+ )
1761
+ ] });
1762
+ }
1763
+ if (onAction === null) return null;
1764
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ClButton, { size: "sm", onClick: onAction, children: "Connect wallet" });
1765
+ }
1766
+ function RequirementItem({
1767
+ ui,
1768
+ expanded = false,
1769
+ onToggle,
1770
+ className,
1771
+ onAction,
1772
+ onContactSupport
1773
+ }) {
1774
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn("flex w-full flex-col", className), children: [
1775
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1776
+ "button",
1777
+ {
1778
+ type: "button",
1779
+ onClick: onToggle,
1780
+ "aria-expanded": expanded,
1781
+ className: "flex h-14 w-full cursor-pointer items-center justify-between rounded-2xl px-2 transition-colors hover:bg-surface",
1782
+ children: [
1783
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
1784
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StepStatusIcon, { status: ui.status }),
1785
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1786
+ "span",
1787
+ {
1788
+ className: cn(
1789
+ typeClasses.body,
1790
+ "whitespace-nowrap font-medium text-text-primary"
1791
+ ),
1792
+ children: ui.label
1793
+ }
1794
+ )
1795
+ ] }),
1796
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1797
+ import_lucide_react4.ChevronRight,
1798
+ {
1799
+ className: cn(
1800
+ "h-6 w-6 shrink-0 text-text-secondary transition-transform duration-300",
1801
+ expanded && "rotate-90"
1802
+ ),
1803
+ "aria-hidden": "true"
1804
+ }
1805
+ )
1806
+ ]
1807
+ }
1808
+ ),
1809
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1810
+ "div",
1811
+ {
1812
+ className: cn(
1813
+ "grid transition-[grid-template-rows] duration-300 ease-in-out",
1814
+ expanded ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
1815
+ ),
1816
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "min-h-0 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-4 px-2 pb-4", children: [
1817
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: cn(typeClasses.subhead, "text-text-secondary"), children: ui.description }),
1818
+ ui.variant === "verification" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1819
+ VerificationActionZone,
1820
+ {
1821
+ status: ui.status,
1822
+ onAction,
1823
+ onContactSupport
1824
+ }
1825
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1826
+ WalletActionZone,
1827
+ {
1828
+ status: ui.status,
1829
+ walletAddress: ui.walletAddress,
1830
+ onAction
1831
+ }
1832
+ )
1833
+ ] }) })
1834
+ }
1835
+ )
1836
+ ] });
1837
+ }
998
1838
 
999
- // src/client/hooks/useCoinListRequirements.ts
1000
- var import_react3 = require("react");
1001
- var LOADING_STATE = { type: "LOADING" };
1002
- function useCoinListRequirements(offerId) {
1839
+ // src/client/components/requirements/RequirementsChecklist.tsx
1840
+ var import_react6 = require("react");
1841
+
1842
+ // src/client/hooks/useRequirements.ts
1843
+ var import_react5 = require("react");
1844
+ var LOADING_STATE2 = { type: "LOADING" };
1845
+ function useRequirements(offerId, options = {}) {
1003
1846
  const { coinlist, isReady } = useCoinList();
1004
- const [requirementsState, setRequirementsState] = (0, import_react3.useState)(LOADING_STATE);
1005
- const [fetchKey, setFetchKey] = (0, import_react3.useState)(0);
1006
- const refetch = (0, import_react3.useCallback)(() => setFetchKey((k) => k + 1), []);
1007
- (0, import_react3.useEffect)(() => {
1847
+ const { data } = options;
1848
+ const [requirementsState, setRequirementsState] = (0, import_react5.useState)(
1849
+ data !== void 0 ? { type: "CONTENT", ...data } : LOADING_STATE2
1850
+ );
1851
+ const [fetchKey, setFetchKey] = (0, import_react5.useState)(0);
1852
+ const refetch = (0, import_react5.useCallback)(() => setFetchKey((k) => k + 1), []);
1853
+ (0, import_react5.useEffect)(() => {
1008
1854
  let isCancelled = false;
1009
1855
  if (!isReady) {
1010
- setRequirementsState(LOADING_STATE);
1856
+ if (data === void 0) {
1857
+ setRequirementsState(LOADING_STATE2);
1858
+ }
1011
1859
  return () => {
1012
1860
  isCancelled = true;
1013
1861
  };
1014
1862
  }
1015
- setRequirementsState(LOADING_STATE);
1863
+ if (data !== void 0 && fetchKey === 0) {
1864
+ return () => {
1865
+ isCancelled = true;
1866
+ };
1867
+ }
1868
+ setRequirementsState(LOADING_STATE2);
1016
1869
  Promise.all([
1017
1870
  coinlist.fetchOfferRequirements(offerId),
1018
1871
  coinlist.fetchRequirementStatuses(offerId)
@@ -1020,7 +1873,7 @@ function useCoinListRequirements(offerId) {
1020
1873
  if (!isCancelled) {
1021
1874
  setRequirementsState({
1022
1875
  type: "CONTENT",
1023
- requirementsByOptionId,
1876
+ requirements: requirementsByOptionId,
1024
1877
  statuses
1025
1878
  });
1026
1879
  }
@@ -1037,18 +1890,148 @@ function useCoinListRequirements(offerId) {
1037
1890
  return { requirementsState, refetch };
1038
1891
  }
1039
1892
 
1040
- // src/client/hooks/useCompleteCoinListOAuth.ts
1041
- var import_react4 = require("react");
1042
- function useCompleteCoinListOAuth(options) {
1893
+ // src/client/components/requirements/RequirementsChecklist.tsx
1894
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1895
+ var DEFAULT_LABELS = {
1896
+ kyc_approved: "KYC Verification",
1897
+ identity_verified: "Identity Verification",
1898
+ proof_of_address: "Proof of Address",
1899
+ source_of_funds: "Source of Funds",
1900
+ external_wallet: "External Wallet",
1901
+ whitelisted_wallet: "Whitelisted Wallet",
1902
+ jurisdiction: "Jurisdiction Check",
1903
+ accreditation: "Accreditation"
1904
+ };
1905
+ var DEFAULT_DESCRIPTIONS = {
1906
+ kyc_approved: "Complete Know Your Customer verification.",
1907
+ identity_verified: "Verify your identity.",
1908
+ proof_of_address: "Provide proof of your residential address.",
1909
+ source_of_funds: "Provide documentation for your source of funds.",
1910
+ external_wallet: "Connect an external wallet.",
1911
+ whitelisted_wallet: "Connect a whitelisted wallet.",
1912
+ jurisdiction: "Confirm your jurisdiction eligibility.",
1913
+ accreditation: "Verify your accreditation status."
1914
+ };
1915
+ function RequirementsChecklist({
1916
+ offerId,
1917
+ optionId,
1918
+ title,
1919
+ description,
1920
+ onContinue,
1921
+ onRequirementActionOverride,
1922
+ onContactSupportOverride,
1923
+ getLabel,
1924
+ getDescription,
1925
+ loading,
1926
+ error,
1927
+ className,
1928
+ data
1929
+ }) {
1043
1930
  const { coinlist } = useCoinList();
1044
- const postOAuthCompleteRef = (0, import_react4.useRef)(options.postOAuthComplete);
1045
- const onFailureRef = (0, import_react4.useRef)(options.onFailure);
1046
- const onSuccessRef = (0, import_react4.useRef)(options.onSuccess);
1931
+ const { requirementsState } = useRequirements(offerId, { data });
1932
+ const [expandedIndex, setExpandedIndex] = (0, import_react6.useState)(null);
1933
+ const onRequirementAction = onRequirementActionOverride !== void 0 ? onRequirementActionOverride : (requirement) => coinlist.handleRequirement(requirement);
1934
+ const onContactSupport = onContactSupportOverride !== void 0 ? onContactSupportOverride : () => coinlist.contactSupport();
1935
+ if (requirementsState.type === "LOADING") {
1936
+ if (loading) return loading;
1937
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1938
+ "output",
1939
+ {
1940
+ "aria-live": "polite",
1941
+ className: "flex min-h-40 items-center justify-center rounded-2xl border border-border bg-background p-6",
1942
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: cn(typeClasses.body, "text-text-secondary"), children: "Loading requirements..." })
1943
+ }
1944
+ );
1945
+ }
1946
+ if (requirementsState.type === "ERROR") {
1947
+ if (error) return error;
1948
+ const errorMessage = requirementsState.reason === "not-authenticated" ? "Sign in to view requirements." : "Unable to load requirements right now. Please try again.";
1949
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1950
+ AlertBanner,
1951
+ {
1952
+ variant: "error",
1953
+ icon: true,
1954
+ content: errorMessage,
1955
+ className: "w-full max-w-[28rem]"
1956
+ }
1957
+ );
1958
+ }
1959
+ const { requirements: requirementsByOptionId, statuses } = requirementsState;
1960
+ const requirements = requirementsByOptionId[optionId] ?? [];
1961
+ const requirementIds = new Set(requirements.map((r) => r.id));
1962
+ const optionStatuses = statuses.filter((s) => requirementIds.has(s.id));
1963
+ const statusByRequirementId = new Map(optionStatuses.map((s) => [s.id, s]));
1964
+ const checklistStatus = ChecklistStatus.derive(optionStatuses);
1965
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cn("flex w-full flex-col gap-4", className), children: [
1966
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
1967
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { className: cn(typeClasses.h3, "text-text-primary"), children: title }),
1968
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: cn(typeClasses.body, "text-text-secondary"), children: description })
1969
+ ] }),
1970
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex flex-col divide-y divide-divider", children: requirements.map((req, index) => {
1971
+ const statusInfo = statusByRequirementId.get(req.id);
1972
+ const label = getLabel ? getLabel(req) : DEFAULT_LABELS[req.type];
1973
+ const itemDescription = (getDescription ? getDescription(req) : null) ?? DEFAULT_DESCRIPTIONS[req.type];
1974
+ let onActionBound;
1975
+ if (onRequirementAction === null) {
1976
+ onActionBound = null;
1977
+ } else if (onRequirementAction) {
1978
+ onActionBound = () => onRequirementAction(req);
1979
+ }
1980
+ let onContactSupportBound;
1981
+ if (onContactSupport === null) {
1982
+ onContactSupportBound = null;
1983
+ } else if (onContactSupport) {
1984
+ onContactSupportBound = () => onContactSupport(req);
1985
+ }
1986
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1987
+ RequirementItem,
1988
+ {
1989
+ ui: RequirementItemUi.fromDomain(
1990
+ req,
1991
+ statusInfo,
1992
+ label,
1993
+ itemDescription
1994
+ ),
1995
+ expanded: expandedIndex === index,
1996
+ onToggle: () => setExpandedIndex((prev) => prev === index ? null : index),
1997
+ onAction: onActionBound,
1998
+ onContactSupport: onContactSupportBound
1999
+ },
2000
+ req.id
2001
+ );
2002
+ }) }),
2003
+ checklistStatus === "ineligible" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2004
+ AlertBanner,
2005
+ {
2006
+ variant: "error",
2007
+ icon: true,
2008
+ content: "You are not eligible to participate."
2009
+ }
2010
+ ),
2011
+ onContinue && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2012
+ ClButton,
2013
+ {
2014
+ fullWidth: true,
2015
+ disabled: checklistStatus !== "eligible",
2016
+ onClick: onContinue,
2017
+ children: "Continue"
2018
+ }
2019
+ )
2020
+ ] });
2021
+ }
2022
+
2023
+ // src/client/hooks/useCompleteOAuth.ts
2024
+ var import_react7 = require("react");
2025
+ function useCompleteOAuth(options) {
2026
+ const { coinlist } = useCoinList();
2027
+ const postOAuthCompleteRef = (0, import_react7.useRef)(options.postOAuthComplete);
2028
+ const onFailureRef = (0, import_react7.useRef)(options.onFailure);
2029
+ const onSuccessRef = (0, import_react7.useRef)(options.onSuccess);
1047
2030
  postOAuthCompleteRef.current = options.postOAuthComplete;
1048
2031
  onFailureRef.current = options.onFailure;
1049
2032
  onSuccessRef.current = options.onSuccess;
1050
- const hasStartedExchangeRef = (0, import_react4.useRef)(false);
1051
- (0, import_react4.useEffect)(() => {
2033
+ const hasStartedExchangeRef = (0, import_react7.useRef)(false);
2034
+ (0, import_react7.useEffect)(() => {
1052
2035
  if (typeof window === "undefined") return;
1053
2036
  const params = new URLSearchParams(window.location.search);
1054
2037
  if (!params.has("code") && !params.has("error")) return;
@@ -1091,74 +2074,112 @@ function useCompleteCoinListOAuth(options) {
1091
2074
  }, [coinlist]);
1092
2075
  }
1093
2076
 
1094
- // src/shared/types/oauth-session.ts
1095
- var OAuthRefreshToken = (value) => value;
1096
- var OAuthSession = {
1097
- fromDto: (dto) => {
1098
- const expiresAt = new Date(Date.now() + dto.expires_in * 1e3);
1099
- return {
1100
- accessToken: {
1101
- value: dto.access_token,
1102
- expiresAt
1103
- },
1104
- ...dto.refresh_token != null && dto.refresh_token !== "" ? { refreshToken: OAuthRefreshToken(dto.refresh_token) } : void 0
2077
+ // src/client/hooks/useOfferDetails.ts
2078
+ var import_react8 = require("react");
2079
+ var LOADING_STATE3 = { type: "LOADING" };
2080
+ function useOfferDetails(offerId, options = {}) {
2081
+ const { coinlist, isReady } = useCoinList();
2082
+ const { data } = options;
2083
+ const [offerDetailsState, setOfferDetailsState] = (0, import_react8.useState)(
2084
+ data !== void 0 ? { type: "CONTENT", offerDetail: data } : LOADING_STATE3
2085
+ );
2086
+ (0, import_react8.useEffect)(() => {
2087
+ let isCancelled = false;
2088
+ if (!isReady) {
2089
+ if (data === void 0) {
2090
+ setOfferDetailsState(LOADING_STATE3);
2091
+ }
2092
+ return () => {
2093
+ isCancelled = true;
2094
+ };
2095
+ }
2096
+ if (data !== void 0) {
2097
+ return () => {
2098
+ isCancelled = true;
2099
+ };
2100
+ }
2101
+ setOfferDetailsState(LOADING_STATE3);
2102
+ coinlist.fetchOfferDetails(offerId).then((offerDetail) => {
2103
+ if (!isCancelled) {
2104
+ setOfferDetailsState({ type: "CONTENT", offerDetail });
2105
+ }
2106
+ }).catch((error) => {
2107
+ if (!isCancelled) {
2108
+ const reason = error instanceof NotAuthenticatedError ? "not-authenticated" : "generic-error";
2109
+ setOfferDetailsState({ type: "ERROR", reason });
2110
+ }
2111
+ });
2112
+ return () => {
2113
+ isCancelled = true;
1105
2114
  };
1106
- }
1107
- };
2115
+ }, [coinlist, isReady, offerId, data]);
2116
+ return { offerDetailsState };
2117
+ }
1108
2118
 
1109
- // src/shared/types/user.ts
1110
- var UserId = (value) => value;
1111
- var UserEmail = (value) => value;
1112
- var User = {
1113
- fromDto: (dto) => ({
1114
- id: UserId(dto.id),
1115
- email: UserEmail(dto.email)
1116
- })
1117
- };
2119
+ // src/client/hooks/useParticipations.ts
2120
+ var import_react9 = require("react");
2121
+ var LOADING_STATE4 = { type: "LOADING" };
2122
+ function useParticipations(offerId, options = {}) {
2123
+ const { coinlist, isReady } = useCoinList();
2124
+ const { data } = options;
2125
+ const [participationsState, setParticipationsState] = (0, import_react9.useState)(
2126
+ data !== void 0 ? { type: "CONTENT", participations: data } : LOADING_STATE4
2127
+ );
2128
+ (0, import_react9.useEffect)(() => {
2129
+ let isCancelled = false;
2130
+ if (!isReady) {
2131
+ if (data === void 0) {
2132
+ setParticipationsState(LOADING_STATE4);
2133
+ }
2134
+ return () => {
2135
+ isCancelled = true;
2136
+ };
2137
+ }
2138
+ if (data !== void 0) {
2139
+ return () => {
2140
+ isCancelled = true;
2141
+ };
2142
+ }
2143
+ setParticipationsState(LOADING_STATE4);
2144
+ coinlist.fetchParticipations(offerId).then((participations) => {
2145
+ if (!isCancelled) {
2146
+ setParticipationsState({ type: "CONTENT", participations });
2147
+ }
2148
+ }).catch((error) => {
2149
+ if (!isCancelled) {
2150
+ const reason = error instanceof NotAuthenticatedError ? "not-authenticated" : "generic-error";
2151
+ setParticipationsState({ type: "ERROR", reason });
2152
+ }
2153
+ });
2154
+ return () => {
2155
+ isCancelled = true;
2156
+ };
2157
+ }, [coinlist, isReady, offerId, data]);
2158
+ return { participationsState };
2159
+ }
1118
2160
  // Annotate the CommonJS export names for ESM import in node:
1119
2161
  0 && (module.exports = {
1120
- AuthorizationCode,
1121
- Blockchain,
1122
2162
  ChecklistStatus,
1123
- ClientId,
1124
- ClientSecret,
1125
- CodeChallenge,
1126
- CodeVerifier,
2163
+ CoinListClientInitializationError,
2164
+ CoinListContext,
1127
2165
  CoinListProvider,
1128
- CreateParticipationParams,
1129
- Cursor,
1130
- FaqItem,
1131
- Link,
1132
- Milestone,
1133
- OAuthRefreshToken,
1134
- OAuthSession,
1135
- Offer,
1136
- OfferDetail,
1137
- OfferId,
1138
- OfferOption,
1139
- OfferOptionId,
1140
- OfferOptionSlug,
1141
- OfferSlug,
1142
- PaginatedResponse,
1143
- PaginationParams,
1144
- Participation,
1145
- ParticipationId,
1146
- ParticipationsPaginationParams,
1147
- RedirectUri,
1148
- Requirement,
1149
- RequirementId,
2166
+ CoinListSignInCard,
2167
+ OAUTH_CODE_VERIFIER_KEY,
2168
+ OAUTH_STATE_KEY,
2169
+ OfferCard,
2170
+ OfferCardUi,
2171
+ OffersGrid,
2172
+ RequirementItem,
2173
+ RequirementItemUi,
1150
2174
  RequirementStatus,
1151
- RequirementStatusInfo,
1152
2175
  RequirementVariant,
1153
- TermItem,
1154
- User,
1155
- UserEmail,
1156
- UserId,
1157
- WalletAddress,
2176
+ RequirementsChecklist,
1158
2177
  createCoinListClient,
1159
- fetchAllPages,
1160
2178
  useCoinList,
1161
- useCoinListRequirements,
1162
- useCompleteCoinListOAuth
2179
+ useCompleteOAuth,
2180
+ useOfferDetails,
2181
+ useOffers,
2182
+ useParticipations,
2183
+ useRequirements
1163
2184
  });
1164
2185
  //# sourceMappingURL=index.cjs.map