diff --git a/apps/web/public/locales/en/teams.json b/apps/web/public/locales/en/teams.json index 67ba54d3c..095cb3224 100644 --- a/apps/web/public/locales/en/teams.json +++ b/apps/web/public/locales/en/teams.json @@ -6,7 +6,13 @@ "yourTeam": "Your Teams", "createTeam": "Create Team", "membersTabLabel": "Members", - "emailSettingsTab": "Email", + "memberName": "Name", + "youLabel": "You", + "emailLabel": "Email", + "roleLabel": "Role", + "primaryOwnerLabel": "Primary Owner", + "joinedAtLabel": "Joined at", + "invitedAtLabel": "Invited at", "membersTabSubheading": "Manage and Invite members", "inviteMembersPageSubheading": "Invite members to your Team", "createTeamModalHeading": "Create Team", @@ -20,7 +26,7 @@ "settingsPageLabel": "General", "createTeamDropdownLabel": "New team", "changeRole": "Change Role", - "removeMember": "Remove", + "removeMember": "Remove from Account", "inviteMembersSuccess": "Members invited successfully!", "inviteMembersError": "Sorry, we encountered an error! Please try again", "inviteMembersLoading": "Inviting members...", @@ -28,20 +34,29 @@ "addAnotherMemberButtonLabel": "Add another one", "inviteMembersSubmitLabel": "Send Invites", "removeMemberModalHeading": "You are removing this user", + "removeMemberModalDescription": "Remove this member from the team. They will no longer have access to the team.", "removeMemberSuccessMessage": "Member removed successfully", "removeMemberErrorMessage": "Sorry, we encountered an error. Please try again", "removeMemberErrorHeading": "Sorry, we couldn't remove the selected member.", "removeMemberLoadingMessage": "Removing member...", "removeMemberSubmitLabel": "Remove User from Team", "chooseDifferentRoleError": "Role is the same as the current one", + "updateRole": "Update Role", "updateRoleLoadingMessage": "Updating role...", "updateRoleSuccessMessage": "Role updated successfully", "updatingRoleErrorMessage": "Sorry, we encountered an error. Please try again.", "updateMemberRoleModalHeading": "Update Member's Role", + "updateMemberRoleModalDescription": "Change the role of the selected member. The role determines the permissions of the member.", + "roleMustBeDifferent": "Role must be different from the current one", "memberRoleInputLabel": "Member role", + "updateRoleDescription": "Pick a role for this member.", "updateRoleSubmitLabel": "Update Role", "transferOwnership": "Transfer Ownership", - "deleteInviteModalHeading": "Deleting Invite", + "transferOwnershipDescription": "Transfer ownership of the team to another member.", + "transferOwnershipInputLabel": "Please type TRANSFER to confirm the transfer of ownership.", + "transferOwnershipInputDescription": "By transferring ownership, you will no longer be the primary owner of the team.", + "deleteInvitation": "Delete Invitation", + "deleteInvitationDialogDescription": "You are about to delete the invitation. The user will no longer be able to join the team.", "deleteInviteSuccessMessage": "Invite deleted successfully", "deleteInviteErrorMessage": "Invite not deleted. Please try again.", "deleteInviteLoadingMessage": "Deleting invite. Please wait...", @@ -60,10 +75,12 @@ "teamLogoInputHeading": "Upload your team's Logo", "teamLogoInputSubheading": "Please choose a photo to upload as your team logo.", "updateTeamSubmitLabel": "Update Team", - "inviteMembersPageHeading": "Invite Members", - "goBackToMembersPage": "Go back to members", + "inviteMembersHeading": "Invite Members to your Team", + "inviteMembersDescription": "Invite members to your team by entering their email and role.", + "emailPlaceholder": "member@email.com", "membersPageHeading": "Members", "inviteMembersButtonLabel": "Invite Members", + "invitingMembers": "Inviting members...", "pendingInvitesHeading": "Pending Invites", "pendingInvitesSubheading": "Manage invites not yet accepted", "noPendingInvites": "No pending invites found", @@ -95,5 +112,8 @@ "transferTeamErrorHeading": "Sorry, we couldn't transfer ownership of your team.", "transferTeamErrorMessage": "We encountered an error transferring ownership of your team. Please try again.", "updateRoleErrorHeading": "Sorry, we couldn't update the role of the selected member.", - "updateRoleErrorMessage": "We encountered an error updating the role of the selected member. Please try again." + "updateRoleErrorMessage": "We encountered an error updating the role of the selected member. Please try again.", + "searchInvitations": "Search Invitations", + "updateInvitation": "Update Invitation", + "removeInvitation": "Remove Invitation" } diff --git a/packages/features/team-accounts/src/components/invitations/account-invitations-table.tsx b/packages/features/team-accounts/src/components/invitations/account-invitations-table.tsx index 11f2d5b4e..470b187c8 100644 --- a/packages/features/team-accounts/src/components/invitations/account-invitations-table.tsx +++ b/packages/features/team-accounts/src/components/invitations/account-invitations-table.tsx @@ -4,6 +4,7 @@ import { useMemo, useState } from 'react'; import { ColumnDef } from '@tanstack/react-table'; import { Ellipsis } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; import { Database } from '@kit/supabase/database'; import { Button } from '@kit/ui/button'; @@ -17,6 +18,7 @@ import { import { If } from '@kit/ui/if'; import { Input } from '@kit/ui/input'; import { ProfileAvatar } from '@kit/ui/profile-avatar'; +import { Trans } from '@kit/ui/trans'; import { RoleBadge } from '../members/role-badge'; import { DeleteInvitationDialog } from './delete-invitation-dialog'; @@ -38,8 +40,9 @@ export function AccountInvitationsTable({ invitations, permissions, }: AccountInvitationsTableProps) { + const { t } = useTranslation('teams'); const [search, setSearch] = useState(''); - const columns = useMemo(() => getColumns(permissions), [permissions]); + const columns = useGetColumns(permissions); const filteredInvitations = invitations.filter((member) => { const searchString = search.toLowerCase(); @@ -56,7 +59,7 @@ export function AccountInvitationsTable({ setSearch((e.target as HTMLInputElement).value)} - placeholder={'Search Invitation'} + placeholder={t(`searchInvitations`)} /> @@ -64,51 +67,59 @@ export function AccountInvitationsTable({ ); } -function getColumns(permissions: { +function useGetColumns(permissions: { canUpdateInvitation: boolean; canRemoveInvitation: boolean; }): ColumnDef[] { - return [ - { - header: 'Email', - size: 200, - cell: ({ row }) => { - const member = row.original; - const email = member.email; + const { t } = useTranslation('teams'); - return ( - - - + return useMemo( + () => [ + { + header: t('emailLabel'), + size: 200, + cell: ({ row }) => { + const member = row.original; + const email = member.email; + + return ( + + + + + + {email} + ); + }, + }, + { + header: t('roleLabel'), + cell: ({ row }) => { + const { role } = row.original; - {email} - - ); + return ; + }, }, - }, - { - header: 'Role', - cell: ({ row }) => { - const { role } = row.original; - - return ; + { + header: t('invitedAtLabel'), + cell: ({ row }) => { + return new Date(row.original.created_at).toLocaleDateString(); + }, }, - }, - { - header: 'Invited At', - cell: ({ row }) => { - return new Date(row.original.created_at).toLocaleDateString(); + { + header: '', + id: 'actions', + cell: ({ row }) => ( + + ), }, - }, - { - header: '', - id: 'actions', - cell: ({ row }) => ( - - ), - }, - ]; + ], + [permissions, t], + ); } function ActionsDropdown({ @@ -133,13 +144,13 @@ function ActionsDropdown({ setIsUpdatingRole(true)}> - Update Invitation + setIsDeletingInvite(true)}> - Remove + diff --git a/packages/features/team-accounts/src/components/invitations/delete-invitation-dialog.tsx b/packages/features/team-accounts/src/components/invitations/delete-invitation-dialog.tsx index a3ce69aed..919780792 100644 --- a/packages/features/team-accounts/src/components/invitations/delete-invitation-dialog.tsx +++ b/packages/features/team-accounts/src/components/invitations/delete-invitation-dialog.tsx @@ -1,14 +1,16 @@ import { useState, useTransition } from 'react'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; -import { Button } from '@kit/ui/button'; import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from '@kit/ui/dialog'; + AlertDialog, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@kit/ui/alert-dialog'; +import { Button } from '@kit/ui/button'; import { If } from '@kit/ui/if'; import { Trans } from '@kit/ui/trans'; @@ -20,24 +22,24 @@ export const DeleteInvitationDialog: React.FC<{ invitationId: number; }> = ({ isOpen, setIsOpen, invitationId }) => { return ( - - - - - - + + + + + + - - Remove the invitation to join this account. - - + + + + - - + + ); }; @@ -66,7 +68,7 @@ function DeleteInvitationForm({ return ( - + @@ -74,13 +76,19 @@ function DeleteInvitationForm({ - - Delete Invitation - + + + + + + + + + ); diff --git a/packages/features/team-accounts/src/components/invitations/update-invitation-dialog.tsx b/packages/features/team-accounts/src/components/invitations/update-invitation-dialog.tsx index cbebc3db3..5c97d6439 100644 --- a/packages/features/team-accounts/src/components/invitations/update-invitation-dialog.tsx +++ b/packages/features/team-accounts/src/components/invitations/update-invitation-dialog.tsx @@ -2,6 +2,7 @@ import { useState, useTransition } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import { Database } from '@kit/supabase/database'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; @@ -69,6 +70,7 @@ function UpdateInvitationForm({ userRole: Role; setIsOpen: (isOpen: boolean) => void; }>) { + const { t } = useTranslation('teams'); const [pending, startTransition] = useTransition(); const [error, setError] = useState(); @@ -94,7 +96,7 @@ function UpdateInvitationForm({ return data.role !== userRole; }, { - message: 'Role must be different from the current role.', + message: t('roleMustBeDifferent'), path: ['role'], }, ), @@ -121,7 +123,10 @@ function UpdateInvitationForm({ render={({ field }) => { return ( - New Role + + + + - Pick a role for this member. + + + diff --git a/packages/features/team-accounts/src/components/members/account-members-table.tsx b/packages/features/team-accounts/src/components/members/account-members-table.tsx index 67d4314d2..e2c1cbd60 100644 --- a/packages/features/team-accounts/src/components/members/account-members-table.tsx +++ b/packages/features/team-accounts/src/components/members/account-members-table.tsx @@ -4,6 +4,7 @@ import { useMemo, useState } from 'react'; import { ColumnDef } from '@tanstack/react-table'; import { Ellipsis } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; import { Database } from '@kit/supabase/database'; import { Button } from '@kit/ui/button'; @@ -17,6 +18,7 @@ import { import { If } from '@kit/ui/if'; import { Input } from '@kit/ui/input'; import { ProfileAvatar } from '@kit/ui/profile-avatar'; +import { Trans } from '@kit/ui/trans'; import { RemoveMemberDialog } from './remove-member-dialog'; import { RoleBadge } from './role-badge'; @@ -44,11 +46,8 @@ export function AccountMembersTable({ currentUserId, }: AccountMembersTableProps) { const [search, setSearch] = useState(''); - - const columns = useMemo( - () => getColumns(permissions, currentUserId), - [currentUserId, permissions], - ); + const { t } = useTranslation('teams'); + const columns = useGetColumns(permissions, currentUserId); const filteredMembers = members.filter((member) => { const searchString = search.toLowerCase(); @@ -65,7 +64,7 @@ export function AccountMembersTable({ setSearch((e.target as HTMLInputElement).value)} - placeholder={'Search Member'} + placeholder={t(`searchMembersPlaceholder`)} /> @@ -73,7 +72,7 @@ export function AccountMembersTable({ ); } -function getColumns( +function useGetColumns( permissions: { canUpdateRole: boolean; canTransferOwnership: boolean; @@ -81,87 +80,92 @@ function getColumns( }, currentUserId: string, ): ColumnDef[] { - return [ - { - header: 'Name', - size: 200, - cell: ({ row }) => { - const member = row.original; - const displayName = member.name ?? member.email.split('@')[0]; - const isSelf = member.user_id === currentUserId; + const { t } = useTranslation('teams'); - return ( - - - + return useMemo( + () => [ + { + header: t('memberName'), + size: 200, + cell: ({ row }) => { + const member = row.original; + const displayName = member.name ?? member.email.split('@')[0]; + const isSelf = member.user_id === currentUserId; + + return ( + + + + + + {displayName} + + + + {t('youLabel')} + + - - {displayName} - - - - You - - - - ); + ); + }, }, - }, - { - header: 'Email', - accessorKey: 'email', - cell: ({ row }) => { - return row.original.email ?? '-'; + { + header: t('emailLabel'), + accessorKey: 'email', + cell: ({ row }) => { + return row.original.email ?? '-'; + }, }, - }, - { - header: 'Role', - cell: ({ row }) => { - const { role, primary_owner_user_id, user_id } = row.original; - const isPrimaryOwner = primary_owner_user_id === user_id; + { + header: t('roleLabel'), + cell: ({ row }) => { + const { role, primary_owner_user_id, user_id } = row.original; + const isPrimaryOwner = primary_owner_user_id === user_id; - return ( - - + return ( + + - - - Primary - - - - ); + + + {t('primaryOwnerLabel')} + + + + ); + }, }, - }, - { - header: 'Joined At', - cell: ({ row }) => { - return new Date(row.original.created_at).toLocaleDateString(); + { + header: t('joinedAtLabel'), + cell: ({ row }) => { + return new Date(row.original.created_at).toLocaleDateString(); + }, }, - }, - { - header: '', - id: 'actions', - cell: ({ row }) => ( - - ), - }, - ]; + { + header: '', + id: 'actions', + cell: ({ row }) => ( + + ), + }, + ], + [permissions, currentUserId, t], + ); } function ActionsDropdown({ @@ -196,19 +200,19 @@ function ActionsDropdown({ setIsUpdatingRole(true)}> - Update Role + setIsTransferring(true)}> - Transfer Ownership + setIsRemoving(true)}> - Remove from Account + 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 f3cb55035..09682a641 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 @@ -56,10 +56,12 @@ export function InviteMembersDialogContainer({ e.preventDefault()}> - Invite Members to Organization + + + - Invite members to your team by entering their email and role. + @@ -88,7 +90,7 @@ function InviteMembersForm({ onSubmit: (data: { invitations: InviteModel[] }) => void; pending: boolean; }) { - const { t } = useTranslation('team'); + const { t } = useTranslation('teams'); const form = useForm({ resolver: zodResolver(InviteMembersSchema), @@ -130,7 +132,7 @@ function InviteMembersForm({ { return ( - Role + + + { fieldArray.append(createEmptyInviteModel()); }} > - - + - - - + + - {pending ? 'Inviting...' : 'Invite Members'} + diff --git a/packages/features/team-accounts/src/components/members/remove-member-dialog.tsx b/packages/features/team-accounts/src/components/members/remove-member-dialog.tsx index 1e327d77e..b5fd51ada 100644 --- a/packages/features/team-accounts/src/components/members/remove-member-dialog.tsx +++ b/packages/features/team-accounts/src/components/members/remove-member-dialog.tsx @@ -1,14 +1,16 @@ import { useState, useTransition } from 'react'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; -import { Button } from '@kit/ui/button'; import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from '@kit/ui/dialog'; + AlertDialog, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@kit/ui/alert-dialog'; +import { Button } from '@kit/ui/button'; import { If } from '@kit/ui/if'; import { Trans } from '@kit/ui/trans'; @@ -21,25 +23,25 @@ export const RemoveMemberDialog: React.FC<{ userId: string; }> = ({ isOpen, setIsOpen, accountId, userId }) => { return ( - - - - - - + + + + + + - - Remove this member from the team. - - + + + + - - + + ); }; @@ -70,7 +72,7 @@ function RemoveMemberForm({ return ( - + @@ -78,14 +80,20 @@ function RemoveMemberForm({ - - - + + + + + + + + + ); diff --git a/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx b/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx index 81d4ae2ad..da5ae08ea 100644 --- a/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx +++ b/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx @@ -46,7 +46,7 @@ export const TransferOwnershipDialog: React.FC<{ - Transfer ownership of the team to another member. + @@ -127,7 +127,7 @@ function TransferOrganizationOwnershipForm({ return ( - Please type TRANSFER to confirm the transfer of ownership. + @@ -135,8 +135,7 @@ function TransferOrganizationOwnershipForm({ - Please make sure you understand the implications of this - action. + diff --git a/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx b/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx index aa417442b..f29ff4bd7 100644 --- a/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx +++ b/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx @@ -2,6 +2,7 @@ import { useState, useTransition } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import { Database } from '@kit/supabase/database'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; @@ -75,6 +76,7 @@ function UpdateMemberForm({ }>) { const [pending, startTransition] = useTransition(); const [error, setError] = useState(); + const { t } = useTranslation('teams'); const onSubmit = ({ role }: { role: Role }) => { startTransition(async () => { @@ -95,7 +97,7 @@ function UpdateMemberForm({ return data.role !== userRole; }, { - message: 'Role must be different from the current role.', + message: t(`roleMustBeDifferent`), path: ['role'], }, ), @@ -122,7 +124,8 @@ function UpdateMemberForm({ render={({ field }) => { return ( - New Role + {t('memberRole')} + - Pick a role for this member. + {t('updateRoleDescription')}
+