@odla-ai/chapter 0.40.2 → 0.41.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.
@@ -407,16 +407,20 @@ interface AdminBuilderLibraryData {
407
407
  receivedAt: number;
408
408
  }[];
409
409
  localAuthoring: boolean;
410
+ /** Closed set of Chapter destinations the server will accept. */
411
+ chapterTargets: {
412
+ chapterId: string;
413
+ }[];
410
414
  visibility: {
411
415
  builtNotFoundSeesChapterSources: boolean;
412
416
  chaptersSeeSiblingSources: false;
413
417
  };
414
418
  }
415
- /** Private source-material editor, visibly separate from member publishing. */
419
+ /** CMS-style private source-material list and editor. */
416
420
  declare function BuilderLibraryAdmin({ ctx, initialData }: {
417
421
  ctx: AdminSectionContext;
418
422
  initialData?: AdminBuilderLibraryData;
419
- }): preact.JSX.Element;
423
+ }): JSX.Element;
420
424
  /** Reusable private Builder Library section for the standard Settings workspace. */
421
425
  declare function builderLibrarySection(): AdminSection;
422
426
 
@@ -33,7 +33,7 @@ import {
33
33
  networkRecordsWorkspace,
34
34
  peopleSection,
35
35
  useAdminResource
36
- } from "../../chunk-4K2Q5JBC.js";
36
+ } from "../../chunk-O22RRPBT.js";
37
37
  import {
38
38
  ChapterCopyProvider,
39
39
  useChapterCopy
@@ -1,6 +1,6 @@
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
2
  export { C as ChapterCopyProvider, u as useChapterCopy } from '../copy-context-B14OOiYq.js';
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';
3
+ export { ApiFn, BrandStyle, BrandStyleProps, GiftCodeRedeemer, GiftCodeRedeemerCopy, GiftCodeRedeemerProps, 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';
6
6
  import '@odla-ai/crm/ui';
package/dist/ui/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ GiftCodeRedeemer,
2
3
  GiftOfferCards,
3
4
  JoinIsland,
4
5
  MEMBERSHIP_CARD_TOKEN_DEFAULTS,
@@ -22,7 +23,7 @@ import {
22
23
  timeLabel,
23
24
  tzShort,
24
25
  useTierSelection
25
- } from "../chunk-E5OXQ3DC.js";
26
+ } from "../chunk-JLLRZO7B.js";
26
27
  import {
27
28
  AdminNote,
28
29
  AdminPage,
@@ -58,7 +59,7 @@ import {
58
59
  networkRecordsWorkspace,
59
60
  peopleSection,
60
61
  useAdminResource
61
- } from "../chunk-4K2Q5JBC.js";
62
+ } from "../chunk-O22RRPBT.js";
62
63
  import {
63
64
  BrandStyle,
64
65
  ChapterCopyProvider,
@@ -73,6 +74,7 @@ export {
73
74
  BuilderLibraryAdmin,
74
75
  ChapterAdmin,
75
76
  ChapterCopyProvider,
77
+ GiftCodeRedeemer,
76
78
  GiftOfferCards,
77
79
  JoinIsland,
78
80
  LeaderNetworkOverview,
@@ -552,6 +552,25 @@ interface GiftOfferCardsProps {
552
552
  /** Exact Chapter gift-offer renderer shared by live signup, members, and CMS preview. */
553
553
  declare function GiftOfferCards(props: GiftOfferCardsProps): JSX.Element;
554
554
 
555
+ /** Resolved Chapter copy consumed by {@link GiftCodeRedeemer}. */
556
+ type GiftCodeRedeemerCopy = Pick<ChapterCopy["members"]["gifts"], "code" | "redeem" | "redeemed" | "redeemFailed">;
557
+ /** Props for the canonical Chapter member gift-code redemption control. */
558
+ interface GiftCodeRedeemerProps {
559
+ api: ApiFn;
560
+ copy: GiftCodeRedeemerCopy;
561
+ id?: string;
562
+ onRedeemed?: () => void | Promise<void>;
563
+ }
564
+ /**
565
+ * Redeems one Chapter gift code through the canonical member API.
566
+ *
567
+ * The component owns input, in-flight, success, and failure state and renders
568
+ * the shared `InlineActionField` contract. Gift codes remain case-sensitive:
569
+ * browser capitalization and spellcheck are disabled, while
570
+ * `autocomplete="one-time-code"` allows platform code suggestions.
571
+ */
572
+ declare function GiftCodeRedeemer({ api, copy, id, onRedeemed }: GiftCodeRedeemerProps): preact.JSX.Element;
573
+
555
574
  /** Authenticated buyer ledger, checkout, code rotation, and beneficiary redemption. */
556
575
  declare function MemberGifts(props: {
557
576
  api: ApiFn;
@@ -569,4 +588,4 @@ interface BrandStyleProps {
569
588
  * there is no brand to theme). */
570
589
  declare function BrandStyle(props: BrandStyleProps): preact.JSX.Element | null;
571
590
 
572
- export { type ApiFn, BrandStyle, type BrandStyleProps, GiftOfferCards, type GiftOfferCardsMode, type GiftOfferCardsProps, type JoinBookingSimulation, type JoinConfig, type JoinFlowState, JoinIsland, type JoinIslandProps, type JoinSimulation, type JoinStepRenderContext, type JoinSubmitRenderContext, type JoinTier, type JoinTierRenderContext, MEMBERSHIP_CARD_TOKEN_DEFAULTS, MemberContentArea, type MemberContentAreaProps, MemberGifts, type MemberProvisionalRenderContext, MembersArea, type MembersAreaProps, type MembershipCardTokenName, MembershipCards, type MembershipCardsMode, type MembershipCardsProps, type PaymentPriceLines, PaymentStep, type PaymentStepProps, type PaymentStepSimulation, type RescheduleProps, Rescheduler, type Slot, SlotPicker, type SlotPickerClasses, type SlotPickerProps, type SubResponse, TierPlaceholder, dayKey, dayLabel, fmtDate, fmtMoney, fullLabel, groupSlotsByDay, loadJoinResume, membershipApplicationHref, tierNeedsPayment, timeLabel, tzShort, useTierSelection };
591
+ export { type ApiFn, BrandStyle, type BrandStyleProps, GiftCodeRedeemer, type GiftCodeRedeemerCopy, type GiftCodeRedeemerProps, GiftOfferCards, type GiftOfferCardsMode, type GiftOfferCardsProps, type JoinBookingSimulation, type JoinConfig, type JoinFlowState, JoinIsland, type JoinIslandProps, type JoinSimulation, type JoinStepRenderContext, type JoinSubmitRenderContext, type JoinTier, type JoinTierRenderContext, MEMBERSHIP_CARD_TOKEN_DEFAULTS, MemberContentArea, type MemberContentAreaProps, MemberGifts, type MemberProvisionalRenderContext, MembersArea, type MembersAreaProps, type MembershipCardTokenName, MembershipCards, type MembershipCardsMode, type MembershipCardsProps, type PaymentPriceLines, PaymentStep, type PaymentStepProps, type PaymentStepSimulation, type RescheduleProps, Rescheduler, type Slot, SlotPicker, type SlotPickerClasses, type SlotPickerProps, type SubResponse, TierPlaceholder, dayKey, dayLabel, fmtDate, fmtMoney, fullLabel, groupSlotsByDay, loadJoinResume, membershipApplicationHref, tierNeedsPayment, timeLabel, tzShort, useTierSelection };
@@ -1,4 +1,5 @@
1
1
  import {
2
+ GiftCodeRedeemer,
2
3
  GiftOfferCards,
3
4
  JoinIsland,
4
5
  MEMBERSHIP_CARD_TOKEN_DEFAULTS,
@@ -22,7 +23,7 @@ import {
22
23
  timeLabel,
23
24
  tzShort,
24
25
  useTierSelection
25
- } from "../../chunk-E5OXQ3DC.js";
26
+ } from "../../chunk-JLLRZO7B.js";
26
27
  import {
27
28
  BrandStyle,
28
29
  ChapterCopyProvider,
@@ -32,6 +33,7 @@ import "../../chunk-IVXVECKR.js";
32
33
  export {
33
34
  BrandStyle,
34
35
  ChapterCopyProvider,
36
+ GiftCodeRedeemer,
35
37
  GiftOfferCards,
36
38
  JoinIsland,
37
39
  MEMBERSHIP_CARD_TOKEN_DEFAULTS,
@@ -1855,12 +1855,16 @@ async function notifyAdminOfApplication(db, env, chapterId, applicationId, field
1855
1855
  }
1856
1856
 
1857
1857
  // src/worker-routes.ts
1858
- async function memberSessionApplication(db, chapterId, email) {
1858
+ async function memberSessionApplication(db, chapterId, identity) {
1859
1859
  let apps = (await db.query({ applications: { $: {
1860
- where: { emailNormalized: normalizeChapterEmail(email) },
1860
+ where: { clerkUserId: identity.userId },
1861
1861
  limit: 20
1862
1862
  } } })).applications;
1863
- if (!apps?.length) apps = (await db.query({ applications: { $: { where: { email }, limit: 20 } } })).applications;
1863
+ if (!apps?.length && identity.email) apps = (await db.query({ applications: { $: {
1864
+ where: { emailNormalized: normalizeChapterEmail(identity.email) },
1865
+ limit: 20
1866
+ } } })).applications;
1867
+ if (!apps?.length && identity.email) apps = (await db.query({ applications: { $: { where: { email: identity.email }, limit: 20 } } })).applications;
1864
1868
  const app = Array.isArray(apps) ? [...apps].sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0] : void 0;
1865
1869
  if (!app) return null;
1866
1870
  const meetings = (await db.query({ meetings: { $: { where: { applicationId: app.id, status: "scheduled" }, order: { createdAt: "desc" }, limit: 1 } } })).meetings;
@@ -1891,8 +1895,8 @@ var handleMe = async (req, url, env, ctx) => {
1891
1895
  const role = await ctx.roleFor(db, u);
1892
1896
  const superAdmin = await ctx.isSuperAdminEmail(db, u.email);
1893
1897
  const base = { authorized: isAdminRole(role, ctx.auth), role, superAdmin, email: u.email ?? null };
1894
- if (ctx.chapter.mode !== "chapter" || !u.email) return json(base);
1895
- const member = await memberSessionApplication(db, ctx.chapter.id, u.email);
1898
+ if (ctx.chapter.mode !== "chapter") return json(base);
1899
+ const member = await memberSessionApplication(db, ctx.chapter.id, { userId: u.userId, email: u.email });
1896
1900
  let privateMetadataSynced = false;
1897
1901
  if (member && ctx.chapter.account !== "none" && typeof member.source.clerkPrivateMetadataSyncedAt !== "number") {
1898
1902
  privateMetadataSynced = await syncApplicantPrivateMetadata(
@@ -6079,8 +6083,8 @@ function validateBuilderLibraryDocument(value, options = {}) {
6079
6083
  if (sourceType === "chapter" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== sourceAppId)) {
6080
6084
  errors.push(`document.resources.${resource.id}: Chapter sources may author only their own chapter scope`);
6081
6085
  }
6082
- if (sourceType === "built_not_found" && resource.scope.kind === "chapter") {
6083
- errors.push(`document.resources.${resource.id}: Built Not Found sources may author only built_not_found or global scope`);
6086
+ if (sourceType === "built_not_found" && resource.scope.kind === "chapter" && !(options.allowedChapterIds ?? []).includes(resource.scope.chapterId)) {
6087
+ errors.push(`document.resources.${resource.id}: Built Not Found may target only configured Chapters`);
6084
6088
  }
6085
6089
  }
6086
6090
  if (errors.length) return { ok: false, errors };
@@ -6240,12 +6244,13 @@ var handleBuilderLibraryControl = async (req, url, env, ctx) => {
6240
6244
  const checked = validateBuilderLibraryDocument(input.document, {
6241
6245
  sourceType,
6242
6246
  sourceAppId: verified.source.sourceId,
6243
- targetAppId: ctx.chapter.id
6247
+ targetAppId: ctx.chapter.id,
6248
+ ...ctx.chapter.mode === "chapter" ? { allowedChapterIds: [ctx.chapter.id] } : {}
6244
6249
  });
6245
6250
  if (!checked.ok) return json({ ok: false, reason: "invalid_document", errors: checked.errors }, 400);
6246
6251
  const document = checked.value;
6247
- if (ctx.chapter.mode === "chapter" && document.resources.some((resource) => resource.scope.kind !== "global")) {
6248
- return json({ ok: false, reason: "invalid_document", errors: ["Built Not Found may deliver only global resources to a Chapter"] }, 400);
6252
+ if (ctx.chapter.mode === "chapter" && document.resources.some((resource) => resource.scope.kind !== "global" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== ctx.chapter.id))) {
6253
+ return json({ ok: false, reason: "invalid_document", errors: ["Built Not Found may deliver only global or this Chapter's resources"] }, 400);
6249
6254
  }
6250
6255
  if (!document.target || document.target.environment !== env.ODLA_ENV) return json({ ok: false, reason: "wrong_environment" }, 409);
6251
6256
  if (!digest || await builderLibraryDigest(document) !== digest) return json({ ok: false, reason: "invalid_digest" }, 400);
@@ -6351,7 +6356,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
6351
6356
  source: { type: "built_not_found", appId: ctx.chapter.id },
6352
6357
  resources: [...local3?.document.resources ?? [], promoted]
6353
6358
  };
6354
- const checked2 = validateBuilderLibraryDocument(document, { sourceType: "built_not_found", sourceAppId: ctx.chapter.id });
6359
+ const checked2 = validateBuilderLibraryDocument(document, {
6360
+ sourceType: "built_not_found",
6361
+ sourceAppId: ctx.chapter.id,
6362
+ allowedChapterIds: ctx.chapter.builderLibrary.sources.map((source2) => source2.sourceId)
6363
+ });
6355
6364
  if (!checked2.ok) return json({ error: "promoted resource is invalid", errors: checked2.errors }, 400);
6356
6365
  try {
6357
6366
  const saved = await saveLocalBuilderLibrary(db, checked2.value, env.ODLA_ENV, expectedRevision2, now2, actorFor(user));
@@ -6366,6 +6375,7 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
6366
6375
  local: local3 ? { revision: local3.revision, document: local3.document } : null,
6367
6376
  sources: controls.map(({ sourceId, revision, digest, document, receivedAt }) => ({ sourceId, revision, digest, document, receivedAt })),
6368
6377
  localAuthoring: ctx.chapter.builderLibrary.localAuthoring,
6378
+ chapterTargets: ctx.chapter.mode === "hub" ? ctx.chapter.builderLibrary.sources.map((source) => ({ chapterId: source.sourceId })) : [{ chapterId: ctx.chapter.id }],
6369
6379
  visibility: { builtNotFoundSeesChapterSources: ctx.chapter.mode === "hub", chaptersSeeSiblingSources: false }
6370
6380
  });
6371
6381
  }
@@ -6384,7 +6394,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
6384
6394
  const now = Date.now();
6385
6395
  const actor = actorFor(user);
6386
6396
  const authoritative = withLocalAuthority2(input.document, ctx.chapter.mode, ctx.chapter.id, expectedRevision + 1, now, actor, local3?.document);
6387
- const checked = validateBuilderLibraryDocument(authoritative, { sourceType: ctx.chapter.mode === "hub" ? "built_not_found" : "chapter", sourceAppId: ctx.chapter.id });
6397
+ const checked = validateBuilderLibraryDocument(authoritative, {
6398
+ sourceType: ctx.chapter.mode === "hub" ? "built_not_found" : "chapter",
6399
+ sourceAppId: ctx.chapter.id,
6400
+ ...ctx.chapter.mode === "hub" ? { allowedChapterIds: ctx.chapter.builderLibrary.sources.map((source) => source.sourceId) } : {}
6401
+ });
6388
6402
  if (!checked.ok) return json({ error: "invalid Builder Library", errors: checked.errors }, 400);
6389
6403
  try {
6390
6404
  const saved = await saveLocalBuilderLibrary(db, checked.value, env.ODLA_ENV, expectedRevision, now, actor);