Implement i18n translation formatting for team accounts
Integrated i18n translations in team account components, enhancing the application's multi-language support. This includes updating dialog container text, button labels, and placeholders for improved localization. Also added translations for table headers, button labels, and form fields across various components.
This commit is contained in:
@@ -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({
|
||||
<Input
|
||||
value={search}
|
||||
onInput={(e) => setSearch((e.target as HTMLInputElement).value)}
|
||||
placeholder={'Search Invitation'}
|
||||
placeholder={t(`searchInvitations`)}
|
||||
/>
|
||||
|
||||
<DataTable columns={columns} data={filteredInvitations} />
|
||||
@@ -64,51 +67,59 @@ export function AccountInvitationsTable({
|
||||
);
|
||||
}
|
||||
|
||||
function getColumns(permissions: {
|
||||
function useGetColumns(permissions: {
|
||||
canUpdateInvitation: boolean;
|
||||
canRemoveInvitation: boolean;
|
||||
}): ColumnDef<Invitations[0]>[] {
|
||||
return [
|
||||
{
|
||||
header: 'Email',
|
||||
size: 200,
|
||||
cell: ({ row }) => {
|
||||
const member = row.original;
|
||||
const email = member.email;
|
||||
const { t } = useTranslation('teams');
|
||||
|
||||
return (
|
||||
<span className={'flex items-center space-x-4 text-left'}>
|
||||
<span>
|
||||
<ProfileAvatar text={email} />
|
||||
return useMemo(
|
||||
() => [
|
||||
{
|
||||
header: t('emailLabel'),
|
||||
size: 200,
|
||||
cell: ({ row }) => {
|
||||
const member = row.original;
|
||||
const email = member.email;
|
||||
|
||||
return (
|
||||
<span className={'flex items-center space-x-4 text-left'}>
|
||||
<span>
|
||||
<ProfileAvatar text={email} />
|
||||
</span>
|
||||
|
||||
<span>{email}</span>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: t('roleLabel'),
|
||||
cell: ({ row }) => {
|
||||
const { role } = row.original;
|
||||
|
||||
<span>{email}</span>
|
||||
</span>
|
||||
);
|
||||
return <RoleBadge role={role} />;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Role',
|
||||
cell: ({ row }) => {
|
||||
const { role } = row.original;
|
||||
|
||||
return <RoleBadge role={role} />;
|
||||
{
|
||||
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 }) => (
|
||||
<ActionsDropdown
|
||||
permissions={permissions}
|
||||
invitation={row.original}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
id: 'actions',
|
||||
cell: ({ row }) => (
|
||||
<ActionsDropdown permissions={permissions} invitation={row.original} />
|
||||
),
|
||||
},
|
||||
];
|
||||
],
|
||||
[permissions, t],
|
||||
);
|
||||
}
|
||||
|
||||
function ActionsDropdown({
|
||||
@@ -133,13 +144,13 @@ function ActionsDropdown({
|
||||
<DropdownMenuContent>
|
||||
<If condition={permissions.canUpdateInvitation}>
|
||||
<DropdownMenuItem onClick={() => setIsUpdatingRole(true)}>
|
||||
Update Invitation
|
||||
<Trans i18nKey={'teams:updateInvitation'} />
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
|
||||
<If condition={permissions.canRemoveInvitation}>
|
||||
<DropdownMenuItem onClick={() => setIsDeletingInvite(true)}>
|
||||
Remove
|
||||
<Trans i18nKey={'teams:removeInvitation'} />
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@@ -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 (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans i18nKey="team:deleteInvitationDialogTitle" />
|
||||
</DialogTitle>
|
||||
<AlertDialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
<Trans i18nKey="team:deleteInvitation" />
|
||||
</AlertDialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Remove the invitation to join this account.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<AlertDialogDescription>
|
||||
<Trans i18nKey="team:deleteInvitationDialogDescription" />
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
<DeleteInvitationForm
|
||||
setIsOpen={setIsOpen}
|
||||
invitationId={invitationId}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -66,7 +68,7 @@ function DeleteInvitationForm({
|
||||
return (
|
||||
<form action={onInvitationRemoved}>
|
||||
<div className={'flex flex-col space-y-6'}>
|
||||
<p className={'text-sm'}>
|
||||
<p className={'text-muted-foreground text-sm'}>
|
||||
<Trans i18nKey={'common:modalConfirmationQuestion'} />
|
||||
</p>
|
||||
|
||||
@@ -74,13 +76,19 @@ function DeleteInvitationForm({
|
||||
<RemoveInvitationErrorAlert />
|
||||
</If>
|
||||
|
||||
<Button
|
||||
data-test={'confirm-delete-invitation'}
|
||||
variant={'destructive'}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Delete Invitation
|
||||
</Button>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
<Trans i18nKey={'common:cancel'} />
|
||||
</AlertDialogCancel>
|
||||
|
||||
<Button
|
||||
data-test={'confirm-delete-invitation'}
|
||||
variant={'destructive'}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
<Trans i18nKey={'teams:deleteInvitation'} />
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -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<boolean>();
|
||||
|
||||
@@ -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 (
|
||||
<FormItem>
|
||||
<FormLabel>New Role</FormLabel>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'teams:roleLabel'} />
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<MembershipRoleSelector
|
||||
currentUserRole={userRole}
|
||||
@@ -130,7 +135,9 @@ function UpdateInvitationForm({
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>Pick a role for this member.</FormDescription>
|
||||
<FormDescription>
|
||||
<Trans i18nKey={'teams:updateRoleDescription'} />
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -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({
|
||||
<Input
|
||||
value={search}
|
||||
onInput={(e) => setSearch((e.target as HTMLInputElement).value)}
|
||||
placeholder={'Search Member'}
|
||||
placeholder={t(`searchMembersPlaceholder`)}
|
||||
/>
|
||||
|
||||
<DataTable columns={columns} data={filteredMembers} />
|
||||
@@ -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<Members[0]>[] {
|
||||
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 (
|
||||
<span className={'flex items-center space-x-4 text-left'}>
|
||||
<span>
|
||||
<ProfileAvatar
|
||||
displayName={displayName}
|
||||
pictureUrl={member.picture_url}
|
||||
/>
|
||||
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 (
|
||||
<span className={'flex items-center space-x-4 text-left'}>
|
||||
<span>
|
||||
<ProfileAvatar
|
||||
displayName={displayName}
|
||||
pictureUrl={member.picture_url}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span>{displayName}</span>
|
||||
|
||||
<If condition={isSelf}>
|
||||
<span
|
||||
className={
|
||||
'bg-muted rounded-md px-2.5 py-1 text-xs font-medium'
|
||||
}
|
||||
>
|
||||
{t('youLabel')}
|
||||
</span>
|
||||
</If>
|
||||
</span>
|
||||
|
||||
<span>{displayName}</span>
|
||||
|
||||
<If condition={isSelf}>
|
||||
<span
|
||||
className={
|
||||
'bg-muted rounded-md px-2.5 py-1 text-xs font-medium'
|
||||
}
|
||||
>
|
||||
You
|
||||
</span>
|
||||
</If>
|
||||
</span>
|
||||
);
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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 (
|
||||
<span className={'flex items-center space-x-1'}>
|
||||
<RoleBadge role={role} />
|
||||
return (
|
||||
<span className={'flex items-center space-x-1'}>
|
||||
<RoleBadge role={role} />
|
||||
|
||||
<If condition={isPrimaryOwner}>
|
||||
<span
|
||||
className={
|
||||
'rounded-md bg-yellow-400 px-2.5 py-1 text-xs font-medium dark:text-black'
|
||||
}
|
||||
>
|
||||
Primary
|
||||
</span>
|
||||
</If>
|
||||
</span>
|
||||
);
|
||||
<If condition={isPrimaryOwner}>
|
||||
<span
|
||||
className={
|
||||
'rounded-md bg-yellow-400 px-2.5 py-1 text-xs font-medium dark:text-black'
|
||||
}
|
||||
>
|
||||
{t('primaryOwnerLabel')}
|
||||
</span>
|
||||
</If>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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 }) => (
|
||||
<ActionsDropdown
|
||||
permissions={permissions}
|
||||
member={row.original}
|
||||
currentUserId={currentUserId}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
{
|
||||
header: '',
|
||||
id: 'actions',
|
||||
cell: ({ row }) => (
|
||||
<ActionsDropdown
|
||||
permissions={permissions}
|
||||
member={row.original}
|
||||
currentUserId={currentUserId}
|
||||
/>
|
||||
),
|
||||
},
|
||||
],
|
||||
[permissions, currentUserId, t],
|
||||
);
|
||||
}
|
||||
|
||||
function ActionsDropdown({
|
||||
@@ -196,19 +200,19 @@ function ActionsDropdown({
|
||||
<DropdownMenuContent>
|
||||
<If condition={permissions.canUpdateRole}>
|
||||
<DropdownMenuItem onClick={() => setIsUpdatingRole(true)}>
|
||||
Update Role
|
||||
<Trans i18nKey={'teams:updateRole'} />
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
|
||||
<If condition={permissions.canTransferOwnership}>
|
||||
<DropdownMenuItem onClick={() => setIsTransferring(true)}>
|
||||
Transfer Ownership
|
||||
<Trans i18nKey={'teams:transferOwnership'} />
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
|
||||
<If condition={permissions.canRemoveFromAccount}>
|
||||
<DropdownMenuItem onClick={() => setIsRemoving(true)}>
|
||||
Remove from Account
|
||||
<Trans i18nKey={'teams:removeMember'} />
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@@ -56,10 +56,12 @@ export function InviteMembersDialogContainer({
|
||||
|
||||
<DialogContent onInteractOutside={(e) => e.preventDefault()}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Invite Members to Organization</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans i18nKey={'teams:inviteMembersHeading'} />
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Invite members to your team by entering their email and role.
|
||||
<Trans i18nKey={'teams:inviteMembersDescription'} />
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -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({
|
||||
<FormControl>
|
||||
<Input
|
||||
data-test={'invite-email-input'}
|
||||
placeholder="member@email.com"
|
||||
placeholder={t('emailPlaceholder')}
|
||||
type="email"
|
||||
required
|
||||
{...field}
|
||||
@@ -148,7 +150,9 @@ function InviteMembersForm({
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>Role</FormLabel>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'teams:roleLabel'} />
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<MembershipRoleSelector
|
||||
@@ -199,26 +203,30 @@ function InviteMembersForm({
|
||||
<Button
|
||||
data-test={'append-new-invite-button'}
|
||||
type={'button'}
|
||||
variant={'outline'}
|
||||
variant={'link'}
|
||||
size={'sm'}
|
||||
disabled={pending}
|
||||
onClick={() => {
|
||||
fieldArray.append(createEmptyInviteModel());
|
||||
}}
|
||||
>
|
||||
<span className={'flex items-center space-x-2'}>
|
||||
<Plus className={'h-4'} />
|
||||
<Plus className={'mr-1 h-3'} />
|
||||
|
||||
<span>
|
||||
<Trans i18nKey={'teams:addAnotherMemberButtonLabel'} />
|
||||
</span>
|
||||
<span>
|
||||
<Trans i18nKey={'teams:addAnotherMemberButtonLabel'} />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button disabled={pending}>
|
||||
{pending ? 'Inviting...' : 'Invite Members'}
|
||||
<Trans
|
||||
i18nKey={
|
||||
pending
|
||||
? 'teams:invitingMembers'
|
||||
: 'teams:inviteMembersButtonLabel'
|
||||
}
|
||||
/>
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -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 (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans i18nKey="team:removeMemberModalHeading" />
|
||||
</DialogTitle>
|
||||
<AlertDialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
<Trans i18nKey="teamS:removeMemberModalHeading" />
|
||||
</AlertDialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Remove this member from the team.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<AlertDialogDescription>
|
||||
<Trans i18nKey={'teams:removeMemberModalDescription'} />
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
<RemoveMemberForm
|
||||
setIsOpen={setIsOpen}
|
||||
accountId={accountId}
|
||||
userId={userId}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -70,7 +72,7 @@ function RemoveMemberForm({
|
||||
return (
|
||||
<form action={onMemberRemoved}>
|
||||
<div className={'flex flex-col space-y-6'}>
|
||||
<p className={'text-sm'}>
|
||||
<p className={'text-muted-foreground text-sm'}>
|
||||
<Trans i18nKey={'common:modalConfirmationQuestion'} />
|
||||
</p>
|
||||
|
||||
@@ -78,14 +80,20 @@ function RemoveMemberForm({
|
||||
<RemoveMemberErrorAlert />
|
||||
</If>
|
||||
|
||||
<Button
|
||||
data-test={'confirm-remove-member'}
|
||||
variant={'destructive'}
|
||||
disabled={isSubmitting}
|
||||
onClick={onMemberRemoved}
|
||||
>
|
||||
<Trans i18nKey={'teams:removeMemberSubmitLabel'} />
|
||||
</Button>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
<Trans i18nKey={'common:cancel'} />
|
||||
</AlertDialogCancel>
|
||||
|
||||
<Button
|
||||
data-test={'confirm-remove-member'}
|
||||
variant={'destructive'}
|
||||
disabled={isSubmitting}
|
||||
onClick={onMemberRemoved}
|
||||
>
|
||||
<Trans i18nKey={'teams:removeMemberSubmitLabel'} />
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -46,7 +46,7 @@ export const TransferOwnershipDialog: React.FC<{
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Transfer ownership of the team to another member.
|
||||
<Trans i18nKey="team:transferOwnershipDescription" />
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -127,7 +127,7 @@ function TransferOrganizationOwnershipForm({
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
Please type TRANSFER to confirm the transfer of ownership.
|
||||
<Trans i18nKey={'teams:transferOwnershipInputLabel'} />
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
@@ -135,8 +135,7 @@ function TransferOrganizationOwnershipForm({
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
Please make sure you understand the implications of this
|
||||
action.
|
||||
<Trans i18nKey={'teams:transferOwnershipInputDescription'} />
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
|
||||
@@ -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<boolean>();
|
||||
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 (
|
||||
<FormItem>
|
||||
<FormLabel>New Role</FormLabel>
|
||||
<FormLabel>{t('memberRole')}</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<MembershipRoleSelector
|
||||
currentUserRole={userRole}
|
||||
@@ -131,7 +134,7 @@ function UpdateMemberForm({
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>Pick a role for this member.</FormDescription>
|
||||
<FormDescription>{t('updateRoleDescription')}</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
Reference in New Issue
Block a user