@odla-ai/chapter 0.40.0 → 0.40.2

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.
@@ -1,8 +1,8 @@
1
1
  import * as preact from 'preact';
2
2
  import { ComponentChildren, JSX } from 'preact';
3
3
  import { CrmClient, Crm, CrmRecord } from '@odla-ai/crm';
4
- import { a as Chapter, b as ChapterCopy, M as MemberContentDocument, E as EffectiveMemberContent, c as MemberContentTierAuthority } from '../../copy-context-Dbu9Bbq1.js';
5
- export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-Dbu9Bbq1.js';
4
+ import { a as Chapter, b as ChapterCopy, M as MemberContentDocument, E as EffectiveMemberContent, c as MemberContentTierAuthority } from '../../copy-context-B14OOiYq.js';
5
+ export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-B14OOiYq.js';
6
6
  import { CrmLifecycleAdapter, CrmWorkspaceMasterContext, CrmWorkspaceRecordContext, RecordPanelTab } from '@odla-ai/crm/ui';
7
7
 
8
8
  /** Normalized public auth configuration consumed by the Clerk gate. */
@@ -1,5 +1,5 @@
1
1
  export { AdminAccountMenuProps, AdminBuilderLibraryData, AdminChrome, AdminMemberContentData, AdminNote, AdminPage, AdminResource, AdminRouteState, AdminRouteTarget, AdminRouting, AdminSection, AdminSectionContext, AdminWorkspace, AdminWorkspaceNavProps, AdmissionGrantsWorkspace, AvailabilitySectionOptions, BillingSectionOptions, BuilderLibraryAdmin, ChapterAdmin, ChapterAdminAuthAdapter, ChapterAdminAuthConfig, ChapterAdminAuthLoadContext, ChapterAdminProps, ChapterAdminUser, CollectionSectionOptions, DashboardSectionOptions, EmailSectionOptions, LeaderNetworkOverview, MeetingsSectionOptions, MemberContentAdmin, NetworkRecordsWorkspace, NetworkShareActions, NetworkShareActionsProps, Panel, PeopleSectionOptions, RecordActions, RecordActionsProps, ThemeWarning, adminFetch, adminRouteFromUrl, adminRouteHref, adminSectionFromUrl, adminSectionHref, admissionGrantsWorkspace, applicationLifecycleAdapter, availabilitySection, billingSection, builderLibrarySection, collectionSection, dashboardSection, defaultAdminSections, defaultAdminWorkspaces, emailSection, loadChapterAdminConfig, loadChapterAdminUser, meetingsSection, memberContentSection, networkRecordsWorkspace, peopleSection, useAdminResource } from './admin/index.js';
2
- export { C as ChapterCopyProvider, u as useChapterCopy } from '../copy-context-Dbu9Bbq1.js';
2
+ export { C as ChapterCopyProvider, u as useChapterCopy } from '../copy-context-B14OOiYq.js';
3
3
  export { ApiFn, BrandStyle, BrandStyleProps, GiftOfferCards, GiftOfferCardsMode, GiftOfferCardsProps, JoinBookingSimulation, JoinConfig, JoinFlowState, JoinIsland, JoinIslandProps, JoinSimulation, JoinStepRenderContext, JoinSubmitRenderContext, JoinTier, JoinTierRenderContext, MEMBERSHIP_CARD_TOKEN_DEFAULTS, MemberContentArea, MemberContentAreaProps, MemberGifts, MemberProvisionalRenderContext, MembersArea, MembersAreaProps, MembershipCardTokenName, MembershipCards, MembershipCardsMode, MembershipCardsProps, PaymentPriceLines, PaymentStep, PaymentStepProps, PaymentStepSimulation, RescheduleProps, Rescheduler, Slot, SlotPicker, SlotPickerClasses, SlotPickerProps, SubResponse, TierPlaceholder, dayKey, dayLabel, fmtDate, fmtMoney, fullLabel, groupSlotsByDay, loadJoinResume, membershipApplicationHref, tierNeedsPayment, timeLabel, tzShort, useTierSelection } from './member/index.js';
4
4
  import 'preact';
5
5
  import '@odla-ai/crm';
package/dist/ui/index.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  timeLabel,
23
23
  tzShort,
24
24
  useTierSelection
25
- } from "../chunk-BIYWPPH6.js";
25
+ } from "../chunk-E5OXQ3DC.js";
26
26
  import {
27
27
  AdminNote,
28
28
  AdminPage,
@@ -1,7 +1,7 @@
1
1
  import * as preact from 'preact';
2
2
  import { ComponentChildren, JSX } from 'preact';
3
- import { d as MemberContentProjection, e as MemberContentPageProjection, b as ChapterCopy, f as ChapterBrand } from '../../copy-context-Dbu9Bbq1.js';
4
- export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-Dbu9Bbq1.js';
3
+ import { d as MemberContentProjection, e as MemberContentPageProjection, b as ChapterCopy, f as ChapterBrand } from '../../copy-context-B14OOiYq.js';
4
+ export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-B14OOiYq.js';
5
5
  import { FieldType, FieldConditions } from '@odla-ai/crm';
6
6
 
7
7
  /** A bookable slot: a start instant in epoch milliseconds. */
@@ -164,6 +164,8 @@ interface PaymentStepProps {
164
164
  applicationId: string;
165
165
  /** Refund-policy copy the applicant must acknowledge to start payment. */
166
166
  refundPolicyText: string;
167
+ /** Plain-language merchant identity shown before the applicant consents. */
168
+ merchantDisclosureText?: string;
167
169
  /** Called once the payment succeeds (or is processing) — advance to booking. */
168
170
  onPaid: (result: {
169
171
  gift: boolean;
@@ -340,6 +342,8 @@ interface JoinConfig {
340
342
  name: string;
341
343
  paymentsReady: boolean;
342
344
  refundPolicyText?: string;
345
+ /** Plain-language merchant identity displayed before payment consent. */
346
+ merchantDisclosureText?: string;
343
347
  /** Offered membership tiers, in display order. Empty on a chapter that
344
348
  * declares none, in which case no tier is posted and nothing renders. */
345
349
  tiers?: readonly JoinTier[];
@@ -22,7 +22,7 @@ import {
22
22
  timeLabel,
23
23
  tzShort,
24
24
  useTierSelection
25
- } from "../../chunk-BIYWPPH6.js";
25
+ } from "../../chunk-E5OXQ3DC.js";
26
26
  import {
27
27
  BrandStyle,
28
28
  ChapterCopyProvider,
@@ -350,6 +350,7 @@ function joinConfig(group, paymentsReady, tiers = [], conditions = {}) {
350
350
  conditions,
351
351
  disclaimerText: group.disclaimerText ?? "",
352
352
  refundPolicyText: group.refundPolicyText ?? "",
353
+ merchantDisclosureText: group.merchantDisclosureText ?? "",
353
354
  trustCopy: group.trustCopy ?? "",
354
355
  commitmentText: group.commitmentText ?? "",
355
356
  normsText: group.normsText ?? "",
@@ -1675,7 +1676,14 @@ async function createClerkUser(secretKey, input, fetchImpl = fetch) {
1675
1676
  headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
1676
1677
  body: JSON.stringify(body)
1677
1678
  });
1678
- if (res.ok) return { ok: true, status: res.status };
1679
+ if (res.ok) {
1680
+ const body2 = typeof res.json === "function" ? await res.json().catch(() => null) : null;
1681
+ return {
1682
+ ok: true,
1683
+ status: res.status,
1684
+ ...typeof body2?.id === "string" && body2.id ? { userId: body2.id } : {}
1685
+ };
1686
+ }
1679
1687
  const healed = heal(res.status);
1680
1688
  const repair = input.repairMetadata ?? {
1681
1689
  ...input.publicMetadata !== void 0 ? { publicMetadata: input.publicMetadata } : {},
@@ -1725,6 +1733,34 @@ async function syncApplicantPrivateMetadata(db, chapter, applicationId, fields,
1725
1733
  return false;
1726
1734
  }
1727
1735
  }
1736
+ var AccountProvisioningError = class extends Error {
1737
+ constructor(code, status) {
1738
+ super(code);
1739
+ this.code = code;
1740
+ this.status = status;
1741
+ }
1742
+ code;
1743
+ status;
1744
+ name = "AccountProvisioningError";
1745
+ };
1746
+ async function provisionApplicantForRoute(db, chapter, applicationId, fields) {
1747
+ try {
1748
+ await provisionApplicant(db, chapter, applicationId, fields);
1749
+ return null;
1750
+ } catch (error) {
1751
+ if (!(error instanceof AccountProvisioningError)) throw error;
1752
+ return {
1753
+ status: error.status,
1754
+ body: {
1755
+ error: {
1756
+ code: error.code,
1757
+ message: error.code === "clerk_secret_unavailable" ? "account provisioning credential is unavailable" : "account provisioning failed"
1758
+ },
1759
+ applicationId
1760
+ }
1761
+ };
1762
+ }
1763
+ }
1728
1764
  async function provisionApplicant(db, chapter, applicationId, fields) {
1729
1765
  const email = typeof fields.email === "string" ? fields.email : "";
1730
1766
  if (!email) return;
@@ -1749,9 +1785,9 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
1749
1785
  } catch {
1750
1786
  }
1751
1787
  if (chapter.account === "none") return;
1788
+ const secret = await getVaultSecret(db, "clerk_secret_key");
1789
+ if (!secret) throw new AccountProvisioningError("clerk_secret_unavailable", 503);
1752
1790
  try {
1753
- const secret = await getVaultSecret(db, "clerk_secret_key");
1754
- if (!secret) return;
1755
1791
  const privateMetadata = applicantPrivateMetadata(chapter, applicationId, fields);
1756
1792
  if (chapter.account === "create") {
1757
1793
  const result = await createClerkUser(secret, {
@@ -1764,11 +1800,13 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
1764
1800
  privateMetadata: applicantPrivateMetadata(chapter, applicationId, fields, true)
1765
1801
  }
1766
1802
  });
1767
- if (result.ok && (!result.existed || result.refreshed)) {
1768
- await markPrivateMetadataSynced(db, applicationId);
1803
+ if (!result.ok || result.existed && !result.refreshed) {
1804
+ throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
1769
1805
  }
1806
+ await markPrivateMetadataSynced(db, applicationId, result.userId);
1770
1807
  } else {
1771
1808
  const result = await createClerkInvitation(secret, { email });
1809
+ if (!result.ok) throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
1772
1810
  if (result.existed) {
1773
1811
  const refreshed = await updateClerkUserMetadataByEmail(secret, email, {
1774
1812
  publicMetadata: publicProfileRemoval,
@@ -1777,7 +1815,9 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
1777
1815
  if (refreshed) await markPrivateMetadataSynced(db, applicationId);
1778
1816
  }
1779
1817
  }
1780
- } catch {
1818
+ } catch (error) {
1819
+ if (error instanceof AccountProvisioningError) throw error;
1820
+ throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
1781
1821
  }
1782
1822
  }
1783
1823
  async function notifyAdminOfApplication(db, env, chapterId, applicationId, fields) {
@@ -2001,8 +2041,9 @@ var handleMember = async (req, url, env, ctx) => {
2001
2041
  if (effectiveChapter.sends.adminNotification === "submit") {
2002
2042
  await notifyAdminOfApplication(db, env, chapter.id, result.id, parsed);
2003
2043
  }
2004
- await provisionApplicant(db, effectiveChapter, result.id, parsed);
2005
2044
  }
2045
+ const provisioningFailure = await provisionApplicantForRoute(db, effectiveChapter, result.id, parsed);
2046
+ if (provisioningFailure) return json(provisioningFailure.body, provisioningFailure.status);
2006
2047
  const apps = (await db.query({ applications: { $: { where: { id: result.id }, limit: 1 } } })).applications;
2007
2048
  const canonical2 = Array.isArray(apps) ? apps[0] : void 0;
2008
2049
  const groups = (await db.query({ groups: { $: { where: { id: chapter.id }, limit: 1 } } })).groups;
@@ -2353,7 +2394,14 @@ function policyOf(value, errors) {
2353
2394
  errors.push("document.policy must be an object");
2354
2395
  return void 0;
2355
2396
  }
2356
- const allowed2 = /* @__PURE__ */ new Set(["disclaimerText", "refundPolicyText", "trustCopy", "commitmentText", "normsText"]);
2397
+ const allowed2 = /* @__PURE__ */ new Set([
2398
+ "disclaimerText",
2399
+ "refundPolicyText",
2400
+ "merchantDisclosureText",
2401
+ "trustCopy",
2402
+ "commitmentText",
2403
+ "normsText"
2404
+ ]);
2357
2405
  unknownKeys(input, allowed2, "document.policy", errors);
2358
2406
  const result = {};
2359
2407
  for (const key of allowed2) {
@@ -4190,7 +4238,8 @@ var handleGifts = async (req, url, env, ctx) => {
4190
4238
  return json({
4191
4239
  offers: publicGiftOffers(control3?.document.giftOffers, Date.now()),
4192
4240
  claims: buyerGiftClaims(rows2(found.giftClaims).sort((a, b) => Number(b.createdAt) - Number(a.createdAt)), Date.now()),
4193
- refundPolicyText: typeof group?.refundPolicyText === "string" ? group.refundPolicyText : ""
4241
+ refundPolicyText: typeof group?.refundPolicyText === "string" ? group.refundPolicyText : "",
4242
+ merchantDisclosureText: typeof group?.merchantDisclosureText === "string" ? group.merchantDisclosureText : ""
4194
4243
  });
4195
4244
  }
4196
4245
  if (req.method === "POST" && url.pathname === "/api/gifts/checkout") {