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: {
}>
-
+
+
-
-
-
-
-
-
-
-
@@ -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