@sudobility/subscription_pages 0.0.3 → 0.0.4

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/dist/index.js CHANGED
@@ -1,48 +1,48 @@
1
- import { jsx as c } from "react/jsx-runtime";
2
- import { useState as I } from "react";
3
- import { usePackagesByDuration as N, useUserSubscription as H, getSubscriptionInstance as J, refreshSubscription as K, useAllOfferings as ee, useOfferingPackages as te, periodToMonths as G } from "@sudobility/subscription_lib";
4
- import { SubscriptionLayout as y, SubscriptionTile as Q, SegmentedControl as W } from "@sudobility/subscription-components";
5
- function oe({
1
+ import { jsx as i, jsxs as F } from "react/jsx-runtime";
2
+ import { useState as C } from "react";
3
+ import { usePackagesByDuration as ce, useUserSubscription as V, getSubscriptionInstance as X, refreshSubscription as Z, useAllOfferings as oe, useOfferingPackages as le, periodToMonths as J } from "@sudobility/subscription_lib";
4
+ import { SubscriptionLayout as N, SubscriptionTile as se, SegmentedControl as ee } from "@sudobility/subscription-components";
5
+ function he({
6
6
  isLoggedIn: n,
7
7
  onNavigateToLogin: s,
8
- userId: f,
8
+ userId: d,
9
9
  userEmail: o,
10
- featuresByPackage: i,
10
+ featuresByPackage: p,
11
11
  freeFeatures: u,
12
- title: g = "Choose Your Plan",
13
- className: m
12
+ title: f = "Choose Your Plan",
13
+ className: S
14
14
  }) {
15
15
  const {
16
- packagesByDuration: P,
17
- availableDurations: l,
18
- isLoading: k,
19
- error: B
20
- } = N(), {
21
- subscription: e,
22
- isLoading: t,
23
- error: F
24
- } = H({ userId: f, userEmail: o }), [T, h] = I(null), [O, w] = I(null), [A, E] = I(!1), S = T ?? l[0] ?? null, U = k || t, D = B || F, C = async (r, d) => {
16
+ packagesByDuration: I,
17
+ availableDurations: v,
18
+ isLoading: g,
19
+ error: P
20
+ } = ce(), {
21
+ subscription: r,
22
+ isLoading: M,
23
+ error: e
24
+ } = V({ userId: d, userEmail: o }), [O, R] = C(null), [b, x] = C(null), [T, k] = C(!1), y = O ?? v[0] ?? null, w = g || M, A = P || e, E = async (t, h) => {
25
25
  try {
26
- E(!0), w(null), await J().purchase({
27
- packageId: r,
28
- offeringId: d,
26
+ k(!0), x(null), await X().purchase({
27
+ packageId: t,
28
+ offeringId: h,
29
29
  customerEmail: o
30
- }), await K();
31
- } catch (v) {
32
- w(
33
- v instanceof Error ? v.message : "Purchase failed"
30
+ }), await Z();
31
+ } catch (l) {
32
+ x(
33
+ l instanceof Error ? l.message : "Purchase failed"
34
34
  );
35
35
  } finally {
36
- E(!1);
36
+ k(!1);
37
37
  }
38
- }, M = () => n ? (e == null ? void 0 : e.isActive) && e.packageId ? {
38
+ }, L = () => n ? (r == null ? void 0 : r.isActive) && r.packageId ? {
39
39
  title: "Free",
40
40
  price: "$0",
41
41
  features: u ?? [],
42
42
  ctaButton: {
43
43
  label: "Cancel Subscription",
44
44
  onClick: () => {
45
- e.managementUrl && window.open(e.managementUrl, "_blank");
45
+ r.managementUrl && window.open(r.managementUrl, "_blank");
46
46
  }
47
47
  }
48
48
  } : {
@@ -61,164 +61,175 @@ function oe({
61
61
  label: "Try it for Free",
62
62
  onClick: s
63
63
  }
64
- }, R = (r) => {
64
+ }, U = (t) => {
65
65
  if (!n)
66
66
  return {
67
67
  label: "Log in to Continue",
68
68
  onClick: s
69
69
  };
70
- const d = (e == null ? void 0 : e.isActive) && e.packageId;
71
- if ((e == null ? void 0 : e.packageId) !== r.package.packageId)
72
- return d ? {
70
+ const h = (r == null ? void 0 : r.isActive) && r.packageId;
71
+ if ((r == null ? void 0 : r.packageId) !== t.package.packageId)
72
+ return h ? {
73
73
  label: "Change Subscription",
74
- onClick: () => C(r.package.packageId, r.offerId)
74
+ onClick: () => E(t.package.packageId, t.offerId)
75
75
  } : {
76
76
  label: "Subscribe",
77
- onClick: () => C(r.package.packageId, r.offerId)
77
+ onClick: () => E(t.package.packageId, t.offerId)
78
78
  };
79
79
  };
80
- if (U)
81
- return /* @__PURE__ */ c(
82
- y,
80
+ if (w)
81
+ return /* @__PURE__ */ i(
82
+ N,
83
83
  {
84
- title: g,
85
- className: m,
84
+ title: f,
85
+ className: S,
86
86
  variant: "cta",
87
- children: /* @__PURE__ */ c("p", { children: "Loading subscription plans..." })
87
+ children: /* @__PURE__ */ i("p", { children: "Loading subscription plans..." })
88
88
  }
89
89
  );
90
- const Y = O ?? (D ? D.message : null), L = n && (e != null && e.isActive) && e.packageId ? {
90
+ const Y = b ?? (A ? A.message : null), j = n && (r != null && r.isActive) && r.packageId ? {
91
91
  isActive: !0,
92
92
  activeContent: {
93
93
  title: "Active Subscription",
94
94
  fields: [
95
- ...e.productId ? [{ label: "Plan", value: e.productId }] : [],
96
- ...e.expirationDate ? [
95
+ ...r.productId ? [{ label: "Plan", value: r.productId }] : [],
96
+ ...r.expirationDate ? [
97
97
  {
98
98
  label: "Expires",
99
- value: e.expirationDate.toLocaleDateString()
99
+ value: r.expirationDate.toLocaleDateString()
100
100
  }
101
101
  ] : [],
102
- ...e.willRenew !== void 0 ? [
102
+ ...r.willRenew !== void 0 ? [
103
103
  {
104
104
  label: "Auto-Renew",
105
- value: e.willRenew ? "Yes" : "No"
105
+ value: r.willRenew ? "Yes" : "No"
106
106
  }
107
107
  ] : []
108
108
  ]
109
109
  }
110
- } : void 0, $ = S ? P[S] ?? [] : [];
111
- return /* @__PURE__ */ c(
112
- y,
110
+ } : void 0, D = y ? I[y] ?? [] : [];
111
+ return /* @__PURE__ */ i(
112
+ N,
113
113
  {
114
- title: g,
115
- className: m,
114
+ title: f,
115
+ className: S,
116
116
  variant: "cta",
117
117
  error: Y,
118
- currentStatus: L,
119
- freeTileConfig: M(),
120
- aboveProducts: l.length > 1 ? /* @__PURE__ */ c(
121
- W,
118
+ currentStatus: j,
119
+ freeTileConfig: L(),
120
+ aboveProducts: v.length > 1 ? /* @__PURE__ */ i(
121
+ ee,
122
122
  {
123
- options: l.map((r) => ({
124
- value: r,
125
- label: r.charAt(0).toUpperCase() + r.slice(1)
123
+ options: v.map((t) => ({
124
+ value: t,
125
+ label: t.charAt(0).toUpperCase() + t.slice(1)
126
126
  })),
127
- value: S ?? l[0],
128
- onChange: (r) => h(r)
127
+ value: y ?? v[0],
128
+ onChange: (t) => R(t)
129
129
  }
130
130
  ) : void 0,
131
- children: $.map((r) => {
132
- var x;
133
- const d = n && (e == null ? void 0 : e.isActive) && e.packageId === r.package.packageId, v = R(r);
134
- return /* @__PURE__ */ c(
135
- Q,
131
+ children: D.map((t) => {
132
+ var $;
133
+ const h = n && (r == null ? void 0 : r.isActive) && r.packageId === t.package.packageId, l = U(t);
134
+ return /* @__PURE__ */ i(
135
+ se,
136
136
  {
137
- id: r.package.packageId,
138
- title: r.package.name,
139
- price: ((x = r.package.product) == null ? void 0 : x.priceString) ?? "$0",
140
- periodLabel: r.package.product ? `/${r.package.product.period}` : void 0,
141
- features: (i == null ? void 0 : i[r.package.packageId]) ?? [],
137
+ id: t.package.packageId,
138
+ title: t.package.name,
139
+ price: (($ = t.package.product) == null ? void 0 : $.priceString) ?? "$0",
140
+ periodLabel: t.package.product ? `/${t.package.product.period}` : void 0,
141
+ features: (p == null ? void 0 : p[t.package.packageId]) ?? [],
142
142
  isSelected: !1,
143
143
  onSelect: () => {
144
144
  },
145
- isCurrentPlan: d,
146
- ctaButton: v,
147
- disabled: A
145
+ isCurrentPlan: h,
146
+ ctaButton: l,
147
+ disabled: T
148
148
  },
149
- `${r.offerId}-${r.package.packageId}`
149
+ `${t.offerId}-${t.package.packageId}`
150
150
  );
151
151
  })
152
152
  }
153
153
  );
154
154
  }
155
- function re(n, s) {
155
+ function K(n) {
156
+ return n.charAt(0).toUpperCase() + n.slice(1);
157
+ }
158
+ const ue = {
159
+ weekly: "Weekly",
160
+ monthly: "Monthly",
161
+ quarterly: "Quarterly",
162
+ yearly: "Yearly",
163
+ lifetime: "Lifetime"
164
+ };
165
+ function de(n, s) {
156
166
  if (!n.product || !s.product) return null;
157
- const f = G(n.product.period), o = G(s.product.period);
158
- if (f <= 0 || o <= 0 || f === 1 / 0 || o === 1 / 0 || f === o) return null;
159
- const i = n.product.price / f, u = s.product.price / o;
160
- if (i <= 0) return null;
161
- const g = Math.round(
162
- (i - u) / i * 100
167
+ const d = J(n.product.period), o = J(s.product.period);
168
+ if (d <= 0 || o <= 0 || d === 1 / 0 || o === 1 / 0 || d === o) return null;
169
+ const p = n.product.price / d, u = s.product.price / o;
170
+ if (p <= 0) return null;
171
+ const f = Math.round(
172
+ (p - u) / p * 100
163
173
  );
164
- return g > 0 ? g : null;
174
+ return f > 0 ? f : null;
165
175
  }
166
- function le({
176
+ function ve({
167
177
  isLoggedIn: n,
168
178
  onNavigateToLogin: s,
169
- userId: f,
179
+ userId: d,
170
180
  userEmail: o,
171
- featuresByPackage: i,
181
+ featuresByPackage: p,
172
182
  freeFeatures: u,
173
- title: g = "Choose Your Plan",
174
- className: m,
175
- t: P
183
+ title: f = "Choose Your Plan",
184
+ className: S,
185
+ t: I,
186
+ renderOfferingContent: v
176
187
  }) {
177
- var V;
178
- const l = (a, p) => P ? P(a, p) : p, {
179
- offerings: k,
180
- isLoading: B,
181
- error: e
182
- } = ee(), {
183
- subscription: t,
184
- isLoading: F,
185
- error: T
186
- } = H({ userId: f, userEmail: o }), [h, O] = I("free"), [w, A] = I(null), [E, S] = I(!1), U = ((V = k[0]) == null ? void 0 : V.offerId) ?? "", D = h !== "free" ? h : U, {
187
- packages: C,
188
- isLoading: M,
188
+ var q;
189
+ const g = (a, c) => I ? I(a, c) : c, {
190
+ offerings: P,
191
+ isLoading: r,
192
+ error: M
193
+ } = oe(), {
194
+ subscription: e,
195
+ isLoading: O,
189
196
  error: R
190
- } = te(D), Y = B || F || M, L = e || T || R, $ = async (a, p) => {
197
+ } = V({ userId: d, userEmail: o }), [b, x] = C("free"), [T, k] = C(null), [y, w] = C(!1), A = ((q = P[0]) == null ? void 0 : q.offerId) ?? "", E = b !== "free" ? b : A, {
198
+ packages: L,
199
+ isLoading: U,
200
+ error: Y
201
+ } = le(E), j = r || O || U, D = M || R || Y, t = async (a, c) => {
191
202
  try {
192
- S(!0), A(null), await J().purchase({
203
+ w(!0), k(null), await X().purchase({
193
204
  packageId: a,
194
- offeringId: p,
205
+ offeringId: c,
195
206
  customerEmail: o
196
- }), await K();
197
- } catch (b) {
198
- A(
199
- b instanceof Error ? b.message : "Purchase failed"
207
+ }), await Z();
208
+ } catch (m) {
209
+ k(
210
+ m instanceof Error ? m.message : "Purchase failed"
200
211
  );
201
212
  } finally {
202
- S(!1);
213
+ w(!1);
203
214
  }
204
- }, r = [
205
- { value: "free", label: l("free", "Free") },
206
- ...k.map((a) => ({
215
+ }, h = [
216
+ { value: "free", label: g("free", "Free") },
217
+ ...P.map((a) => ({
207
218
  value: a.offerId,
208
- label: l(a.offerId, a.offerId.charAt(0).toUpperCase() + a.offerId.slice(1))
219
+ label: g(a.offerId, K(a.offerId))
209
220
  }))
210
- ], d = C[0] ?? null, v = () => n ? (t == null ? void 0 : t.isActive) && t.packageId ? {
211
- title: l("free", "Free"),
221
+ ], l = L[0] ?? null, $ = () => n ? (e == null ? void 0 : e.isActive) && e.packageId ? {
222
+ title: g("free", "Free"),
212
223
  price: "$0",
213
224
  features: u ?? [],
214
225
  ctaButton: {
215
226
  label: "Cancel Subscription",
216
227
  onClick: () => {
217
- t.managementUrl && window.open(t.managementUrl, "_blank");
228
+ e.managementUrl && window.open(e.managementUrl, "_blank");
218
229
  }
219
230
  }
220
231
  } : {
221
- title: l("free", "Free"),
232
+ title: g("free", "Free"),
222
233
  price: "$0",
223
234
  features: u ?? [],
224
235
  ctaButton: {
@@ -226,111 +237,103 @@ function le({
226
237
  },
227
238
  topBadge: { text: "Current Plan", color: "blue" }
228
239
  } : {
229
- title: l("free", "Free"),
240
+ title: g("free", "Free"),
230
241
  price: "$0",
231
242
  features: u ?? [],
232
243
  ctaButton: {
233
244
  label: "Try it for Free",
234
245
  onClick: s
235
246
  }
236
- }, x = (a, p) => {
237
- if (!n)
238
- return {
239
- label: "Log in to Continue",
240
- onClick: s
241
- };
242
- const b = (t == null ? void 0 : t.isActive) && t.packageId;
243
- if ((t == null ? void 0 : t.packageId) !== a.packageId)
244
- return b ? {
245
- label: "Change Subscription",
246
- onClick: () => $(a.packageId, p)
247
- } : {
248
- label: "Subscribe",
249
- onClick: () => $(a.packageId, p)
250
- };
247
+ }, re = (a) => n ? (e == null ? void 0 : e.packageId) === a.packageId ? "Current Plan" : (e == null ? void 0 : e.isActive) && e.packageId ? "Change Subscription" : "Subscribe" : "Log in to Continue", te = (a, c) => {
248
+ if (!n) return s;
249
+ if ((e == null ? void 0 : e.packageId) !== a.packageId)
250
+ return () => t(a.packageId, c);
251
251
  };
252
- if (Y)
253
- return /* @__PURE__ */ c(
254
- y,
252
+ if (j)
253
+ return /* @__PURE__ */ i(
254
+ N,
255
255
  {
256
- title: g,
257
- className: m,
256
+ title: f,
257
+ className: S,
258
258
  variant: "cta",
259
- children: /* @__PURE__ */ c("p", { children: "Loading subscription plans..." })
259
+ children: /* @__PURE__ */ i("p", { children: "Loading subscription plans..." })
260
260
  }
261
261
  );
262
- const X = w ?? (L ? L.message : null), Z = n && (t != null && t.isActive) && t.packageId ? {
262
+ const ae = T ?? (D ? D.message : null), ne = n && (e != null && e.isActive) && e.packageId ? {
263
263
  isActive: !0,
264
264
  activeContent: {
265
265
  title: "Active Subscription",
266
266
  fields: [
267
- ...t.productId ? [{ label: "Plan", value: t.productId }] : [],
268
- ...t.expirationDate ? [
267
+ ...e.productId ? [{ label: "Plan", value: e.productId }] : [],
268
+ ...e.expirationDate ? [
269
269
  {
270
270
  label: "Expires",
271
- value: t.expirationDate.toLocaleDateString()
271
+ value: e.expirationDate.toLocaleDateString()
272
272
  }
273
273
  ] : [],
274
- ...t.willRenew !== void 0 ? [
274
+ ...e.willRenew !== void 0 ? [
275
275
  {
276
276
  label: "Auto-Renew",
277
- value: t.willRenew ? "Yes" : "No"
277
+ value: e.willRenew ? "Yes" : "No"
278
278
  }
279
279
  ] : []
280
280
  ]
281
281
  }
282
- } : void 0, _ = h === "free";
283
- return /* @__PURE__ */ c(
284
- y,
282
+ } : void 0, _ = b === "free";
283
+ return /* @__PURE__ */ i(
284
+ N,
285
285
  {
286
- title: g,
287
- className: m,
286
+ title: f,
287
+ className: S,
288
288
  variant: "cta",
289
- error: X,
290
- currentStatus: Z,
291
- freeTileConfig: _ ? v() : void 0,
292
- aboveProducts: r.length > 1 ? /* @__PURE__ */ c("div", { className: "flex justify-center", children: /* @__PURE__ */ c(
293
- W,
289
+ error: ae,
290
+ currentStatus: ne,
291
+ freeTileConfig: _ ? $() : void 0,
292
+ aboveProducts: h.length > 1 ? /* @__PURE__ */ i("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ i(
293
+ ee,
294
294
  {
295
- options: r,
296
- value: h,
297
- onChange: O
295
+ options: h,
296
+ value: b,
297
+ onChange: x
298
298
  }
299
299
  ) }) : void 0,
300
- children: !_ && C.map((a) => {
301
- var q;
302
- const p = n && (t == null ? void 0 : t.isActive) && t.packageId === a.packageId, b = x(a, h);
303
- let j;
304
- if (d && a.packageId !== d.packageId) {
305
- const z = re(d, a);
306
- z !== null && (j = {
307
- text: `Save ${z}%`,
308
- isBestValue: a === C[C.length - 1]
309
- });
310
- }
311
- return /* @__PURE__ */ c(
312
- Q,
313
- {
314
- id: a.packageId,
315
- title: a.name,
316
- price: ((q = a.product) == null ? void 0 : q.priceString) ?? "$0",
317
- periodLabel: a.product ? `/${a.product.period}` : void 0,
318
- features: (i == null ? void 0 : i[a.packageId]) ?? [],
319
- isSelected: !1,
320
- onSelect: () => {
300
+ children: !_ && /* @__PURE__ */ F("div", { className: "col-span-full space-y-6", children: [
301
+ v && /* @__PURE__ */ i("div", { className: "rounded-xl bg-gray-50 dark:bg-gray-800/50 border border-gray-200 dark:border-gray-700 p-5", children: v(b) }),
302
+ /* @__PURE__ */ i("div", { className: "space-y-3", children: L.map((a) => {
303
+ var G, H;
304
+ const c = (G = a.product) == null ? void 0 : G.period, m = c ? g(c, ue[c] ?? K(c)) : a.name, B = n && (e == null ? void 0 : e.isActive) && e.packageId === a.packageId, z = l && a.packageId !== l.packageId ? de(l, a) : null, Q = re(a), W = te(a, b), ie = ((H = a.product) == null ? void 0 : H.priceString) ?? "$0";
305
+ return /* @__PURE__ */ F(
306
+ "div",
307
+ {
308
+ className: "flex items-center justify-between rounded-xl p-4 transition-all " + (B ? "bg-blue-50 dark:bg-blue-900/20 border-2 border-blue-500 dark:border-blue-400" : "bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700"),
309
+ children: [
310
+ /* @__PURE__ */ F("div", { className: "min-w-0 flex-1", children: [
311
+ /* @__PURE__ */ i("p", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: m }),
312
+ z !== null ? /* @__PURE__ */ F("p", { className: "text-sm font-medium text-green-600 dark:text-green-400", children: [
313
+ "Save ",
314
+ z,
315
+ "%"
316
+ ] }) : B ? /* @__PURE__ */ i("p", { className: "text-sm font-medium text-blue-600 dark:text-blue-400", children: "Current Plan" }) : null
317
+ ] }),
318
+ /* @__PURE__ */ i(
319
+ "button",
320
+ {
321
+ onClick: W,
322
+ disabled: y || !W,
323
+ className: "ml-4 flex-shrink-0 rounded-lg px-4 py-2 text-sm font-semibold transition-colors " + (B ? "bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300 cursor-default" : "bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"),
324
+ children: B ? Q : `${ie} · ${Q}`
325
+ }
326
+ )
327
+ ]
321
328
  },
322
- isCurrentPlan: p,
323
- ctaButton: b,
324
- disabled: E,
325
- discountBadge: j
326
- },
327
- a.packageId
328
- );
329
- })
329
+ a.packageId
330
+ );
331
+ }) })
332
+ ] })
330
333
  }
331
334
  );
332
335
  }
333
336
  export {
334
- oe as SubscriptionByDurationPage,
335
- le as SubscriptionByOfferPage
337
+ he as SubscriptionByDurationPage,
338
+ ve as SubscriptionByOfferPage
336
339
  };
@@ -1,11 +1,4 @@
1
- /**
2
- * SubscriptionByOfferPage
3
- *
4
- * Subscription page that organizes packages by offering.
5
- * Uses a SegmentedControl to switch between offerings (with a 'Free' option).
6
- * Packages within each offer are sorted by duration (short to long).
7
- * Savings are calculated relative to the shortest duration package.
8
- */
1
+ import { default as React } from 'react';
9
2
  export interface SubscriptionByOfferPageProps {
10
3
  /** Whether the user is logged in */
11
4
  isLoggedIn: boolean;
@@ -24,10 +17,17 @@ export interface SubscriptionByOfferPageProps {
24
17
  /** Additional CSS classes */
25
18
  className?: string;
26
19
  /**
27
- * Translation function for localizing offer names and labels.
28
- * Keys passed: offer identifiers (e.g. "basic", "premium"), "free".
20
+ * Translation function for localizing offer names, period labels, etc.
21
+ * Keys passed: offer identifiers (e.g. "basic", "premium"), "free",
22
+ * period names (e.g. "monthly", "yearly").
29
23
  * Falls back to the fallback string if not provided.
30
24
  */
31
25
  t?: (key: string, fallback: string) => string;
26
+ /**
27
+ * Render content describing what an offering includes.
28
+ * Called with the offering identifier (e.g. "basic", "premium").
29
+ * Return a list of strings or React elements shown in a styled container.
30
+ */
31
+ renderOfferingContent?: (offerId: string) => React.ReactNode;
32
32
  }
33
- export declare function SubscriptionByOfferPage({ isLoggedIn, onNavigateToLogin, userId, userEmail, featuresByPackage, freeFeatures, title, className, t: translate, }: SubscriptionByOfferPageProps): import("react/jsx-runtime").JSX.Element;
33
+ export declare function SubscriptionByOfferPage({ isLoggedIn, onNavigateToLogin, userId, userEmail, featuresByPackage: _featuresByPackage, freeFeatures, title, className, t: translate, renderOfferingContent, }: SubscriptionByOfferPageProps): import("react/jsx-runtime").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/subscription_pages",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Subscription page components for React web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",