fix(core): adjust member limit dialog (#9195)

close AF-1945 AF-1989 AF-1981 AF-1998 AF-1950 AF-1951

Adjust the member limit dialog. Now it will determine whether the member limit is reached when clicking Send Invite Email.
Modified the notification after sending the invitation.
This commit is contained in:
JimmFly
2024-12-19 02:23:35 +00:00
parent def4dc854a
commit 70deeaa804
5 changed files with 95 additions and 36 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 300 KiB

View File

@@ -81,18 +81,15 @@ export const CloudWorkspaceMembersPanel = ({
const plan = useLiveData(
subscriptionService.subscription.pro$.map(s => s?.plan)
);
const isLimited =
workspaceQuota && workspaceQuota.memberLimit
? workspaceQuota.memberCount >= workspaceQuota.memberLimit
: null;
const t = useI18n();
const [open, setOpen] = useState(false);
const [openInvite, setOpenInvite] = useState(false);
const [openMemberLimit, setOpenMemberLimit] = useState(false);
const [isMutating, setIsMutating] = useState(false);
const openModal = useCallback(() => {
setOpen(true);
const openInviteModal = useCallback(() => {
setOpenInvite(true);
}, []);
const onGenerateInviteLink = useCallback(
@@ -116,21 +113,51 @@ export const CloudWorkspaceMembersPanel = ({
emails,
}: Parameters<InviteTeamMemberModalProps['onConfirm']>[0]) => {
setIsMutating(true);
const success = await permissionService.permission.inviteMembers(
emails,
const uniqueEmails = deduplicateEmails(emails);
if (
!isTeam &&
workspaceQuota &&
uniqueEmails.length >
workspaceQuota.memberLimit - workspaceQuota.memberCount
) {
setOpenMemberLimit(true);
setIsMutating(false);
return;
}
const results = await permissionService.permission.inviteMembers(
uniqueEmails,
true
);
if (success) {
notify.success({
title: t['Invitation sent'](),
message: t['Invitation sent hint'](),
const unSuccessInvites = results.reduce<string[]>((acc, result) => {
if (!result.sentSuccess) {
acc.push(result.email);
}
return acc;
}, []);
if (results) {
notify({
title: t['com.affine.payment.member.team.invite.notify.title']({
successCount: (
uniqueEmails.length - unSuccessInvites.length
).toString(),
failedCount: unSuccessInvites.length.toString(),
}),
message: <NotifyMessage unSuccessInvites={unSuccessInvites} />,
});
setOpen(false);
setOpenInvite(false);
membersService.members.revalidate();
workspaceQuotaService.quota.revalidate();
}
setIsMutating(false);
},
[membersService.members, permissionService.permission, t]
[
isTeam,
membersService.members,
permissionService.permission,
t,
workspaceQuota,
workspaceQuotaService.quota,
]
);
const onImportCSV = useAsyncCallback(
@@ -209,29 +236,28 @@ export const CloudWorkspaceMembersPanel = ({
<SettingRow name={title} desc={desc} spreadCol={!!isOwner}>
{isOwner ? (
<>
<Button onClick={openModal}>{t['Invite Members']()}</Button>
{isLimited && !isTeam ? (
<Button onClick={openInviteModal}>{t['Invite Members']()}</Button>
{!isTeam ? (
<MemberLimitModal
isFreePlan={!plan}
open={open}
open={openMemberLimit}
plan={workspaceQuota.humanReadable.name ?? ''}
quota={workspaceQuota.humanReadable.memberLimit ?? ''}
setOpen={setOpen}
setOpen={setOpenMemberLimit}
onConfirm={handleUpgradeConfirm}
/>
) : (
<InviteTeamMemberModal
open={open}
setOpen={setOpen}
onConfirm={onInviteBatchConfirm}
isMutating={isMutating}
copyTextToClipboard={copyTextToClipboard}
onGenerateInviteLink={onGenerateInviteLink}
onRevokeInviteLink={onRevokeInviteLink}
importCSV={<ImportCSV onImport={onImportCSV} />}
invitationLink={inviteLink}
/>
)}
) : null}
<InviteTeamMemberModal
open={openInvite}
setOpen={setOpenInvite}
onConfirm={onInviteBatchConfirm}
isMutating={isMutating}
copyTextToClipboard={copyTextToClipboard}
onGenerateInviteLink={onGenerateInviteLink}
onRevokeInviteLink={onRevokeInviteLink}
importCSV={<ImportCSV onImport={onImportCSV} />}
invitationLink={inviteLink}
/>
</>
) : null}
</SettingRow>
@@ -243,6 +269,27 @@ export const CloudWorkspaceMembersPanel = ({
);
};
const NotifyMessage = ({
unSuccessInvites,
}: {
unSuccessInvites: string[];
}) => {
const t = useI18n();
if (unSuccessInvites.length === 0) {
return t['Invitation sent hint']();
}
return (
<div>
{t['com.affine.payment.member.team.invite.notify.fail-message']()}
{unSuccessInvites.map((email, index) => (
<div key={`${index}:${email}`}>{email}</div>
))}
</div>
);
};
export const MembersPanelFallback = () => {
const t = useI18n();
@@ -298,3 +345,16 @@ const ImportCSV = ({ onImport }: { onImport: (file: File) => void }) => {
</Upload>
);
};
function deduplicateEmails(emails: string[]): string[] {
const seenEmails = new Set<string>();
return emails.filter(email => {
const lowerCaseEmail = email.trim().toLowerCase();
if (seenEmails.has(lowerCaseEmail)) {
return false;
} else {
seenEmails.add(lowerCaseEmail);
return true;
}
});
}

View File

@@ -43,7 +43,6 @@ export const storageProgressContainer = style({
});
export const storageProgressWrapper = style({
flexGrow: 1,
marginRight: '20px',
});
globalStyle(`${storageProgressWrapper} .storage-progress-desc`, {
fontSize: cssVar('fontXs'),

View File

@@ -898,7 +898,7 @@
"com.affine.payment.cloud.team-workspace.benefit.g1-5": "Multiple admin roles.",
"com.affine.payment.cloud.team-workspace.benefit.g1-6": "Priority customer support.",
"com.affine.payment.cloud.team-workspace.description": "Best for scalable teams.",
"com.affine.payment.cloud.team-workspace.name": "Team Workspace",
"com.affine.payment.cloud.team-workspace.name": "Team",
"com.affine.payment.cloud.team-workspace.title.billed-yearly": "annually",
"com.affine.payment.cloud.team-workspace.title.price-monthly": "{{price}} per seat/month",
"com.affine.payment.contact-sales": "Contact sales",
@@ -953,8 +953,8 @@
"com.affine.payment.member.team.invite.generate": "Generate",
"com.affine.payment.member.team.invite.copy": "Copy",
"com.affine.payment.member.team.invite.done": "Done",
"com.affine.payment.member.team.invite.notify.title": "Invitation sent",
"com.affine.payment.member.team.invite.notify.message": "Invited members have been notified with email to join this Workspace.",
"com.affine.payment.member.team.invite.notify.title": "Invitation sent,{{successCount}} successful, {{failedCount}} failed",
"com.affine.payment.member.team.invite.notify.fail-message": "These email addresses have already been invited:",
"com.affine.payment.member.team.revoke": "Revoke invitation",
"com.affine.payment.member.team.approve": "Approve",
"com.affine.payment.member.team.decline": "Decline",