From 9e06d420bdf56c875870162ec86b1419d5e75969 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Fri, 29 Mar 2024 16:40:44 +0800 Subject: [PATCH] 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. --- apps/web/public/locales/en/teams.json | 6 +- .../settings/team-account-danger-zone.tsx | 102 ++++++++++++++---- .../src/schema/leave-team-account.schema.ts | 1 + .../services/leave-team-account.service.ts | 16 ++- supabase/migrations/20221215192558_schema.sql | 18 ++++ 5 files changed, 120 insertions(+), 23 deletions(-) diff --git a/apps/web/public/locales/en/teams.json b/apps/web/public/locales/en/teams.json index 5b845c475..30622aeee 100644 --- a/apps/web/public/locales/en/teams.json +++ b/apps/web/public/locales/en/teams.json @@ -49,7 +49,7 @@ "inviteMembersLoading": "Inviting members...", "removeInviteButtonLabel": "Remove invite", "addAnotherMemberButtonLabel": "Add another one", - "inviteMembersSubmitLabel": "Send Invites", + "inviteMembersButtonLabel": "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", @@ -153,5 +153,7 @@ "acceptInvitationDescription": "You have been invited to join the team {{accountName}}. If you wish to accept the invitation, please click the button below.", "joinTeam": "Join {{accountName}}", "joinTeamAccount": "Join Team", - "joiningTeam": "Joining team..." + "joiningTeam": "Joining team...", + "leaveTeamInputLabel": "Please type LEAVE to confirm leaving the team.", + "leaveTeamInputDescription": "By leaving the team, you will no longer have access to it." } diff --git a/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx b/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx index f1c33cf10..06070802b 100644 --- a/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx +++ b/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx @@ -56,7 +56,7 @@ export function TeamAccountDangerZone({ const userIsPrimaryOwner = user?.id === primaryOwnerUserId; if (userIsPrimaryOwner) { - return ; + return ; } return ; @@ -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 (

@@ -276,18 +290,58 @@ function LeaveTeamContainer(props: { }> -

- -
+
+ + + + { + return ( + + + + + + + + + + + + + + + + ); + }} + /> + + + + + + + + + + - - - - - - - -
@@ -310,15 +364,23 @@ function LeaveTeamSubmitButton() { function LeaveTeamErrorAlert() { return ( - - - - + <> + + + + - - - - + + + + + + + + + + + ); } diff --git a/packages/features/team-accounts/src/schema/leave-team-account.schema.ts b/packages/features/team-accounts/src/schema/leave-team-account.schema.ts index 99204bd63..1ba4c6ce5 100644 --- a/packages/features/team-accounts/src/schema/leave-team-account.schema.ts +++ b/packages/features/team-accounts/src/schema/leave-team-account.schema.ts @@ -2,4 +2,5 @@ import { z } from 'zod'; export const LeaveTeamAccountSchema = z.object({ accountId: z.string(), + confirmation: z.custom((value) => value === 'LEAVE'), }); diff --git a/packages/features/team-accounts/src/server/services/leave-team-account.service.ts b/packages/features/team-accounts/src/server/services/leave-team-account.service.ts index eaa34d3e5..059cdc1d8 100644 --- a/packages/features/team-accounts/src/server/services/leave-team-account.service.ts +++ b/packages/features/team-accounts/src/server/services/leave-team-account.service.ts @@ -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) {} async leaveTeamAccount(params: z.infer) { + 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'); } } diff --git a/supabase/migrations/20221215192558_schema.sql b/supabase/migrations/20221215192558_schema.sql index d06c0dc47..77e38710d 100644 --- a/supabase/migrations/20221215192558_schema.sql +++ b/supabase/migrations/20221215192558_schema.sql @@ -453,6 +453,22 @@ delete on table public.accounts_memberships to service_role; -- Enable RLS on the accounts_memberships table alter table public.accounts_memberships enable row level security; +-- Trigger to prevent a primary owner from being removed from an account +create +or replace function kit.prevent_account_owner_membership_delete () returns trigger as $$ +begin + if exists (select 1 from public.accounts where id = old.account_id and primary_owner_user_id = old.user_id) then + raise exception 'The primary account owner cannot be removed from the account membership list'; + end if; + + return old; +end; +$$ language plpgsql; + +create or replace trigger prevent_account_owner_membership_delete_check before delete + on public.accounts_memberships for each row +execute function kit.prevent_account_owner_membership_delete (); + create or replace function public.has_role_on_account ( account_id uuid, @@ -572,6 +588,8 @@ using ( ) ); + + /* * ------------------------------------------------------- * Section: Account Roles