@tribe-nest/forge 3.26.0 → 3.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/client/_tests/localeHeader.spec.tsx +98 -0
- package/src/client/activeLocale.ts +34 -0
- package/src/client/createForgeClient.ts +13 -0
- package/src/contexts/CartContext.tsx +76 -0
- package/src/contexts/_tests/CartContext.spec.tsx +210 -0
- package/src/data/queries/useCheckouts.ts +83 -0
- package/src/data/queries/useWebsite.ts +7 -0
- package/src/i18n/CONVENTION.md +184 -0
- package/src/i18n/_tests/translationKeys.spec.ts +153 -0
- package/src/i18n/de.json +960 -0
- package/src/i18n/en.json +960 -0
- package/src/i18n/index.ts +125 -0
- package/src/index.ts +21 -0
- package/src/provider/ForgeAppProvider.tsx +2 -1
- package/src/provider/SiteConfigProvider.tsx +11 -0
- package/src/ui/headless/checkout/_tests/cartResume.spec.ts +291 -0
- package/src/ui/headless/checkout/_tests/useCartResume.spec.tsx +294 -0
- package/src/ui/headless/checkout/resumedCoupon.ts +61 -0
- package/src/ui/headless/checkout/useCartResume.ts +340 -0
- package/src/ui/headless/checkout/useCheckout.ts +9 -2
- package/src/ui/headless/index.ts +18 -0
- package/src/ui/index.ts +2 -0
- package/src/ui/shell/TribeNestApp.tsx +36 -27
- package/src/ui/styled/AccountDashboard.tsx +197 -93
- package/src/ui/styled/AiAgentWidget.tsx +10 -6
- package/src/ui/styled/AudioPlayer.tsx +14 -6
- package/src/ui/styled/BlogCategory.tsx +4 -2
- package/src/ui/styled/BlogComments.tsx +27 -20
- package/src/ui/styled/BlogList.tsx +5 -3
- package/src/ui/styled/BlogPost.tsx +14 -11
- package/src/ui/styled/BundleConfirmation.tsx +32 -23
- package/src/ui/styled/CancellationTerms.tsx +3 -1
- package/src/ui/styled/Cart.tsx +32 -14
- package/src/ui/styled/ChatRoom.tsx +65 -20
- package/src/ui/styled/Checkout.tsx +151 -89
- package/src/ui/styled/CheckoutConfirmation.tsx +66 -35
- package/src/ui/styled/CoachingBooking.tsx +71 -44
- package/src/ui/styled/CoachingConfirmation.tsx +56 -19
- package/src/ui/styled/CoachingDetail.tsx +15 -11
- package/src/ui/styled/CohortPage.tsx +10 -8
- package/src/ui/styled/ConfirmSubscription.tsx +14 -8
- package/src/ui/styled/ContactForm.tsx +31 -12
- package/src/ui/styled/CookieConsent.tsx +19 -15
- package/src/ui/styled/CourseAccess.tsx +54 -47
- package/src/ui/styled/CourseCheckout.tsx +56 -38
- package/src/ui/styled/CourseConfirmation.tsx +39 -17
- package/src/ui/styled/CourseDetail.tsx +21 -15
- package/src/ui/styled/CoursesGrid.tsx +4 -2
- package/src/ui/styled/CurrencySwitcher.tsx +4 -1
- package/src/ui/styled/DiscountCode.tsx +22 -18
- package/src/ui/styled/DonationButton.tsx +52 -17
- package/src/ui/styled/DonationPage.tsx +4 -4
- package/src/ui/styled/EmailListForm.tsx +22 -9
- package/src/ui/styled/EventConfirmation.tsx +37 -14
- package/src/ui/styled/EventCountdown.tsx +10 -6
- package/src/ui/styled/EventDetail.tsx +8 -6
- package/src/ui/styled/EventSeriesDetail.tsx +18 -13
- package/src/ui/styled/EventTickets.tsx +232 -108
- package/src/ui/styled/EventWaitlist.tsx +42 -33
- package/src/ui/styled/EventsList.tsx +15 -13
- package/src/ui/styled/ForgotPasswordForm.tsx +12 -12
- package/src/ui/styled/FormRenderer.tsx +12 -7
- package/src/ui/styled/InstallBanner.tsx +21 -18
- package/src/ui/styled/InvoiceConfirmation.tsx +13 -5
- package/src/ui/styled/InvoicePayment.tsx +37 -16
- package/src/ui/styled/LeadMagnet.tsx +9 -7
- package/src/ui/styled/Loading.tsx +7 -1
- package/src/ui/styled/LoginForm.tsx +22 -14
- package/src/ui/styled/MembershipCheckout.tsx +32 -13
- package/src/ui/styled/MembershipTiers.tsx +27 -18
- package/src/ui/styled/OfferButton.tsx +33 -13
- package/src/ui/styled/PaymentLinkConfirmation.tsx +11 -5
- package/src/ui/styled/PaymentLinkPayment.tsx +30 -10
- package/src/ui/styled/PodcastEpisode.tsx +20 -9
- package/src/ui/styled/PodcastList.tsx +9 -3
- package/src/ui/styled/PodcastShow.tsx +29 -7
- package/src/ui/styled/PostsFeed.tsx +10 -8
- package/src/ui/styled/PresaleCode.tsx +8 -11
- package/src/ui/styled/ProductBrowseNav.tsx +21 -11
- package/src/ui/styled/ProductDetail.tsx +58 -28
- package/src/ui/styled/ReactionBar.tsx +4 -2
- package/src/ui/styled/ReplayList.tsx +24 -13
- package/src/ui/styled/ResetPasswordForm.tsx +27 -15
- package/src/ui/styled/ResumeCart.tsx +289 -0
- package/src/ui/styled/ReviewForm.tsx +52 -58
- package/src/ui/styled/SignupForm.tsx +29 -16
- package/src/ui/styled/TicketTransfer.tsx +55 -54
- package/src/ui/styled/UserMenu.tsx +16 -14
- package/src/ui/styled/_tests/ResumeCart.spec.tsx +183 -0
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from "../../../data/queries/useCheckouts";
|
|
21
21
|
import { bundleCouponSummary, bundleReturnUrl } from "./bundleCoupon";
|
|
22
22
|
import { holdExpiredMessage, isHoldExpiredError } from "./inventoryHold";
|
|
23
|
+
import { takeResumedCoupon } from "./resumedCoupon";
|
|
23
24
|
import { useInventoryHold } from "./useInventoryHold";
|
|
24
25
|
import { readAttributionRef } from "../../../utils/attribution";
|
|
25
26
|
import { readLanding } from "../../../utils/landing";
|
|
@@ -191,8 +192,14 @@ export function useCheckout(opts: UseCheckoutOptions = {}) {
|
|
|
191
192
|
[selectedShippingRates],
|
|
192
193
|
);
|
|
193
194
|
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
/**
|
|
196
|
+
* A code the buyer arrived with from a recovery link, prefilled but NOT
|
|
197
|
+
* applied: they still press Apply and the server re-evaluates it against
|
|
198
|
+
* today. The lazy initialiser consumes the stash, so a code cannot linger and
|
|
199
|
+
* prefill an unrelated checkout later in the same tab.
|
|
200
|
+
*/
|
|
201
|
+
const [couponCode, setCouponCode] = useState(takeResumedCoupon);
|
|
202
|
+
const [showCouponInput, setShowCouponInput] = useState(() => couponCode.length > 0);
|
|
196
203
|
const [couponError, setCouponError] = useState("");
|
|
197
204
|
const [isApplyingCoupon, setIsApplyingCoupon] = useState(false);
|
|
198
205
|
const [appliedCoupon, setAppliedCoupon] = useState<AppliedCoupon | null>(null);
|
package/src/ui/headless/index.ts
CHANGED
|
@@ -29,6 +29,24 @@ export {
|
|
|
29
29
|
type InventoryHoldState,
|
|
30
30
|
type UseInventoryHoldOptions,
|
|
31
31
|
} from "./checkout/useInventoryHold";
|
|
32
|
+
// Cart recovery: what a resume link does when the buyer clicks it. Shared so
|
|
33
|
+
// `apps/client` and a Forge code site cannot decide differently.
|
|
34
|
+
export {
|
|
35
|
+
useCartResume,
|
|
36
|
+
effectiveUnitCents,
|
|
37
|
+
partitionResumeLines,
|
|
38
|
+
resumeLineToCartItem,
|
|
39
|
+
resumeLinesToTicketItems,
|
|
40
|
+
type ResumePartition,
|
|
41
|
+
type UseCartResumeOptions,
|
|
42
|
+
type UseCartResumeResult,
|
|
43
|
+
type CartResumeStatus,
|
|
44
|
+
type ResumeLine,
|
|
45
|
+
type ResumedCheckout,
|
|
46
|
+
type ResumeLineOption,
|
|
47
|
+
type ResumeLineUnavailableReason,
|
|
48
|
+
} from "./checkout/useCartResume";
|
|
49
|
+
export { rememberResumedCoupon, takeResumedCoupon } from "./checkout/resumedCoupon";
|
|
32
50
|
export {
|
|
33
51
|
isHoldExpiredError,
|
|
34
52
|
holdExpiredMessage,
|
package/src/ui/index.ts
CHANGED
|
@@ -55,6 +55,8 @@ export { PresaleCodeField, type PresaleCodeFieldProps } from "./styled/PresaleCo
|
|
|
55
55
|
export { MembershipCheckout, type MembershipCheckoutProps } from "./styled/MembershipCheckout";
|
|
56
56
|
export { ForgeStripePayment } from "./payment/ForgeStripePayment";
|
|
57
57
|
export { Cart, type CartProps } from "./styled/Cart";
|
|
58
|
+
/** The page a cart-recovery email lands on. Drop in at `/i/checkout/resume`. */
|
|
59
|
+
export { ResumeCart, type ResumeCartProps } from "./styled/ResumeCart";
|
|
58
60
|
// The resolver is exported alongside the component because the Craft themes
|
|
59
61
|
// are a separate rendering stack (Tailwind, not inline styles) and cannot use
|
|
60
62
|
// the component — but the legacy `color`/`size` fallback must not diverge.
|
|
@@ -8,6 +8,7 @@ import { CookieConsent } from "../styled/CookieConsent";
|
|
|
8
8
|
import { AiAgentWidget } from "../styled/AiAgentWidget";
|
|
9
9
|
import { useThemeTokens } from "../theme/ForgeThemeProvider";
|
|
10
10
|
import { useInitialSiteConfig } from "../../provider/SiteConfigProvider";
|
|
11
|
+
import { ForgeI18nProvider } from "../../i18n";
|
|
11
12
|
import { PoweredBy } from "./PoweredBy";
|
|
12
13
|
import { PreviewDiagnostics } from "./PreviewDiagnostics";
|
|
13
14
|
import { shellAiAgentEnabled, shellPoweredByEnabled, shellPwaEnabled } from "./shellGating";
|
|
@@ -142,33 +143,41 @@ export function TribeNestApp({
|
|
|
142
143
|
: null;
|
|
143
144
|
return (
|
|
144
145
|
<ForgeProvider editable={editable} {...forgeProps}>
|
|
145
|
-
{/*
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
{
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
146
|
+
{/* Forge's own copy, in the site's language. Mounted here rather than in
|
|
147
|
+
each tenant's __root.tsx, so a Forge upgrade is enough to give an
|
|
148
|
+
existing site i18n. `locale` is the ForgeProvider prop the site
|
|
149
|
+
already passes (absent or unsupported resolves to English). When
|
|
150
|
+
`profile_configurations.locale` lands on SiteConfig, that becomes the
|
|
151
|
+
source and this prop stays the override. */}
|
|
152
|
+
<ForgeI18nProvider locale={forgeProps.locale}>
|
|
153
|
+
{/* First in the tree: it must render even if everything below it doesn't. */}
|
|
154
|
+
{ssrFailure && <PreviewDiagnostics diagnostics={ssrFailure} />}
|
|
155
|
+
{children}
|
|
156
|
+
{/* "Powered by TribeNest" — last in the page flow, so it sits below the
|
|
157
|
+
tenant's own footer. Released builds only (live + published). */}
|
|
158
|
+
<PoweredByGate editable={editable} state={state} />
|
|
159
|
+
{/* Global playback bar — self-pins to the viewport bottom, renders nothing
|
|
160
|
+
until a track loads. */}
|
|
161
|
+
<AudioPlayer />
|
|
162
|
+
{/* Auto page views + clicks (+ Meta Pixel, gated on marketing consent).
|
|
163
|
+
First-party runs cookieless. Off in the editor so preview isn't counted. */}
|
|
164
|
+
{analytics && <ForgeAnalytics enabled={!editable} firstPartyMode="cookieless" />}
|
|
165
|
+
{/* Ad attribution capture (tracked-link ref + first-touch landing + visit
|
|
166
|
+
beacon). Off in the editor so preview visits aren't credited. */}
|
|
167
|
+
<AttributionCapture enabled={!editable} apiUrl={forgeProps.apiUrl} profileId={forgeProps.profileId} />
|
|
168
|
+
{/* PWA — SW registration + install banner + (fallback) manifest link. */}
|
|
169
|
+
{pwa && <PwaRegistration enabled={pwaEnabled} />}
|
|
170
|
+
{pwa && <InstallBanner enabled={pwaEnabled} />}
|
|
171
|
+
{pwa && <PwaHeadClient enabled={pwaEnabled} manifestHref={manifestHref} />}
|
|
172
|
+
{/* Consent banner — shown on first visit, re-openable from anywhere via
|
|
173
|
+
useCookieConsent().reopen(). Hidden in the editor. */}
|
|
174
|
+
{cookieConsent && !editable && <CookieConsent useTribeNestPrivacy />}
|
|
175
|
+
{/* Website AI agent — floating bubble, self-hiding when the profile has no
|
|
176
|
+
agent enabled. Lives here rather than in each __root so enabling the
|
|
177
|
+
agent in admin is enough to make it appear; before this it shipped only
|
|
178
|
+
on the Craft stack and no code site ever rendered it. */}
|
|
179
|
+
{shellAiAgentEnabled({ aiAgent, editable, appId: forgeProps.appId }) && <AiAgentWidget />}
|
|
180
|
+
</ForgeI18nProvider>
|
|
172
181
|
</ForgeProvider>
|
|
173
182
|
);
|
|
174
183
|
}
|