@shoppexio/storefront-react 0.3.0 → 0.4.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/CHANGELOG.md +23 -0
- package/dist/account.css +4 -8
- package/dist/account.js +1257 -778
- package/dist/account.js.map +1 -1
- package/dist/index.d.ts +92 -8
- package/dist/index.js +15 -16
- package/dist/index.js.map +1 -1
- package/eject/account/account-downloads.ts +89 -1
- package/eject/account/account-referrals.ts +70 -1
- package/eject/account/account-reseller.ts +6 -140
- package/eject/account/downloads.tsx +73 -4
- package/eject/account/hosted-page.ts +45 -0
- package/eject/account/messages/ar.json +55 -29
- package/eject/account/messages/de.json +55 -29
- package/eject/account/messages/en.json +55 -29
- package/eject/account/messages/es.json +55 -29
- package/eject/account/messages/fi.json +55 -29
- package/eject/account/messages/fr.json +55 -29
- package/eject/account/messages/hi.json +55 -29
- package/eject/account/messages/it.json +55 -29
- package/eject/account/messages/lt.json +55 -29
- package/eject/account/messages/nl.json +55 -29
- package/eject/account/messages/pl.json +55 -29
- package/eject/account/messages/pt.json +55 -29
- package/eject/account/messages/ru.json +55 -29
- package/eject/account/messages/tr.json +55 -29
- package/eject/account/messages/uk.json +55 -29
- package/eject/account/messages/zh.json +55 -29
- package/eject/account/orders.tsx +88 -0
- package/eject/account/overview.tsx +6 -0
- package/eject/account/referrals.tsx +47 -5
- package/eject/account/reseller.tsx +59 -306
- package/eject/account/rewards.tsx +5 -0
- package/eject/account/shared.tsx +39 -1
- package/eject/account/subscriptions.tsx +75 -16
- package/package.json +4 -4
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
*
|
|
10
10
|
* The payment-method portal is NOT one of them. That route answers with a
|
|
11
11
|
* signed redirect into the provider's own portal, and any script sharing this
|
|
12
|
-
* origin could fetch the link and forward it
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* origin could fetch the link and forward it. So "Update card" is a link to the
|
|
13
|
+
* hosted billing page on the checkout host (design Batch 7, 5b), which asks for
|
|
14
|
+
* its own email code before it mints the portal session; the producer's
|
|
15
|
+
* `can_manage_payment_method` decides whether it shows.
|
|
16
16
|
*
|
|
17
17
|
* List and detail are two views of one panel. The platform defines a detail
|
|
18
18
|
* path for orders and for nothing else, so a `/dashboard/subscriptions/<id>`
|
|
@@ -29,10 +29,11 @@ import {
|
|
|
29
29
|
subscriptionTiming,
|
|
30
30
|
subscriptionTimingKey,
|
|
31
31
|
} from './account-subscriptions';
|
|
32
|
-
import { callCustomerApi } from './customer-account';
|
|
32
|
+
import { callCustomerApi, safeExternalHref } from './customer-account';
|
|
33
|
+
import { hostedPageHref } from './hosted-page';
|
|
33
34
|
import type { SdkCustomerSubscription } from '@shoppexio/storefront-react';
|
|
34
35
|
import { t } from './i18n';
|
|
35
|
-
import {
|
|
36
|
+
import { CircleAlertIcon } from './icons';
|
|
36
37
|
import { useAccountDashboard } from './dashboard';
|
|
37
38
|
import {
|
|
38
39
|
AccountBackButton,
|
|
@@ -43,6 +44,7 @@ import {
|
|
|
43
44
|
AccountRowsSkeleton,
|
|
44
45
|
AccountSectionTitle,
|
|
45
46
|
BusySpinner,
|
|
47
|
+
HostedPageLink,
|
|
46
48
|
StatusChip,
|
|
47
49
|
formatAccountAmount,
|
|
48
50
|
formatAccountDate,
|
|
@@ -127,6 +129,7 @@ export function AccountSubscriptions() {
|
|
|
127
129
|
) : null}
|
|
128
130
|
<StatusChip status={subscription.status} />
|
|
129
131
|
</button>
|
|
132
|
+
<RenewalDue subscription={subscription} className="sxa:mt-2" />
|
|
130
133
|
</li>
|
|
131
134
|
))}
|
|
132
135
|
</ul>
|
|
@@ -203,6 +206,12 @@ function SubscriptionDetail({
|
|
|
203
206
|
{notice ? <AccountNotice message={notice} /> : null}
|
|
204
207
|
{actionError ? <div className="sxa:mt-3"><AccountError message={actionError} /></div> : null}
|
|
205
208
|
|
|
209
|
+
<RenewalDue subscription={subscription} className="sxa:mt-3" />
|
|
210
|
+
|
|
211
|
+
{subscription.status === 'PAST_DUE' && subscription.actions.can_manage_payment_method ? (
|
|
212
|
+
<PastDueCard subscription={subscription} />
|
|
213
|
+
) : null}
|
|
214
|
+
|
|
206
215
|
<AccountCard className="sxa:mt-5 sxa:p-6">
|
|
207
216
|
{subscription.plan ? (
|
|
208
217
|
<>
|
|
@@ -232,17 +241,13 @@ function SubscriptionDetail({
|
|
|
232
241
|
</dl>
|
|
233
242
|
</AccountCard>
|
|
234
243
|
|
|
235
|
-
{/*
|
|
236
|
-
Where a "Manage payment method" button would have gone. The flag is true
|
|
237
|
-
for every Stripe plan, cancelled ones included, and the route behind it
|
|
238
|
-
answers with a signed provider URL — so this lane renders the sentence
|
|
239
|
-
and no control at all.
|
|
240
|
-
*/}
|
|
241
244
|
{subscription.actions.can_manage_payment_method ? (
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
{
|
|
245
|
-
|
|
245
|
+
<HostedPageLink
|
|
246
|
+
page="billing"
|
|
247
|
+
hints={{ subscription: subscription.uniqid }}
|
|
248
|
+
label={t('subscriptions.updateCard')}
|
|
249
|
+
className="sxa:mt-4"
|
|
250
|
+
/>
|
|
246
251
|
) : null}
|
|
247
252
|
|
|
248
253
|
{/*
|
|
@@ -347,6 +352,60 @@ function SubscriptionDetail({
|
|
|
347
352
|
);
|
|
348
353
|
}
|
|
349
354
|
|
|
355
|
+
/**
|
|
356
|
+
* A renewal payment that is due, and the way to pay it.
|
|
357
|
+
*
|
|
358
|
+
* `renewal_checkout_url` is the hosted checkout of the renewal invoice the
|
|
359
|
+
* server already opened, so this is a plain link: following it creates
|
|
360
|
+
* nothing, and no call is made from here. No URL means nothing is due and
|
|
361
|
+
* nothing is shown; a URL that fails the scheme check gets no anchor, only the
|
|
362
|
+
* sentence, because the payment is still due.
|
|
363
|
+
*/
|
|
364
|
+
function RenewalDue({ subscription, className }: { subscription: SdkCustomerSubscription; className: string }) {
|
|
365
|
+
const url = subscription.renewal_checkout_url ?? null;
|
|
366
|
+
if (url === null) return null;
|
|
367
|
+
const href = safeExternalHref(url);
|
|
368
|
+
|
|
369
|
+
return (
|
|
370
|
+
<AccountCard className={`${className} sxa:flex sxa:flex-wrap sxa:items-center sxa:justify-between sxa:gap-3 sxa:px-5 sxa:py-3`}>
|
|
371
|
+
<p className="sxa:min-w-0 sxa:text-small">{t('subscriptions.renewalDue')}</p>
|
|
372
|
+
{href === null ? null : (
|
|
373
|
+
<a
|
|
374
|
+
href={href}
|
|
375
|
+
className="sx-account-cta sxa:cta-button sxa:inline-flex sxa:h-9 sxa:shrink-0 sxa:items-center sxa:px-4 sxa:text-small sxa:font-semibold"
|
|
376
|
+
>
|
|
377
|
+
{t('subscriptions.payNow')}
|
|
378
|
+
</a>
|
|
379
|
+
)}
|
|
380
|
+
</AccountCard>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* A past-due Stripe plan: the last charge failed, and the card is what to
|
|
386
|
+
* change before the plan ends. The same hosted billing link as "Update card".
|
|
387
|
+
*/
|
|
388
|
+
function PastDueCard({ subscription }: { subscription: SdkCustomerSubscription }) {
|
|
389
|
+
// No shop or checkout host to link to: the notice would point at nothing.
|
|
390
|
+
if (hostedPageHref('billing', { subscription: subscription.uniqid }) === null) return null;
|
|
391
|
+
return (
|
|
392
|
+
<AccountCard className="sx-account-past-due sxa:mt-3 sxa:flex sxa:items-start sxa:gap-3 sxa:px-5 sxa:py-4">
|
|
393
|
+
<CircleAlertIcon className="sxa:mt-0.5 sxa:h-4 sxa:w-4 sxa:shrink-0 sxa:text-error" />
|
|
394
|
+
<div className="sxa:min-w-0 sxa:flex-1">
|
|
395
|
+
<p className="sxa:text-small sxa:font-semibold">{t('subscriptions.pastDueCardTitle')}</p>
|
|
396
|
+
<p className="sxa:mt-1 sxa:text-small sxa:text-muted">{t('subscriptions.pastDueCardBody')}</p>
|
|
397
|
+
<HostedPageLink
|
|
398
|
+
page="billing"
|
|
399
|
+
hints={{ subscription: subscription.uniqid }}
|
|
400
|
+
label={t('subscriptions.updateCard')}
|
|
401
|
+
hint={null}
|
|
402
|
+
className="sxa:mt-3"
|
|
403
|
+
/>
|
|
404
|
+
</div>
|
|
405
|
+
</AccountCard>
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
350
409
|
function DetailRow({ label, children }: { label: string; children: ReactNode }) {
|
|
351
410
|
return (
|
|
352
411
|
<div className="sxa:flex sxa:items-center sxa:justify-between sxa:gap-4">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shoppexio/storefront-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "React hooks, models and a customer account for Shoppex code storefronts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://docs.shoppex.io/storefront/code-storefront-development",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"build": "tsup && bun run build:css && bun run build:eject",
|
|
38
38
|
"build:css": "tailwindcss --input src/account/account.css --output dist/account.css --optimize",
|
|
39
39
|
"lint": "biome lint src tests scripts",
|
|
40
|
-
"typecheck": "
|
|
40
|
+
"typecheck": "tsrs --noEmit",
|
|
41
41
|
"test": "bun run build:css && bun test ./tests",
|
|
42
42
|
"build:eject": "bun scripts/build-eject.ts"
|
|
43
43
|
},
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"@types/react": "19.2.14",
|
|
73
73
|
"@types/react-dom": "19.2.3",
|
|
74
74
|
"jsdom-cjs": "npm:jsdom@26.1.0",
|
|
75
|
-
"react": "19.
|
|
76
|
-
"react-dom": "19.
|
|
75
|
+
"react": "19.3.1",
|
|
76
|
+
"react-dom": "19.3.1",
|
|
77
77
|
"react-router-dom": "7.18.1",
|
|
78
78
|
"tailwindcss": "4.2.1",
|
|
79
79
|
"tsup": "^8.5.0",
|