@spree/seller-dashboard 6.0.0 → 6.0.2
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 +11 -15
- package/src/locales/ar.json +0 -13
- package/src/locales/de.json +0 -13
- package/src/locales/en.json +0 -13
- package/src/locales/fr.json +0 -13
- package/src/locales/ja.json +851 -0
- package/src/locales/pl.json +0 -13
- package/src/locales/vi.json +851 -0
- package/src/locales/zh-CN.json +0 -13
- package/src/pages/accept-invitation.tsx +26 -105
- package/src/pages/onboarding.tsx +5 -10
package/src/locales/zh-CN.json
CHANGED
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"accept_invitation": {
|
|
3
|
-
"failed": "无法接受该邀请。邀请可能已过期,或密码有误。",
|
|
4
|
-
"first_name": "名字",
|
|
5
|
-
"invalid_link": "此链接缺少令牌。请从邀请邮件中打开链接。",
|
|
6
|
-
"last_name": "姓氏",
|
|
7
|
-
"not_found": "此邀请已失效。请让邀请你的人重新发送一份。",
|
|
8
|
-
"password": "密码",
|
|
9
|
-
"password_confirmation": "确认密码",
|
|
10
|
-
"submit": "接受邀请",
|
|
11
|
-
"submitting": "正在接受…",
|
|
12
|
-
"subtitle": "你以 {{email}} 的身份受邀。设置密码即可完成。",
|
|
13
|
-
"title": "加入团队"
|
|
14
|
-
},
|
|
15
2
|
"account": {
|
|
16
3
|
"avatar": "照片",
|
|
17
4
|
"avatar_help": "在面板中显示在您的姓名旁边。方形图片效果最佳。",
|
|
@@ -1,26 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Button, Field, FieldError, FieldGroup, FieldLabel, Input } from '@spree/dashboard-ui'
|
|
3
|
-
import { useQuery } from '@tanstack/react-query'
|
|
1
|
+
import { InvitationAcceptance } from '@spree/dashboard-core'
|
|
4
2
|
import { useNavigate } from '@tanstack/react-router'
|
|
5
|
-
import { useState } from 'react'
|
|
6
|
-
import { useForm } from 'react-hook-form'
|
|
7
|
-
import { useTranslation } from 'react-i18next'
|
|
8
3
|
import { sellerClient } from '../api-client'
|
|
9
|
-
import { CenteredMessage } from '../components/centered-message'
|
|
10
|
-
|
|
11
|
-
interface AcceptValues {
|
|
12
|
-
password: string
|
|
13
|
-
password_confirmation: string
|
|
14
|
-
first_name: string
|
|
15
|
-
last_name: string
|
|
16
|
-
}
|
|
17
4
|
|
|
18
5
|
/**
|
|
19
6
|
* Joining a seller's team from the emailed link.
|
|
20
7
|
*
|
|
21
|
-
* The
|
|
22
|
-
*
|
|
23
|
-
*
|
|
8
|
+
* The screens are the framework's, shared with the operator's dashboard; what
|
|
9
|
+
* is the panel's own is which API answers the lookup and where an accepted
|
|
10
|
+
* member lands. That is the seller they joined: the layout under that route
|
|
11
|
+
* loads the member's permissions for it.
|
|
24
12
|
*/
|
|
25
13
|
export function AcceptInvitationPage({
|
|
26
14
|
invitationId,
|
|
@@ -29,98 +17,31 @@ export function AcceptInvitationPage({
|
|
|
29
17
|
invitationId: string
|
|
30
18
|
token?: string
|
|
31
19
|
}) {
|
|
32
|
-
const { t } = useTranslation()
|
|
33
|
-
const { acceptInvitation } = useAuth()
|
|
34
20
|
const navigate = useNavigate()
|
|
35
|
-
const [error, setError] = useState<string | null>(null)
|
|
36
|
-
|
|
37
|
-
const {
|
|
38
|
-
data: invitation,
|
|
39
|
-
isLoading,
|
|
40
|
-
error: lookupError,
|
|
41
|
-
} = useQuery({
|
|
42
|
-
queryKey: ['seller', 'invitation', invitationId],
|
|
43
|
-
queryFn: () => sellerClient().auth.lookupInvitation(invitationId, token ?? ''),
|
|
44
|
-
enabled: Boolean(token),
|
|
45
|
-
retry: false,
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
const form = useForm<AcceptValues>({
|
|
49
|
-
defaultValues: { password: '', password_confirmation: '', first_name: '', last_name: '' },
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
if (!token) return <CenteredMessage>{t('accept_invitation.invalid_link')}</CenteredMessage>
|
|
53
|
-
if (isLoading) return <CenteredMessage>{t('common.loading')}</CenteredMessage>
|
|
54
|
-
if (lookupError || !invitation) {
|
|
55
|
-
return <CenteredMessage>{t('accept_invitation.not_found')}</CenteredMessage>
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function onSubmit(values: AcceptValues) {
|
|
59
|
-
setError(null)
|
|
60
|
-
try {
|
|
61
|
-
await acceptInvitation(invitationId, token ?? '', values)
|
|
62
|
-
// The index route decides where to go from there — it already knows how
|
|
63
|
-
// to skip the picker for someone who runs exactly one seller. Without
|
|
64
|
-
// this the form simply sits there after a successful accept, and the
|
|
65
|
-
// obvious next move is to submit again, against an invitation that has
|
|
66
|
-
// now been consumed.
|
|
67
|
-
navigate({ to: '/', replace: true })
|
|
68
|
-
} catch {
|
|
69
|
-
setError(t('accept_invitation.failed'))
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const { errors, isSubmitting } = form.formState
|
|
74
21
|
|
|
75
22
|
return (
|
|
76
23
|
<div className="flex min-h-screen items-center justify-center p-6">
|
|
77
|
-
<div className="w-full max-w-sm">
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
id="password"
|
|
99
|
-
type="password"
|
|
100
|
-
aria-invalid={!!errors.password || undefined}
|
|
101
|
-
{...form.register('password', { required: true })}
|
|
102
|
-
/>
|
|
103
|
-
<FieldError errors={[errors.password]} />
|
|
104
|
-
</Field>
|
|
105
|
-
|
|
106
|
-
<Field>
|
|
107
|
-
<FieldLabel htmlFor="password_confirmation">
|
|
108
|
-
{t('accept_invitation.password_confirmation')}
|
|
109
|
-
</FieldLabel>
|
|
110
|
-
<Input
|
|
111
|
-
id="password_confirmation"
|
|
112
|
-
type="password"
|
|
113
|
-
{...form.register('password_confirmation')}
|
|
114
|
-
/>
|
|
115
|
-
</Field>
|
|
116
|
-
|
|
117
|
-
{error && <p className="text-destructive text-sm">{error}</p>}
|
|
118
|
-
|
|
119
|
-
<Button type="submit" disabled={isSubmitting}>
|
|
120
|
-
{isSubmitting ? t('accept_invitation.submitting') : t('accept_invitation.submit')}
|
|
121
|
-
</Button>
|
|
122
|
-
</FieldGroup>
|
|
123
|
-
</form>
|
|
24
|
+
<div className="flex w-full max-w-sm flex-col gap-6">
|
|
25
|
+
<InvitationAcceptance
|
|
26
|
+
invitationId={invitationId}
|
|
27
|
+
token={token}
|
|
28
|
+
lookup={async (id, token) => {
|
|
29
|
+
const invitation = await sellerClient().auth.lookupInvitation(id, token)
|
|
30
|
+
// Carried by the lookup alone; the team listings leave it out.
|
|
31
|
+
return {
|
|
32
|
+
...invitation,
|
|
33
|
+
invitee_exists: invitation.invitee_exists ?? false,
|
|
34
|
+
target: invitation.seller,
|
|
35
|
+
}
|
|
36
|
+
}}
|
|
37
|
+
onAccepted={(invitation) =>
|
|
38
|
+
navigate({
|
|
39
|
+
to: '/$sellerId',
|
|
40
|
+
params: { sellerId: invitation.target.id },
|
|
41
|
+
replace: true,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
/>
|
|
124
45
|
</div>
|
|
125
46
|
</div>
|
|
126
47
|
)
|
package/src/pages/onboarding.tsx
CHANGED
|
@@ -24,15 +24,13 @@ import {
|
|
|
24
24
|
Progress,
|
|
25
25
|
ScrollArea,
|
|
26
26
|
StatusBadge as SharedStatusBadge,
|
|
27
|
+
StepIcon,
|
|
27
28
|
Textarea,
|
|
28
29
|
toastManager,
|
|
29
30
|
} from '@spree/dashboard-ui'
|
|
30
31
|
import {
|
|
31
|
-
CheckCircle2Icon,
|
|
32
32
|
ChevronRightIcon,
|
|
33
33
|
CircleAlertIcon,
|
|
34
|
-
CircleIcon,
|
|
35
|
-
ClockIcon,
|
|
36
34
|
ExternalLinkIcon,
|
|
37
35
|
XCircleIcon,
|
|
38
36
|
} from '@spree/dashboard-ui/icons'
|
|
@@ -517,17 +515,14 @@ function panelRoute(kind: string): string | undefined {
|
|
|
517
515
|
}
|
|
518
516
|
}
|
|
519
517
|
|
|
518
|
+
// The operator's setup checklist icons, plus a submission sent back.
|
|
520
519
|
function StatusIcon({ status }: { status: string }) {
|
|
521
|
-
if (status === 'complete')
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
if (status === 'pending') {
|
|
525
|
-
return <ClockIcon className="size-5 shrink-0 text-warning" />
|
|
526
|
-
}
|
|
520
|
+
if (status === 'complete') return <StepIcon status="done" className="size-5" />
|
|
521
|
+
if (status === 'pending') return <StepIcon status="in_progress" className="size-5" />
|
|
527
522
|
if (status === 'rejected') {
|
|
528
523
|
return <XCircleIcon className="size-5 shrink-0 text-destructive" />
|
|
529
524
|
}
|
|
530
|
-
return <
|
|
525
|
+
return <StepIcon status="todo" className="size-5" />
|
|
531
526
|
}
|
|
532
527
|
|
|
533
528
|
function StatusBadge({ status }: { status: string }) {
|