@sudobility/subscription_pages 0.0.2 → 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,44 +1,44 @@
1
- import { jsx as n } from "react/jsx-runtime";
2
- import { useState as g } from "react";
3
- import { usePackagesByDuration as M, useUserSubscription as z, getSubscriptionInstance as G, refreshSubscription as H, useAllOfferings as Q, useOfferingPackages as V } from "@sudobility/subscription_lib";
4
- import { SubscriptionLayout as F, SubscriptionTile as J, SegmentedControl as K } from "@sudobility/subscription-components";
5
- function ee({
6
- isLoggedIn: i,
7
- onNavigateToLogin: v,
8
- userId: y,
9
- userEmail: b,
10
- featuresByPackage: o,
11
- freeFeatures: l,
12
- title: C = "Choose Your Plan",
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
+ isLoggedIn: n,
7
+ onNavigateToLogin: s,
8
+ userId: d,
9
+ userEmail: o,
10
+ featuresByPackage: p,
11
+ freeFeatures: u,
12
+ title: f = "Choose Your Plan",
13
13
  className: S
14
14
  }) {
15
15
  const {
16
- packagesByDuration: m,
17
- availableDurations: p,
18
- isLoading: O,
19
- error: e
20
- } = M(), {
16
+ packagesByDuration: I,
17
+ availableDurations: v,
18
+ isLoading: g,
19
+ error: P
20
+ } = ce(), {
21
21
  subscription: r,
22
- isLoading: T,
23
- error: s
24
- } = z({ userId: y, userEmail: b }), [B, R] = g(null), [P, k] = g(null), [w, A] = g(!1), h = B ?? p[0] ?? null, U = O || T, E = e || s, D = async (t, u) => {
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
- A(!0), k(null), await G().purchase({
26
+ k(!0), x(null), await X().purchase({
27
27
  packageId: t,
28
- offeringId: u,
29
- customerEmail: b
30
- }), await H();
31
- } catch (c) {
32
- k(
33
- c instanceof Error ? c.message : "Purchase failed"
28
+ offeringId: h,
29
+ customerEmail: o
30
+ }), await Z();
31
+ } catch (l) {
32
+ x(
33
+ l instanceof Error ? l.message : "Purchase failed"
34
34
  );
35
35
  } finally {
36
- A(!1);
36
+ k(!1);
37
37
  }
38
- }, Y = () => i ? (r == null ? void 0 : r.isActive) && r.packageId ? {
38
+ }, L = () => n ? (r == null ? void 0 : r.isActive) && r.packageId ? {
39
39
  title: "Free",
40
40
  price: "$0",
41
- features: l ?? [],
41
+ features: u ?? [],
42
42
  ctaButton: {
43
43
  label: "Cancel Subscription",
44
44
  onClick: () => {
@@ -48,7 +48,7 @@ function ee({
48
48
  } : {
49
49
  title: "Free",
50
50
  price: "$0",
51
- features: l ?? [],
51
+ features: u ?? [],
52
52
  ctaButton: {
53
53
  label: "Current Plan"
54
54
  },
@@ -56,38 +56,38 @@ function ee({
56
56
  } : {
57
57
  title: "Free",
58
58
  price: "$0",
59
- features: l ?? [],
59
+ features: u ?? [],
60
60
  ctaButton: {
61
61
  label: "Try it for Free",
62
- onClick: v
62
+ onClick: s
63
63
  }
64
- }, L = (t) => {
65
- if (!i)
64
+ }, U = (t) => {
65
+ if (!n)
66
66
  return {
67
67
  label: "Log in to Continue",
68
- onClick: v
68
+ onClick: s
69
69
  };
70
- const u = (r == null ? void 0 : r.isActive) && r.packageId;
70
+ const h = (r == null ? void 0 : r.isActive) && r.packageId;
71
71
  if ((r == null ? void 0 : r.packageId) !== t.package.packageId)
72
- return u ? {
72
+ return h ? {
73
73
  label: "Change Subscription",
74
- onClick: () => D(t.package.packageId, t.offerId)
74
+ onClick: () => E(t.package.packageId, t.offerId)
75
75
  } : {
76
76
  label: "Subscribe",
77
- onClick: () => D(t.package.packageId, t.offerId)
77
+ onClick: () => E(t.package.packageId, t.offerId)
78
78
  };
79
79
  };
80
- if (U)
81
- return /* @__PURE__ */ n(
82
- F,
80
+ if (w)
81
+ return /* @__PURE__ */ i(
82
+ N,
83
83
  {
84
- title: C,
84
+ title: f,
85
85
  className: S,
86
86
  variant: "cta",
87
- children: /* @__PURE__ */ n("p", { children: "Loading subscription plans..." })
87
+ children: /* @__PURE__ */ i("p", { children: "Loading subscription plans..." })
88
88
  }
89
89
  );
90
- const $ = P ?? (E ? E.message : null), x = i && (r != null && r.isActive) && r.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",
@@ -107,44 +107,44 @@ function ee({
107
107
  ] : []
108
108
  ]
109
109
  }
110
- } : void 0, _ = h ? m[h] ?? [] : [];
111
- return /* @__PURE__ */ n(
112
- F,
110
+ } : void 0, D = y ? I[y] ?? [] : [];
111
+ return /* @__PURE__ */ i(
112
+ N,
113
113
  {
114
- title: C,
114
+ title: f,
115
115
  className: S,
116
116
  variant: "cta",
117
- error: $,
118
- currentStatus: x,
119
- freeTileConfig: Y(),
120
- aboveProducts: p.length > 1 ? /* @__PURE__ */ n(
121
- K,
117
+ error: Y,
118
+ currentStatus: j,
119
+ freeTileConfig: L(),
120
+ aboveProducts: v.length > 1 ? /* @__PURE__ */ i(
121
+ ee,
122
122
  {
123
- options: p.map((t) => ({
123
+ options: v.map((t) => ({
124
124
  value: t,
125
125
  label: t.charAt(0).toUpperCase() + t.slice(1)
126
126
  })),
127
- value: h ?? p[0],
127
+ value: y ?? v[0],
128
128
  onChange: (t) => R(t)
129
129
  }
130
130
  ) : void 0,
131
- children: _.map((t) => {
132
- var I;
133
- const u = i && (r == null ? void 0 : r.isActive) && r.packageId === t.package.packageId, c = L(t);
134
- return /* @__PURE__ */ n(
135
- J,
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
137
  id: t.package.packageId,
138
138
  title: t.package.name,
139
- price: ((I = t.package.product) == null ? void 0 : I.priceString) ?? "$0",
139
+ price: (($ = t.package.product) == null ? void 0 : $.priceString) ?? "$0",
140
140
  periodLabel: t.package.product ? `/${t.package.product.period}` : void 0,
141
- features: (o == null ? void 0 : o[t.package.packageId]) ?? [],
141
+ features: (p == null ? void 0 : p[t.package.packageId]) ?? [],
142
142
  isSelected: !1,
143
143
  onSelect: () => {
144
144
  },
145
- isCurrentPlan: u,
146
- ctaButton: c,
147
- disabled: w
145
+ isCurrentPlan: h,
146
+ ctaButton: l,
147
+ disabled: T
148
148
  },
149
149
  `${t.offerId}-${t.package.packageId}`
150
150
  );
@@ -152,53 +152,76 @@ function ee({
152
152
  }
153
153
  );
154
154
  }
155
- function re({
156
- isLoggedIn: i,
157
- onNavigateToLogin: v,
158
- userId: y,
159
- userEmail: b,
160
- featuresByPackage: o,
161
- freeFeatures: l,
162
- title: C = "Choose Your Plan",
163
- className: 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) {
166
+ if (!n.product || !s.product) return null;
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
173
+ );
174
+ return f > 0 ? f : null;
175
+ }
176
+ function ve({
177
+ isLoggedIn: n,
178
+ onNavigateToLogin: s,
179
+ userId: d,
180
+ userEmail: o,
181
+ featuresByPackage: p,
182
+ freeFeatures: u,
183
+ title: f = "Choose Your Plan",
184
+ className: S,
185
+ t: I,
186
+ renderOfferingContent: v
164
187
  }) {
165
188
  var q;
166
- const {
167
- offerings: m,
168
- isLoading: p,
169
- error: O
170
- } = Q(), {
171
- subscription: e,
189
+ const g = (a, c) => I ? I(a, c) : c, {
190
+ offerings: P,
172
191
  isLoading: r,
173
- error: T
174
- } = z({ userId: y, userEmail: b }), [s, B] = g("free"), [R, P] = g(null), [k, w] = g(!1), A = ((q = m[0]) == null ? void 0 : q.offerId) ?? "", h = s !== "free" ? s : A, {
175
- packages: U,
176
- isLoading: E,
177
- error: D
178
- } = V(h), Y = p || r || E, L = O || T || D, $ = async (a, f) => {
192
+ error: M
193
+ } = oe(), {
194
+ subscription: e,
195
+ isLoading: O,
196
+ error: R
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) => {
179
202
  try {
180
- w(!0), P(null), await G().purchase({
203
+ w(!0), k(null), await X().purchase({
181
204
  packageId: a,
182
- offeringId: f,
183
- customerEmail: b
184
- }), await H();
185
- } catch (d) {
186
- P(
187
- d instanceof Error ? d.message : "Purchase failed"
205
+ offeringId: c,
206
+ customerEmail: o
207
+ }), await Z();
208
+ } catch (m) {
209
+ k(
210
+ m instanceof Error ? m.message : "Purchase failed"
188
211
  );
189
212
  } finally {
190
213
  w(!1);
191
214
  }
192
- }, x = [
193
- { value: "free", label: "Free" },
194
- ...m.map((a) => ({
215
+ }, h = [
216
+ { value: "free", label: g("free", "Free") },
217
+ ...P.map((a) => ({
195
218
  value: a.offerId,
196
- label: a.offerId
219
+ label: g(a.offerId, K(a.offerId))
197
220
  }))
198
- ], _ = () => i ? (e == null ? void 0 : e.isActive) && e.packageId ? {
199
- title: "Free",
221
+ ], l = L[0] ?? null, $ = () => n ? (e == null ? void 0 : e.isActive) && e.packageId ? {
222
+ title: g("free", "Free"),
200
223
  price: "$0",
201
- features: l ?? [],
224
+ features: u ?? [],
202
225
  ctaButton: {
203
226
  label: "Cancel Subscription",
204
227
  onClick: () => {
@@ -206,48 +229,37 @@ function re({
206
229
  }
207
230
  }
208
231
  } : {
209
- title: "Free",
232
+ title: g("free", "Free"),
210
233
  price: "$0",
211
- features: l ?? [],
234
+ features: u ?? [],
212
235
  ctaButton: {
213
236
  label: "Current Plan"
214
237
  },
215
238
  topBadge: { text: "Current Plan", color: "blue" }
216
239
  } : {
217
- title: "Free",
240
+ title: g("free", "Free"),
218
241
  price: "$0",
219
- features: l ?? [],
242
+ features: u ?? [],
220
243
  ctaButton: {
221
244
  label: "Try it for Free",
222
- onClick: v
245
+ onClick: s
223
246
  }
224
- }, t = (a, f) => {
225
- if (!i)
226
- return {
227
- label: "Log in to Continue",
228
- onClick: v
229
- };
230
- const d = (e == null ? void 0 : e.isActive) && e.packageId;
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;
231
249
  if ((e == null ? void 0 : e.packageId) !== a.packageId)
232
- return d ? {
233
- label: "Change Subscription",
234
- onClick: () => $(a.packageId, f)
235
- } : {
236
- label: "Subscribe",
237
- onClick: () => $(a.packageId, f)
238
- };
250
+ return () => t(a.packageId, c);
239
251
  };
240
- if (Y)
241
- return /* @__PURE__ */ n(
242
- F,
252
+ if (j)
253
+ return /* @__PURE__ */ i(
254
+ N,
243
255
  {
244
- title: C,
256
+ title: f,
245
257
  className: S,
246
258
  variant: "cta",
247
- children: /* @__PURE__ */ n("p", { children: "Loading subscription plans..." })
259
+ children: /* @__PURE__ */ i("p", { children: "Loading subscription plans..." })
248
260
  }
249
261
  );
250
- const u = R ?? (L ? L.message : null), c = i && (e != null && e.isActive) && e.packageId ? {
262
+ const ae = T ?? (D ? D.message : null), ne = n && (e != null && e.isActive) && e.packageId ? {
251
263
  isActive: !0,
252
264
  activeContent: {
253
265
  title: "Active Subscription",
@@ -267,49 +279,61 @@ function re({
267
279
  ] : []
268
280
  ]
269
281
  }
270
- } : void 0, I = s === "free";
271
- return /* @__PURE__ */ n(
272
- F,
282
+ } : void 0, _ = b === "free";
283
+ return /* @__PURE__ */ i(
284
+ N,
273
285
  {
274
- title: C,
286
+ title: f,
275
287
  className: S,
276
288
  variant: "cta",
277
- error: u,
278
- currentStatus: c,
279
- freeTileConfig: I ? _() : void 0,
280
- aboveProducts: x.length > 1 ? /* @__PURE__ */ n(
281
- K,
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,
282
294
  {
283
- options: x,
284
- value: s,
285
- onChange: B
295
+ options: h,
296
+ value: b,
297
+ onChange: x
286
298
  }
287
- ) : void 0,
288
- children: !I && U.map((a) => {
289
- var j;
290
- const f = i && (e == null ? void 0 : e.isActive) && e.packageId === a.packageId, d = t(a, s);
291
- return /* @__PURE__ */ n(
292
- J,
293
- {
294
- id: a.packageId,
295
- title: a.name,
296
- price: ((j = a.product) == null ? void 0 : j.priceString) ?? "$0",
297
- periodLabel: a.product ? `/${a.product.period}` : void 0,
298
- features: (o == null ? void 0 : o[a.packageId]) ?? [],
299
- isSelected: !1,
300
- onSelect: () => {
299
+ ) }) : void 0,
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
+ ]
301
328
  },
302
- isCurrentPlan: f,
303
- ctaButton: d,
304
- disabled: k
305
- },
306
- a.packageId
307
- );
308
- })
329
+ a.packageId
330
+ );
331
+ }) })
332
+ ] })
309
333
  }
310
334
  );
311
335
  }
312
336
  export {
313
- ee as SubscriptionByDurationPage,
314
- re as SubscriptionByOfferPage
337
+ he as SubscriptionByDurationPage,
338
+ ve as SubscriptionByOfferPage
315
339
  };
@@ -1,9 +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
- */
1
+ import { default as React } from 'react';
7
2
  export interface SubscriptionByOfferPageProps {
8
3
  /** Whether the user is logged in */
9
4
  isLoggedIn: boolean;
@@ -21,5 +16,18 @@ export interface SubscriptionByOfferPageProps {
21
16
  title?: string;
22
17
  /** Additional CSS classes */
23
18
  className?: string;
19
+ /**
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").
23
+ * Falls back to the fallback string if not provided.
24
+ */
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;
24
32
  }
25
- export declare function SubscriptionByOfferPage({ isLoggedIn, onNavigateToLogin, userId, userEmail, featuresByPackage, freeFeatures, title, className, }: 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.2",
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",
@@ -28,13 +28,13 @@
28
28
  "peerDependencies": {
29
29
  "react": "^18.0.0 || ^19.0.0",
30
30
  "react-dom": "^18.0.0 || ^19.0.0",
31
- "@sudobility/subscription_lib": "^0.0.25",
31
+ "@sudobility/subscription_lib": "^0.0.26",
32
32
  "@sudobility/subscription-components": "^1.0.27",
33
33
  "@sudobility/types": "^1.9.58"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@sudobility/subscription-components": "^1.0.27",
37
- "@sudobility/subscription_lib": "^0.0.25",
37
+ "@sudobility/subscription_lib": "^0.0.26",
38
38
  "@sudobility/types": "^1.9.58",
39
39
  "@testing-library/jest-dom": "^6.0.0",
40
40
  "@testing-library/react": "^16.0.0",