From 917a1271bf72185d1f8e2f5bcb8121d991284891 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 27 Dec 2023 17:32:56 +1100 Subject: [PATCH] feat: wip --- .../app/(dashboard)/settings/teams/page.tsx | 55 +--------- .../settings/teams/team-email-usage.tsx | 103 ++++++++++++++++++ .../email/templates/confirm-team-email.tsx | 4 +- packages/lib/constants/teams.ts | 9 +- .../team/delete-team-invitations.ts | 2 +- .../server-only/team/delete-team-members.ts | 2 +- .../server-only/team/update-team-member.ts | 4 +- packages/ui/primitives/combobox.tsx | 103 ++++++++++-------- 8 files changed, 170 insertions(+), 112 deletions(-) create mode 100644 apps/web/src/app/(dashboard)/settings/teams/team-email-usage.tsx diff --git a/apps/web/src/app/(dashboard)/settings/teams/page.tsx b/apps/web/src/app/(dashboard)/settings/teams/page.tsx index 282a516fe..756f652b8 100644 --- a/apps/web/src/app/(dashboard)/settings/teams/page.tsx +++ b/apps/web/src/app/(dashboard)/settings/teams/page.tsx @@ -3,40 +3,17 @@ import { AnimatePresence, motion } from 'framer-motion'; import { trpc } from '@documenso/trpc/react'; -import { Button } from '@documenso/ui/primitives/button'; -import { useToast } from '@documenso/ui/primitives/use-toast'; import SettingsHeader from '~/components/(dashboard)/settings/layout/header'; import CreateTeamDialog from '~/components/(teams)/dialogs/create-team-dialog'; import UserTeamsPageDataTable from '~/components/(teams)/tables/user-teams-page-data-table'; +import TeamEmailUsage from './team-email-usage'; import { TeamInvitations } from './team-invitations'; export default function TeamsSettingsPage() { - const { toast } = useToast(); - const { data: teamEmail } = trpc.team.getTeamEmailByEmail.useQuery(); - const { mutateAsync: deleteTeamEmail, isLoading: isDeletingTeamEmail } = - trpc.team.deleteTeamEmail.useMutation({ - onSuccess: () => { - toast({ - title: 'Success', - description: 'You have successfully revoked access.', - duration: 5000, - }); - }, - onError: () => { - toast({ - title: 'Something went wrong', - variant: 'destructive', - duration: 10000, - description: - 'We encountered an unknown error while attempting to revoke access. Please try again or contact support.', - }); - }, - }); - return (
@@ -58,35 +35,7 @@ export default function TeamsSettingsPage() { opacity: 0, }} > -
-
-

Team email

- -

- Your email is currently being used by team{' '} - {teamEmail.team.name} ({teamEmail.team.url} - ). -

- -

- They have permission on your behalf to: -

- -
    -
  • Display your name and email in documents
  • -
  • View all documents sent to your account
  • -
-
- - {/* Todo: Teams - Add 'are you sure'. */} - -
+ )} diff --git a/apps/web/src/app/(dashboard)/settings/teams/team-email-usage.tsx b/apps/web/src/app/(dashboard)/settings/teams/team-email-usage.tsx new file mode 100644 index 000000000..072b06ba5 --- /dev/null +++ b/apps/web/src/app/(dashboard)/settings/teams/team-email-usage.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { useState } from 'react'; + +import type { TeamEmail } from '@documenso/prisma/client'; +import { trpc } from '@documenso/trpc/react'; +import { Button } from '@documenso/ui/primitives/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@documenso/ui/primitives/dialog'; +import { useToast } from '@documenso/ui/primitives/use-toast'; + +export type TeamEmailUsageProps = { + teamEmail: TeamEmail & { team: { name: string; url: string } }; +}; + +export default function TeamEmailUsage({ teamEmail }: TeamEmailUsageProps) { + const [open, setOpen] = useState(false); + + const { toast } = useToast(); + + const { mutateAsync: deleteTeamEmail, isLoading: isDeletingTeamEmail } = + trpc.team.deleteTeamEmail.useMutation({ + onSuccess: () => { + toast({ + title: 'Success', + description: 'You have successfully revoked access.', + duration: 5000, + }); + }, + onError: () => { + toast({ + title: 'Something went wrong', + variant: 'destructive', + duration: 10000, + description: + 'We encountered an unknown error while attempting to revoke access. Please try again or contact support.', + }); + }, + }); + + return ( +
+
+

Team email

+ +

+ Your email is currently being used by team{' '} + {teamEmail.team.name} ({teamEmail.team.url} + ). +

+ +

They have permission on your behalf to:

+ +
    +
  • Display your name and email in documents
  • +
  • View all documents sent to your account
  • +
+
+ + !isDeletingTeamEmail && setOpen(value)}> + + + + + + + Are you sure? + + + You are about to revoke access for team{' '} + {teamEmail.team.name} ({teamEmail.team.url}) to + use your email. + + + +
+ + + + + +
+
+
+
+ ); +} diff --git a/packages/email/templates/confirm-team-email.tsx b/packages/email/templates/confirm-team-email.tsx index 2030332b6..3493044fc 100644 --- a/packages/email/templates/confirm-team-email.tsx +++ b/packages/email/templates/confirm-team-email.tsx @@ -84,7 +84,9 @@ export const ConfirmTeamEmailTemplate = ({
    -
  • View all documents sent to this email address
  • +
  • + View all documents sent to and from this email address +
  • Allow document recipients to reply directly to this email address
  • diff --git a/packages/lib/constants/teams.ts b/packages/lib/constants/teams.ts index eb83f95e3..58455d20f 100644 --- a/packages/lib/constants/teams.ts +++ b/packages/lib/constants/teams.ts @@ -8,15 +8,12 @@ export const TEAM_MEMBER_ROLE_MAP: Record = export const TEAM_MEMBER_ROLE_PERMISSIONS_MAP = { /** - * Includes updating team name, url, logo, emails. - * - * Todo: Teams - Clean this up, merge etc. + * Includes permissions to: + * - Manage team members + * - Manage team settings, changing name, url, etc. */ MANAGE_TEAM: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER], - DELETE_INVITATIONS: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER], - DELETE_TEAM_MEMBERS: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER], DELETE_TEAM_TRANSFER_REQUEST: [TeamMemberRole.ADMIN], - UPDATE_TEAM_MEMBERS: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER], } satisfies Record; /** diff --git a/packages/lib/server-only/team/delete-team-invitations.ts b/packages/lib/server-only/team/delete-team-invitations.ts index 10551baa1..70c5d731f 100644 --- a/packages/lib/server-only/team/delete-team-invitations.ts +++ b/packages/lib/server-only/team/delete-team-invitations.ts @@ -30,7 +30,7 @@ export const deleteTeamMemberInvitations = async ({ userId, teamId, role: { - in: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['DELETE_INVITATIONS'], + in: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_TEAM'], }, }, }); diff --git a/packages/lib/server-only/team/delete-team-members.ts b/packages/lib/server-only/team/delete-team-members.ts index 6c8196c42..97a8717f0 100644 --- a/packages/lib/server-only/team/delete-team-members.ts +++ b/packages/lib/server-only/team/delete-team-members.ts @@ -36,7 +36,7 @@ export const deleteTeamMembers = async ({ some: { userId, role: { - in: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['DELETE_TEAM_MEMBERS'], + in: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_TEAM'], }, }, }, diff --git a/packages/lib/server-only/team/update-team-member.ts b/packages/lib/server-only/team/update-team-member.ts index 65e82ca26..c99d4a290 100644 --- a/packages/lib/server-only/team/update-team-member.ts +++ b/packages/lib/server-only/team/update-team-member.ts @@ -1,5 +1,5 @@ import { prisma } from '@documenso/prisma'; -import { TeamMemberRole } from '@documenso/prisma/client'; +import type { TeamMemberRole } from '@documenso/prisma/client'; import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/teams'; @@ -27,7 +27,7 @@ export const updateTeamMember = async ({ some: { userId, role: { - in: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['UPDATE_TEAM_MEMBERS'], + in: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_TEAM'], }, }, }, diff --git a/packages/ui/primitives/combobox.tsx b/packages/ui/primitives/combobox.tsx index bf3705763..c1b4466ba 100644 --- a/packages/ui/primitives/combobox.tsx +++ b/packages/ui/primitives/combobox.tsx @@ -84,57 +84,64 @@ export function Combobox({ return selectedOptions.map((option) => option.label).join(', '); }, [selectedOptions, emptySelectionPlaceholder, loading]); + const showClearButton = enableClearAllButton && selectedValues.length > 0; + return ( - - - )} - - +
    + + - + ) : ( + + {buttonLabel} + +
    + +
    +
    + )} + + + + + {/* This is placed outside the trigger since we can't have nested buttons. */} + {showClearButton && ( +
    + +
    + )} +