diff --git a/packages/backend/apps/multitenancy/email_serializers.py b/packages/backend/apps/multitenancy/email_serializers.py new file mode 100644 index 000000000..0f0491d2f --- /dev/null +++ b/packages/backend/apps/multitenancy/email_serializers.py @@ -0,0 +1,6 @@ +from rest_framework import serializers + + +class TenantInvitationEmailSerializer(serializers.Serializer): + token = serializers.CharField() + tenant_membership_id = serializers.CharField() diff --git a/packages/backend/apps/multitenancy/notifications.py b/packages/backend/apps/multitenancy/notifications.py new file mode 100644 index 000000000..577bcb8ab --- /dev/null +++ b/packages/backend/apps/multitenancy/notifications.py @@ -0,0 +1,11 @@ +import logging + +from common import emails +from . import email_serializers + +logger = logging.getLogger(__name__) + + +class TenantInvitationEmail(emails.Email): + name = 'TENANT_INVITATION' + serializer_class = email_serializers.TenantInvitationEmailSerializer diff --git a/packages/backend/apps/multitenancy/services/membership.py b/packages/backend/apps/multitenancy/services/membership.py index 370f4fb2f..1e6aabc32 100644 --- a/packages/backend/apps/multitenancy/services/membership.py +++ b/packages/backend/apps/multitenancy/services/membership.py @@ -1,8 +1,10 @@ +from graphql_relay import to_global_id from django.contrib.auth import get_user_model from ..models import Tenant, TenantMembership from ..constants import TenantUserRole from ..tokens import tenant_invitation_token +from ..notifications import TenantInvitationEmail User = get_user_model() @@ -18,11 +20,12 @@ def create_tenant_membership( user=user, tenant=tenant, role=role, invitee_email_address=invitee_email_address, is_accepted=is_accepted ) if not is_accepted: - # TODO: Change printing token below to email notification - print( # noqa - tenant_invitation_token.make_token( - user_email=invitee_email_address if invitee_email_address else user.email, tenant_membership=membership - ) + token = tenant_invitation_token.make_token( + user_email=invitee_email_address if invitee_email_address else user.email, tenant_membership=membership ) + TenantInvitationEmail( + to=user.email if user else invitee_email_address, + data={'tenant_membership_id': to_global_id("TenantMembershipType", membership.id), 'token': token}, + ).send() return membership diff --git a/packages/webapp-libs/webapp-emails/src/templates/templates.config.ts b/packages/webapp-libs/webapp-emails/src/templates/templates.config.ts index b3c0b7c67..b99046091 100644 --- a/packages/webapp-libs/webapp-emails/src/templates/templates.config.ts +++ b/packages/webapp-libs/webapp-emails/src/templates/templates.config.ts @@ -6,6 +6,7 @@ import * as TrialExpiresSoon from './trialExpiresSoon'; import * as UserExport from './userExport'; import * as UserExportAdmin from './userExportAdmin'; +import * as TenantInvitation from './tenantInvitation'; //<-- INJECT EMAIL TEMPLATE IMPORT --> export const templates: Record = { @@ -15,5 +16,6 @@ export const templates: Record = { [EmailTemplateType.TRIAL_EXPIRES_SOON]: TrialExpiresSoon, [EmailTemplateType.USER_EXPORT]: UserExport, [EmailTemplateType.USER_EXPORT_ADMIN]: UserExportAdmin, + [EmailTemplateType.TENANT_INVITATION]: TenantInvitation, //<-- INJECT EMAIL TEMPLATE --> }; diff --git a/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/index.ts b/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/index.ts new file mode 100644 index 000000000..54cda6668 --- /dev/null +++ b/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/index.ts @@ -0,0 +1 @@ +export * from './tenantInvitation.component'; diff --git a/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/tenantInvitation.component.tsx b/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/tenantInvitation.component.tsx new file mode 100644 index 000000000..f19697138 --- /dev/null +++ b/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/tenantInvitation.component.tsx @@ -0,0 +1,36 @@ +import { useGenerateAbsoluteLocalePath } from '@sb/webapp-core/hooks'; +import { RoutesConfig } from '@sb/webapp-core/config/routes'; +import { FormattedMessage } from 'react-intl'; + +import { EmailComponentProps } from '../../types'; +import { Button, Layout } from '../../base'; + + +export type TenantInvitationProps = EmailComponentProps & { + token: string + tenantMembershipId: string +}; + +export const Template = ({token, tenantMembershipId}: TenantInvitationProps) => { + const generateLocalePath = useGenerateAbsoluteLocalePath(); + const url = generateLocalePath(RoutesConfig.home); + + return ( + } + text={ + + } + > + + + ); +}; + +export const Subject = () => diff --git a/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/tenantInvitation.stories.tsx b/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/tenantInvitation.stories.tsx new file mode 100644 index 000000000..d5ef1159b --- /dev/null +++ b/packages/webapp-libs/webapp-emails/src/templates/tenantInvitation/tenantInvitation.stories.tsx @@ -0,0 +1,23 @@ +import { StoryFn } from '@storybook/react'; + +import { EmailTemplateType } from '../../types'; +import { EmailStory } from '../../emailStory/emailStory.component'; +import { + Template as TenantInvitationEmail, + Subject as TenantInvitationSubject, + TenantInvitationProps, +} from './tenantInvitation.component'; + +const Template: StoryFn = (args) => ( + } emailData={args}> + + +); + +export default { + title: 'Emails/TenantInvitation', + component: TenantInvitationEmail, +}; + +export const Primary = Template.bind({}); +Primary.args = {}; diff --git a/packages/webapp-libs/webapp-emails/src/types.ts b/packages/webapp-libs/webapp-emails/src/types.ts index 3393ca921..c8ad5d6bf 100644 --- a/packages/webapp-libs/webapp-emails/src/types.ts +++ b/packages/webapp-libs/webapp-emails/src/types.ts @@ -7,6 +7,7 @@ export enum EmailTemplateType { TRIAL_EXPIRES_SOON = 'TRIAL_EXPIRES_SOON', USER_EXPORT = 'USER_EXPORT', USER_EXPORT_ADMIN = 'USER_EXPORT_ADMIN', + TENANT_INVITATION = 'TENANT_INVITATION', //<-- INJECT EMAIL TYPE --> }