From 78b6ae1ab0cea1473fae6647300f3d14438a68e8 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Sat, 13 Apr 2024 20:24:23 +0800 Subject: [PATCH] Add team member invitation success and error messages The code now includes success and error messages for team member invitation completion. It validates entered email addresses for duplicates and sets a limit on the number of invitations that can be sent at once to avoid server spam. Also, visual changes have been made to the form - label placement, form message, button types, etc. --- apps/web/public/locales/en/teams.json | 2 + .../invite-members-dialog-container.tsx | 76 +++++++++++++------ .../src/schema/invite-members.schema.ts | 35 ++++----- 3 files changed, 69 insertions(+), 44 deletions(-) diff --git a/apps/web/public/locales/en/teams.json b/apps/web/public/locales/en/teams.json index 22df75bb3..2b348bbf8 100644 --- a/apps/web/public/locales/en/teams.json +++ b/apps/web/public/locales/en/teams.json @@ -98,6 +98,8 @@ "membersPageHeading": "Members", "inviteMembersButton": "Invite Members", "invitingMembers": "Inviting members...", + "inviteMembersSuccessMessage": "Members invited successfully", + "inviteMembersErrorMessage": "Sorry, members could not be invited. Please try again.", "pendingInvitesHeading": "Pending Invites", "pendingInvitesDescription": " Here you can manage the pending invitations to your team.", "noPendingInvites": "No pending invites found", diff --git a/packages/features/team-accounts/src/components/members/invite-members-dialog-container.tsx b/packages/features/team-accounts/src/components/members/invite-members-dialog-container.tsx index 1134c3d26..7cceab52f 100644 --- a/packages/features/team-accounts/src/components/members/invite-members-dialog-container.tsx +++ b/packages/features/team-accounts/src/components/members/invite-members-dialog-container.tsx @@ -6,6 +6,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Plus, X } from 'lucide-react'; import { useFieldArray, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; import { Button } from '@kit/ui/button'; import { @@ -22,7 +23,9 @@ import { FormField, FormItem, FormLabel, + FormMessage, } from '@kit/ui/form'; +import { If } from '@kit/ui/if'; import { Input } from '@kit/ui/input'; import { Tooltip, @@ -41,6 +44,12 @@ type InviteModel = ReturnType; type Role = string; +/** + * The maximum number of invites that can be sent at once. + * Useful to avoid spamming the server with too large payloads + */ +const MAX_INVITES = 5; + export function InviteMembersDialogContainer({ accountSlug, accountId, @@ -53,6 +62,7 @@ export function InviteMembersDialogContainer({ }>) { const [pending, startTransition] = useTransition(); const [isOpen, setIsOpen] = useState(false); + const { t } = useTranslation('teams'); return ( @@ -79,11 +89,17 @@ export function InviteMembersDialogContainer({ roles={roles} onSubmit={(data) => { startTransition(async () => { - await createInvitationsAction({ + const promise = createInvitationsAction({ accountSlug, invitations: data.invitations, }); + toast.promise(() => promise, { + loading: t('invitingMembers'), + success: t('inviteMembersSuccessMessage'), + error: t('inviteMembersErrorMessage'), + }); + setIsOpen(false); }); }} @@ -129,6 +145,8 @@ function InviteMembersForm({ >
{fieldArray.fields.map((field, index) => { + const isFirst = index === 0; + const emailInputName = `invitations.${index}.email` as const; const roleInputName = `invitations.${index}.role` as const; @@ -141,7 +159,9 @@ function InviteMembersForm({ render={({ field }) => { return ( - {t('emailLabel')} + + {t('emailLabel')} + + + ); }} @@ -164,9 +186,11 @@ function InviteMembersForm({ render={({ field }) => { return ( - - - + + + + + + + ); }} />
-
+
-
+ + + + +
+