feat(web-admin): show copyable invite link after sending invite
After a platform admin sends an invite, the generated invite URL is displayed inline with a one-click copy button so it can be shared via any channel (email, WeChat, etc.). Link auto-dismisses when the invite form is reopened. Also adds i18n keys for invite link UI in en/zh. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e31baa1f40
commit
6e50f4cc50
|
|
@ -225,6 +225,8 @@ export default function TenantDetailPage() {
|
|||
const [showInviteForm, setShowInviteForm] = useState(false);
|
||||
const [inviteEmail, setInviteEmail] = useState('');
|
||||
const [inviteRole, setInviteRole] = useState('viewer');
|
||||
const [inviteLink, setInviteLink] = useState<string | null>(null);
|
||||
const [linkCopied, setLinkCopied] = useState(false);
|
||||
const [form, setForm] = useState<TenantFormData>({
|
||||
name: '',
|
||||
plan: 'free',
|
||||
|
|
@ -297,12 +299,15 @@ export default function TenantDetailPage() {
|
|||
|
||||
const sendInviteMutation = useMutation({
|
||||
mutationFn: (body: { email: string; role: string }) =>
|
||||
apiClient(`/api/v1/admin/tenants/${id}/invites`, { method: 'POST', body }),
|
||||
onSuccess: () => {
|
||||
apiClient<{ token: string }>(`/api/v1/admin/tenants/${id}/invites`, { method: 'POST', body }),
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.tenants.invites(id) });
|
||||
setShowInviteForm(false);
|
||||
setInviteEmail('');
|
||||
setInviteRole('viewer');
|
||||
const baseUrl = typeof window !== 'undefined' ? window.location.origin : '';
|
||||
setInviteLink(`${baseUrl}/invite/${data.token}`);
|
||||
setLinkCopied(false);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -686,13 +691,39 @@ export default function TenantDetailPage() {
|
|||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-lg font-semibold">{t('detail.invitations')}</h2>
|
||||
<button
|
||||
onClick={() => setShowInviteForm(!showInviteForm)}
|
||||
onClick={() => { setShowInviteForm(!showInviteForm); setInviteLink(null); }}
|
||||
className="px-3 py-1 text-xs rounded-md bg-primary text-primary-foreground hover:opacity-90"
|
||||
>
|
||||
{t('detail.inviteUser')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{inviteLink && (
|
||||
<div className="mb-4 p-4 border border-green-500/50 rounded-md bg-green-500/10 space-y-2">
|
||||
<p className="text-sm font-medium text-green-700 dark:text-green-400">
|
||||
{t('detail.inviteLinkReady')}
|
||||
</p>
|
||||
<div className="flex gap-2 items-center">
|
||||
<input
|
||||
readOnly
|
||||
value={inviteLink}
|
||||
className="flex-1 px-2 py-1 text-xs font-mono bg-background border rounded-md"
|
||||
/>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(inviteLink);
|
||||
setLinkCopied(true);
|
||||
setTimeout(() => setLinkCopied(false), 2000);
|
||||
}}
|
||||
className="px-3 py-1 text-xs rounded-md bg-primary text-primary-foreground hover:opacity-90 shrink-0"
|
||||
>
|
||||
{linkCopied ? t('detail.copied') : t('detail.copy')}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">{t('detail.inviteLinkHint')}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showInviteForm && (
|
||||
<div className="mb-4 p-4 border rounded-md bg-muted/30 space-y-3">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -66,6 +66,10 @@
|
|||
"sendInvite": "Send Invite",
|
||||
"sending": "Sending...",
|
||||
"revoke": "Revoke",
|
||||
"inviteLinkReady": "Invitation link generated — share it with the user:",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied!",
|
||||
"inviteLinkHint": "This link is valid for 7 days. The user can use it to create their account.",
|
||||
"suspendTenant": "Suspend Tenant",
|
||||
"activateTenant": "Activate Tenant",
|
||||
"viewAuditLog": "View Audit Log",
|
||||
|
|
|
|||
|
|
@ -66,6 +66,10 @@
|
|||
"sendInvite": "发送邀请",
|
||||
"sending": "正在发送...",
|
||||
"revoke": "撤销",
|
||||
"inviteLinkReady": "邀请链接已生成,将其发送给用户:",
|
||||
"copy": "复制",
|
||||
"copied": "已复制!",
|
||||
"inviteLinkHint": "此链接有效期 7 天,用户通过该链接创建账号后即可登录。",
|
||||
"suspendTenant": "停用租户",
|
||||
"activateTenant": "激活租户",
|
||||
"viewAuditLog": "查看审计日志",
|
||||
|
|
|
|||
Loading…
Reference in New Issue