Improve 'Leave Team' process with logging and confirmation step

Added logging to the 'Leave Team' functionality to track user actions, and implemented a confirmation input to further validate a user's intent to leave a team. Also revised the user-facing prompt for more clarity on the team leaving process. Corresponding changes were applied to the relevant services and front-end components.
This commit is contained in:
giancarlo
2024-03-29 16:40:44 +08:00
parent 2b0fbc445b
commit 9e06d420bd
5 changed files with 120 additions and 23 deletions

View File

@@ -56,7 +56,7 @@ export function TeamAccountDangerZone({
const userIsPrimaryOwner = user?.id === primaryOwnerUserId;
if (userIsPrimaryOwner) {
return <DeleteTeamContainer account={account} />;
return <LeaveTeamContainer account={account} />;
}
return <LeaveTeamContainer account={account} />;
@@ -240,6 +240,20 @@ function LeaveTeamContainer(props: {
id: string;
};
}) {
const form = useForm({
resolver: zodResolver(
z.object({
confirmation: z.string().refine((value) => value === 'LEAVE', {
message: 'Confirmation required to leave team',
path: ['confirmation'],
}),
}),
),
defaultValues: {
confirmation: '',
},
});
return (
<div className={'flex flex-col space-y-4'}>
<p className={'text-muted-foreground text-sm'}>
@@ -276,18 +290,58 @@ function LeaveTeamContainer(props: {
</AlertDialogHeader>
<ErrorBoundary fallback={<LeaveTeamErrorAlert />}>
<form action={leaveTeamAccountAction}>
<input type={'hidden'} value={props.account.id} name={'id'} />
</form>
<Form {...form}>
<form
className={'flex flex-col space-y-4'}
action={leaveTeamAccountAction}
>
<input
type={'hidden'}
value={props.account.id}
name={'accountId'}
/>
<FormField
name={'confirmation'}
render={({ field }) => {
return (
<FormItem>
<FormLabel>
<Trans i18nKey={'teams:leaveTeamInputLabel'} />
</FormLabel>
<FormControl>
<Input
data-test="leave-team-input-field"
type="text"
className="w-full"
placeholder=""
pattern="LEAVE"
required
{...field}
/>
</FormControl>
<FormDescription>
<Trans i18nKey={'teams:leaveTeamInputDescription'} />
</FormDescription>
<FormMessage />
</FormItem>
);
}}
/>
<AlertDialogFooter>
<AlertDialogCancel>
<Trans i18nKey={'common:cancel'} />
</AlertDialogCancel>
<LeaveTeamSubmitButton />
</AlertDialogFooter>
</form>
</Form>
</ErrorBoundary>
<AlertDialogFooter>
<AlertDialogCancel>
<Trans i18nKey={'common:cancel'} />
</AlertDialogCancel>
<LeaveTeamSubmitButton />
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
@@ -310,15 +364,23 @@ function LeaveTeamSubmitButton() {
function LeaveTeamErrorAlert() {
return (
<Alert variant={'destructive'}>
<AlertTitle>
<Trans i18nKey={'teams:leaveTeamErrorHeading'} />
</AlertTitle>
<>
<Alert variant={'destructive'}>
<AlertTitle>
<Trans i18nKey={'teams:leaveTeamErrorHeading'} />
</AlertTitle>
<AlertDescription>
<Trans i18nKey={'common:genericError'} />
</AlertDescription>
</Alert>
<AlertDescription>
<Trans i18nKey={'common:genericError'} />
</AlertDescription>
</Alert>
<AlertDialogFooter>
<AlertDialogCancel>
<Trans i18nKey={'common:cancel'} />
</AlertDialogCancel>
</AlertDialogFooter>
</>
);
}

View File

@@ -2,4 +2,5 @@ import { z } from 'zod';
export const LeaveTeamAccountSchema = z.object({
accountId: z.string(),
confirmation: z.custom((value) => value === 'LEAVE'),
});

View File

@@ -3,6 +3,7 @@ import { SupabaseClient } from '@supabase/supabase-js';
import 'server-only';
import { z } from 'zod';
import { Logger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
const Schema = z.object({
@@ -11,9 +12,18 @@ const Schema = z.object({
});
export class LeaveTeamAccountService {
private readonly namespace = 'leave-team-account';
constructor(private readonly adminClient: SupabaseClient<Database>) {}
async leaveTeamAccount(params: z.infer<typeof Schema>) {
const ctx = {
...params,
name: this.namespace,
};
Logger.info(ctx, 'Leaving team account');
const { accountId, userId } = Schema.parse(params);
const { error } = await this.adminClient
@@ -25,7 +35,11 @@ export class LeaveTeamAccountService {
});
if (error) {
throw error;
Logger.error({ ...ctx, error }, 'Failed to leave team account');
throw new Error('Failed to leave team account');
}
Logger.info(ctx, 'Successfully left team account');
}
}