From a1e22c2419d87e7fc9a9ea0e0bb9717715eccf52 Mon Sep 17 00:00:00 2001 From: Ravi Date: Thu, 23 Sep 2021 18:18:22 +0530 Subject: [PATCH 1/6] Added functionality to send Email to selected Group of People i. Server 1. resolver.ts Modified the sendEventInvite Resolver to take an emailGroup as optional argument. ii. Client 1. Added New argument in method definitions in graphql.tsx 2. Created a new Modal component to shoqw the checkboxes for seleting the group 3. Added a button in Actions components to show the Modal componenet --- client/src/generated/graphql.tsx | 19 +-- .../dashboard/Events/components/Actions.tsx | 10 ++ .../Events/components/SendEmailModal.tsx | 109 ++++++++++++++++++ server/src/controllers/Events/resolver.ts | 44 ++++++- 4 files changed, 169 insertions(+), 13 deletions(-) create mode 100644 client/src/modules/dashboard/Events/components/SendEmailModal.tsx diff --git a/client/src/generated/graphql.tsx b/client/src/generated/graphql.tsx index ae35de1c7f..6b6d1890ef 100644 --- a/client/src/generated/graphql.tsx +++ b/client/src/generated/graphql.tsx @@ -4,12 +4,14 @@ export type Maybe = T | null; export type Exact = { [K in keyof T]: T[K]; }; -export type MakeOptional = Omit & { - [SubKey in K]?: Maybe; -}; -export type MakeMaybe = Omit & { - [SubKey in K]: Maybe; -}; +export type MakeOptional = Omit & + { + [SubKey in K]?: Maybe; + }; +export type MakeMaybe = Omit & + { + [SubKey in K]: Maybe; + }; const defaultOptions = {}; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { @@ -729,6 +731,7 @@ export type DeleteRsvpMutation = { export type SendEventInviteMutationVariables = Exact<{ id: Scalars['Int']; + emailGroups?: Maybe>; }>; export type SendEventInviteMutation = { @@ -1892,8 +1895,8 @@ export type DeleteRsvpMutationOptions = Apollo.BaseMutationOptions< DeleteRsvpMutationVariables >; export const SendEventInviteDocument = gql` - mutation sendEventInvite($id: Int!) { - sendEventInvite(id: $id) + mutation sendEventInvite($id: Int!, $emailGroups: [String!]) { + sendEventInvite(id: $id, emailGroups: $emailGroups) } `; export type SendEventInviteMutationFn = Apollo.MutationFunction< diff --git a/client/src/modules/dashboard/Events/components/Actions.tsx b/client/src/modules/dashboard/Events/components/Actions.tsx index 1cdbea0972..bb435e4f64 100644 --- a/client/src/modules/dashboard/Events/components/Actions.tsx +++ b/client/src/modules/dashboard/Events/components/Actions.tsx @@ -2,6 +2,7 @@ import { Button, HStack } from '@chakra-ui/react'; import { useConfirm, useConfirmDelete } from 'chakra-confirm'; import { LinkButton } from 'chakra-next-link'; import React, { useMemo } from 'react'; +import { useDisclosure } from '@chakra-ui/hooks'; import { EVENT, EVENTS } from '../graphql/queries'; import { @@ -9,6 +10,7 @@ import { useCancelEventMutation, useDeleteEventMutation, } from 'generated/graphql'; +import SendEmailModal from './SendEmailModal'; interface ActionsProps { event: Pick; @@ -17,6 +19,7 @@ interface ActionsProps { } const Actions: React.FC = ({ event, onDelete, hideCancel }) => { + const { isOpen, onOpen, onClose } = useDisclosure(); const [cancel] = useCancelEventMutation(); const [remove] = useDeleteEventMutation(); @@ -53,6 +56,9 @@ const Actions: React.FC = ({ event, onDelete, hideCancel }) => { } }; + const clickEmailAttendees = () => { + onOpen(); + }; return ( )} + + ); }; diff --git a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx new file mode 100644 index 0000000000..7178fa00df --- /dev/null +++ b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx @@ -0,0 +1,109 @@ +import { Button } from '@chakra-ui/button'; +import { Checkbox } from '@chakra-ui/checkbox'; +import { Stack } from '@chakra-ui/layout'; +import { + Modal, + ModalBody, + ModalContent, + ModalHeader, + ModalCloseButton, + ModalFooter, + ModalOverlay, +} from '@chakra-ui/modal'; +import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'; +import { useSendEventInviteMutation } from 'generated/graphql'; +import { useForm } from 'react-hook-form'; + +interface SendEmailModalProps { + onClose: () => any; + isOpen: boolean; + eventId: number; +} +const SendEmailModal: React.FC = ({ + onClose, + isOpen, + eventId, +}) => { + const { + register, + getValues, + handleSubmit, + formState: { errors }, + } = useForm(); + + const checkAtLeastOne = () => { + return getValues('confirmed') || + getValues('on_waitlist') || + getValues('interested') + ? true + : 'Please tell me if this is too hard.'; + }; + + const [publish] = useSendEventInviteMutation(); + const onSubmit = (data: any) => { + console.log(data); + const emailGroups = []; + for (let key of Object.keys(data)) { + if (data[key]) { + emailGroups.push(key); + } + } + console.log(emailGroups); + + publish({ variables: { id: eventId, emailGroups: emailGroups } }); + onClose(); + }; + return ( + + + + Send Email to Attendees + + +
+ + + + Confirmed + + + Waitlist + + + Interested + + +
+ {Object.keys(errors).length == 3 && ( + + + + Please select atleast one checkbox + + + )} +
+ + + + + +
+
+ ); +}; + +export default SendEmailModal; diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 19dc7f2025..f9ee8da4ac 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -273,17 +273,51 @@ ${venue.postal_code}
} @Mutation(() => Boolean) - async sendEventInvite(@Arg('id', () => Int) id: number) { + async sendEventInvite( + @Arg('id', () => Int) id: number, + @Arg('emailGroups', () => [String], { + nullable: true, + defaultValue: ['interested'], + }) + emailGroups: Array, + ) { const event = await Event.findOne(id, { - relations: ['venue', 'chapter', 'chapter.users', 'chapter.users.user'], + relations: [ + 'venue', + 'chapter', + 'chapter.users', + 'chapter.users.user', + 'rsvps', + 'rsvps.user', + ], }); if (!event) throw new Error("Can't find event"); // TODO: the default should probably be to bcc everyone. - const addresses = event.chapter.users - .filter((role) => role.interested) - .map(({ user }) => user.email); + const addresses: string[] = []; + if (emailGroups.includes('interested')) { + const interestedUsers: string[] = event.chapter.users + .filter((role) => role.interested) + .map(({ user }) => user.email); + + addresses.push(...interestedUsers); + } + if (emailGroups.includes('on_waitlist')) { + const waitlistUsers: string[] = event.rsvps + .filter((rsvp) => rsvp.on_waitlist) + .map(({ user }) => user.email); + addresses.push(...waitlistUsers); + } + if (emailGroups.includes('confirmed')) { + const confirmedUsers: string[] = event.rsvps + .filter((rsvp) => !rsvp.on_waitlist) + .map(({ user }) => user.email); + addresses.push(...confirmedUsers); + } + + console.log(addresses); + const subject = `Invitation to ${event.name}.`; const chapterURL = `${process.env.CLIENT_LOCATION}/chapters/${event.chapter.id}`; From 3af1f06bd9028ec0bb0402c23df058477daa9308 Mon Sep 17 00:00:00 2001 From: Ravi Date: Thu, 23 Sep 2021 18:59:06 +0530 Subject: [PATCH 2/6] Changes made based on lint errors --- client/src/modules/dashboard/Events/components/Actions.tsx | 5 ++--- .../modules/dashboard/Events/components/SendEmailModal.tsx | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/client/src/modules/dashboard/Events/components/Actions.tsx b/client/src/modules/dashboard/Events/components/Actions.tsx index bb435e4f64..29d383a485 100644 --- a/client/src/modules/dashboard/Events/components/Actions.tsx +++ b/client/src/modules/dashboard/Events/components/Actions.tsx @@ -1,16 +1,15 @@ +import { useDisclosure } from '@chakra-ui/hooks'; import { Button, HStack } from '@chakra-ui/react'; import { useConfirm, useConfirmDelete } from 'chakra-confirm'; import { LinkButton } from 'chakra-next-link'; import React, { useMemo } from 'react'; -import { useDisclosure } from '@chakra-ui/hooks'; - import { EVENT, EVENTS } from '../graphql/queries'; +import SendEmailModal from './SendEmailModal'; import { Event, useCancelEventMutation, useDeleteEventMutation, } from 'generated/graphql'; -import SendEmailModal from './SendEmailModal'; interface ActionsProps { event: Pick; diff --git a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx index 7178fa00df..b9dd680264 100644 --- a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx +++ b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx @@ -11,8 +11,8 @@ import { ModalOverlay, } from '@chakra-ui/modal'; import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'; -import { useSendEventInviteMutation } from 'generated/graphql'; import { useForm } from 'react-hook-form'; +import { useSendEventInviteMutation } from 'generated/graphql'; interface SendEmailModalProps { onClose: () => any; @@ -43,7 +43,7 @@ const SendEmailModal: React.FC = ({ const onSubmit = (data: any) => { console.log(data); const emailGroups = []; - for (let key of Object.keys(data)) { + for (const key of Object.keys(data)) { if (data[key]) { emailGroups.push(key); } From 41589dd549e52ff0cd10f304f44ebbf493a67a01 Mon Sep 17 00:00:00 2001 From: Ravi Date: Fri, 24 Sep 2021 00:03:15 +0530 Subject: [PATCH 3/6] Updated the components for linter rules to Pass --- client/src/generated/graphql.tsx | 14 ++++++-------- .../dashboard/Events/components/SendEmailModal.tsx | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/client/src/generated/graphql.tsx b/client/src/generated/graphql.tsx index 6b6d1890ef..1fc5a0c1c3 100644 --- a/client/src/generated/graphql.tsx +++ b/client/src/generated/graphql.tsx @@ -4,14 +4,12 @@ export type Maybe = T | null; export type Exact = { [K in keyof T]: T[K]; }; -export type MakeOptional = Omit & - { - [SubKey in K]?: Maybe; - }; -export type MakeMaybe = Omit & - { - [SubKey in K]: Maybe; - }; +export type MakeOptional = Omit & { + [SubKey in K]?: Maybe; +}; +export type MakeMaybe = Omit & { + [SubKey in K]: Maybe; +}; const defaultOptions = {}; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { diff --git a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx index b9dd680264..b65816b105 100644 --- a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx +++ b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx @@ -11,9 +11,9 @@ import { ModalOverlay, } from '@chakra-ui/modal'; import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'; +import React from 'react'; import { useForm } from 'react-hook-form'; import { useSendEventInviteMutation } from 'generated/graphql'; - interface SendEmailModalProps { onClose: () => any; isOpen: boolean; From a0b8c47d4b846b948b4146cc3cee00f1af261552 Mon Sep 17 00:00:00 2001 From: Ravi Date: Fri, 24 Sep 2021 13:45:10 +0530 Subject: [PATCH 4/6] Updated the Interested button to Cancelled and Updated the Queries in resolver --- .../Events/components/SendEmailModal.tsx | 8 ++++---- server/src/controllers/Events/resolver.ts | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx index b65816b105..f8088a53ce 100644 --- a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx +++ b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx @@ -34,7 +34,7 @@ const SendEmailModal: React.FC = ({ const checkAtLeastOne = () => { return getValues('confirmed') || getValues('on_waitlist') || - getValues('interested') + getValues('canceled') ? true : 'Please tell me if this is too hard.'; }; @@ -67,6 +67,7 @@ const SendEmailModal: React.FC = ({ Confirmed @@ -76,10 +77,9 @@ const SendEmailModal: React.FC = ({ Waitlist - Interested + Cancelled diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index f9ee8da4ac..b6331986e6 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -305,19 +305,30 @@ ${venue.postal_code}
} if (emailGroups.includes('on_waitlist')) { const waitlistUsers: string[] = event.rsvps - .filter((rsvp) => rsvp.on_waitlist) + .filter((rsvp) => rsvp.on_waitlist && rsvp.interested) .map(({ user }) => user.email); addresses.push(...waitlistUsers); } if (emailGroups.includes('confirmed')) { const confirmedUsers: string[] = event.rsvps - .filter((rsvp) => !rsvp.on_waitlist) + .filter( + (rsvp) => !rsvp.on_waitlist && !rsvp.canceled && rsvp.interested, + ) + .map(({ user }) => user.email); + addresses.push(...confirmedUsers); + } + if (emailGroups.includes('canceled')) { + const confirmedUsers: string[] = event.rsvps + .filter((rsvp) => rsvp.canceled && rsvp.interested) .map(({ user }) => user.email); addresses.push(...confirmedUsers); } console.log(addresses); + if (!addresses.length) { + return true; + } const subject = `Invitation to ${event.name}.`; const chapterURL = `${process.env.CLIENT_LOCATION}/chapters/${event.chapter.id}`; From 48b737823c004b44ca6afdca7184e115f9275012 Mon Sep 17 00:00:00 2001 From: Ravi Date: Fri, 24 Sep 2021 23:40:02 +0530 Subject: [PATCH 5/6] Updated the Code based on the PR review 1. sendEmailModal.tsx 1. Created the Interface of FormInputs 2. Used the interface in useForm 3. renamed the metho to atleastOneChecked 4. Removed the Extra error message in atleastonechecked 5. Removed defaultChecked 2. resolver.ts 1. Added the strict type Checking in emailGroups Parameter 2. Removed the console.log --- .../Events/components/SendEmailModal.tsx | 39 +++++++++++-------- server/src/controllers/Events/resolver.ts | 4 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx index f8088a53ce..8588d6e66d 100644 --- a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx +++ b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx @@ -19,6 +19,11 @@ interface SendEmailModalProps { isOpen: boolean; eventId: number; } +interface FormInputs { + confirmed: boolean; + on_waitlist: boolean; + canceled: boolean; +} const SendEmailModal: React.FC = ({ onClose, isOpen, @@ -29,27 +34,28 @@ const SendEmailModal: React.FC = ({ getValues, handleSubmit, formState: { errors }, - } = useForm(); + } = useForm({ defaultValues: { confirmed: true } }); - const checkAtLeastOne = () => { - return getValues('confirmed') || + const atLeastOneChecked = () => { + return ( + getValues('confirmed') || getValues('on_waitlist') || getValues('canceled') - ? true - : 'Please tell me if this is too hard.'; + ); }; const [publish] = useSendEventInviteMutation(); - const onSubmit = (data: any) => { - console.log(data); + const onSubmit = (data: FormInputs) => { const emailGroups = []; - for (const key of Object.keys(data)) { - if (data[key]) { - emailGroups.push(key); - } + if (data.confirmed) { + emailGroups.push('confirmed'); + } + if (data.canceled) { + emailGroups.push('canceled'); + } + if (data.on_waitlist) { + emailGroups.push('on_waitlist'); } - console.log(emailGroups); - publish({ variables: { id: eventId, emailGroups: emailGroups } }); onClose(); }; @@ -66,18 +72,17 @@ const SendEmailModal: React.FC = ({ Confirmed Waitlist Cancelled diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index b6331986e6..c162b2c8bf 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -279,7 +279,7 @@ ${venue.postal_code}
nullable: true, defaultValue: ['interested'], }) - emailGroups: Array, + emailGroups: Array<'confirmed' | 'on_waitlist' | 'canceled' | 'interested'>, ) { const event = await Event.findOne(id, { relations: [ @@ -324,8 +324,6 @@ ${venue.postal_code}
addresses.push(...confirmedUsers); } - console.log(addresses); - if (!addresses.length) { return true; } From e3a31ae56e405043f9e0e271f9fd8641e0cfb2bc Mon Sep 17 00:00:00 2001 From: Ravi Date: Mon, 27 Sep 2021 16:30:05 +0530 Subject: [PATCH 6/6] Updated the error message in the Modal --- .../src/modules/dashboard/Events/components/SendEmailModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx index 8588d6e66d..17a77b0d62 100644 --- a/client/src/modules/dashboard/Events/components/SendEmailModal.tsx +++ b/client/src/modules/dashboard/Events/components/SendEmailModal.tsx @@ -92,7 +92,7 @@ const SendEmailModal: React.FC = ({ - Please select atleast one checkbox + Please select at least one checkbox )}