-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alex/implement email notification functionality #464
Open
alexappleget
wants to merge
69
commits into
develop
Choose a base branch
from
alex/implement-email-notification-functionality
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
66835c2
feat((admin)/layout.tsx): began building out admin layout
ryandotfurrer 9049bfa
create admin layout
ryandotfurrer 5f3d9e5
Merge branch 'develop' into ryan/create-admin-layout
ryandotfurrer ac4c293
feat(admin/layout.tsx): added classnames to future components and sor…
ryandotfurrer 13ec1c6
Merge branch 'ryan/create-admin-layout' of https://github.com/LetsGet…
ryandotfurrer d4cfc88
(admin/layout.tsx): use alias import for errorboundary rather than re…
ryandotfurrer a0eafb5
refine admin layout and begin componetizing the layout elements
ryandotfurrer 9bc34ef
refine components and pages for admin route
ryandotfurrer 906e83f
Merge branch 'develop' into ryan/create-admin-layout
ryandotfurrer e880373
attempt to add adminheader props to admin layout so the header can be…
ryandotfurrer 9205f10
remove props from adminheader so the layout itself can be reviewed
ryandotfurrer f50fe0e
Merge branch 'develop' into ryan/create-admin-layout
ryandotfurrer b7685f7
Feat: added in email notification functionality
alexappleget 621497e
fix: removed unused optional props after users in the server action
chris-nowicki 85e48dd
Auto stash before merge of "alex/implement-email-notification-functio…
alexappleget 0ce258f
Fix: Added in comments to fix eslint error for sendEmailNotification()
alexappleget 4f2709f
Fix: Added participants prop to sendEmailNotification function and cr…
alexappleget 1cb0041
Merge branch 'develop' into alex/implement-email-notification-functio…
alexappleget 7c7f7f2
Fix: deleted test file and changed AdminHome to AdminNotifications
alexappleget 76ee015
Fix: Fixed testing issues for the server function on the client side.
alexappleget 7b53303
Fix: created test file for sendEmailNotification.ts file to test the …
alexappleget a1ee17e
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 27737c3
Fix: updated id's and name's to be consistent
alexappleget c0932e2
Fix: Added in group emailing functionality.
alexappleget 415bbcc
Merge branch 'alex/add-a-league-into-email-notification' into alex/im…
alexappleget 7ffb7fd
Fix: Alphabetized imports.
alexappleget 32f507b
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 854634c
Fix: Handled PR comments: deleted unnecessary comments in code, realp…
alexappleget 75e0bde
Fix: Changed the wording of the error.
alexappleget 771d6aa
Fix: Deleted param prop that no longer exists.
alexappleget f147c19
Fix: added the code inside the try block of the function.
alexappleget 0c216bb
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 8d41257
Merge branch 'develop' into alex/implement-email-notification-functio…
alexappleget 3864f3a
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 584de00
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget af8287b
Fix: Edited league id here to my own email test league with just me.
alexappleget 8c86b6e
Fix: Changed variable and function names for more readability.
alexappleget 1fd8fe8
Fix: Cleaned up things per Richard's comments.
alexappleget 1903d27
Fix: Alphabetized and edited a small change.
alexappleget 4e44876
Fix: Explained things better in my code.
alexappleget 17e7057
Merge branch 'develop' into alex/implement-email-notification-functio…
alexappleget 75b5b4e
Fix: Added target IDs for bcc
alexappleget 3624dc0
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget cafd345
Fix: Fixed testing.
alexappleget b97e185
Fix: Fixed tests
alexappleget 97f3ae8
Fix: Hard coded bcc target id's
alexappleget 368f41e
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 0d8d397
Merge branch 'develop' into alex/implement-email-notification-functio…
shashilo 9605af5
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 6246c4f
Merge remote-tracking branch 'origin/alex/implement-email-notificatio…
alexappleget 6a58f10
Fix: deleted target ID's
alexappleget 8e3148a
Fix: Fixed tests
alexappleget 9c864d5
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget a0a2c1c
Improved UX for Admin Notification Page (#490)
ryandotfurrer e85408c
Merge branch 'develop' into alex/implement-email-notification-functio…
shashilo b4f7874
Merge branch 'develop' into alex/implement-email-notification-functio…
ryandotfurrer b12ea64
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 3e2aadd
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 247a786
Fix: Changed leagueId to email testing league.
alexappleget 1188d47
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget e55c39f
Fix: Created an api function to convert userIDs into their targetIDs …
alexappleget 8eb3bd6
Fix: Updated the fetch league data function to now be able to fetch t…
alexappleget f4e006c
Fix: Updated sendEmailNotification function test.
alexappleget 0641f59
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget aa4c65d
Fix: Changed file and folder names.
alexappleget 5119a15
Fix: fixed testing errors and changed errors to throw error to be mor…
alexappleget 1717433
Fix: changed mock import to fix test.
alexappleget d207ea0
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget 7bf57bb
Merge remote-tracking branch 'origin/develop' into alex/implement-ema…
alexappleget File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,76 @@ | ||
// /Users/ryanfurrer/Developer/GitHub/gridiron-survivor/app/(admin)/admin/notifications/page.test.tsx | ||
|
||
import { render, screen } from '@testing-library/react'; | ||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'; | ||
import { getCurrentLeague } from '@/api/apiFunctions'; | ||
import { getUserTargets, sendEmailNotifications } from './serverFunctions/serverFunctionHelper'; | ||
import AdminNotifications from './page'; | ||
import React from 'react'; | ||
|
||
let contentInput: HTMLInputElement, | ||
selectAllUsersRadioOption: HTMLElement, | ||
selectRecipientsRadioGroup: HTMLElement, | ||
sendEmailButton: HTMLElement, | ||
subjectInput: HTMLInputElement; | ||
|
||
jest.mock('@/api/apiFunctions', () => ({ | ||
getCurrentLeague: jest.fn(), | ||
})); | ||
|
||
jest.mock('./serverFunctions/serverFunctionHelper', () => ({ | ||
sendEmailNotifications: jest.fn(), | ||
getUserTargets: jest.fn(), | ||
})); | ||
|
||
describe('Admin notifications page', () => { | ||
it(`should render it's content`, () => { | ||
beforeEach(async () => { | ||
jest.clearAllMocks(); | ||
|
||
(getCurrentLeague as jest.Mock).mockResolvedValue({ | ||
participants: ['12345', '1234', '123'], | ||
leagueName: 'Test League', | ||
}); | ||
|
||
(getUserTargets as jest.Mock).mockResolvedValue([ | ||
'target1', | ||
'target2', | ||
'target3', | ||
]); | ||
|
||
render(<AdminNotifications />); | ||
|
||
contentInput = screen.getByTestId('content-text'); | ||
selectAllUsersRadioOption = screen.getByTestId('all-users-option'); | ||
selectRecipientsRadioGroup = screen.getByTestId('radio-group-default'); | ||
sendEmailButton = screen.getByTestId('send-email'); | ||
subjectInput = screen.getByTestId('subject-text'); | ||
}); | ||
|
||
it(`should render it's content`, () => { | ||
choir241 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(sendEmailNotifications as jest.Mock).mockResolvedValue({}); | ||
|
||
const adminNotificationsContent = screen.getByTestId( | ||
'admin-notifications-content', | ||
); | ||
expect(adminNotificationsContent).toBeInTheDocument(); | ||
}); | ||
|
||
it('should call the sendEmailNotifications function with the provided inputs', async () => { | ||
fireEvent.click(selectAllUsersRadioOption); | ||
fireEvent.change(subjectInput, { target: { value: 'Test Title' } }); | ||
fireEvent.change(contentInput, { | ||
target: { value: 'Test message section.' }, | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(sendEmailButton).toBeInTheDocument(); | ||
}); | ||
|
||
fireEvent.submit(sendEmailButton); | ||
|
||
await waitFor(() => { | ||
expect(sendEmailNotifications as jest.Mock).toHaveBeenCalledWith({ | ||
content: 'Test message section.', | ||
groupUsers: ['target1', 'target2', 'target3'], | ||
subject: 'Test Title', | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,163 @@ | ||
// Copyright (c) Gridiron Survivor. | ||
// Licensed under the MIT License. | ||
|
||
import { JSX } from 'react'; | ||
'use client'; | ||
import { Button } from '@/components/Button/Button'; | ||
import { getCurrentLeague } from '@/api/apiFunctions'; | ||
import { Input } from '@/components/Input/Input'; | ||
import { JSX, useState } from 'react'; | ||
import { LabelText } from '@/components/LabelText/LabelText'; | ||
import { | ||
RadioGroupDefault, | ||
RadioGroupDefaultItem, | ||
} from '@/components/RadioGroupDefault/RadioGroupDefault'; | ||
import { sendEmailNotifications } from './serverFunctions/serverFunctionHelper'; | ||
import { Textarea } from '@/components/Textarea/Textarea'; | ||
import React, { useEffect } from 'react'; | ||
import { getUserTargets } from './serverFunctions/serverFunctionHelper'; | ||
|
||
/** | ||
* The admin home page. | ||
* @returns The rendered AdminHome page. | ||
*/ | ||
const AdminNotifications = (): JSX.Element => { | ||
const [content, setContent] = useState<string>(''); | ||
const [emailSubjects, setEmailSubjects] = useState<string>('all users'); | ||
const [groupUsers, setGroupUsers] = useState<string[]>([]); | ||
const [leagueName, setLeagueName] = useState<string>(''); | ||
const [subject, setSubject] = useState<string>(''); | ||
|
||
/** | ||
* To grab all users from the league. | ||
* @returns The league data. | ||
*/ | ||
const getLeagueData = async (): Promise<void> => { | ||
try { | ||
const leagueId = '66f1a8e300102bff03ff'; // EMAIL TEST LEAGUE (DO NOT CHANGE) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we not have this dynamically working? |
||
const leagueData = await getCurrentLeague(leagueId); | ||
const userTargets = await getUserTargets({ | ||
userIDs: leagueData.participants, | ||
}); | ||
setGroupUsers(userTargets); | ||
setLeagueName(leagueData.leagueName); | ||
} catch (error) { | ||
throw error; | ||
} | ||
}; | ||
|
||
/** | ||
* Handle form submission | ||
* @param event - Prevents the default reloading. | ||
*/ | ||
const handleSubmit = async (event: React.FormEvent): Promise<void> => { | ||
event.preventDefault(); | ||
await sendEmailNotifications({ | ||
content, | ||
groupUsers, | ||
subject, | ||
}); | ||
}; | ||
|
||
/** | ||
* Function to handle radio selection logic. | ||
* @param value - Value of the radio buttons. | ||
*/ | ||
const handleRadioChange = (value: string): void => { | ||
setGroupUsers([value]); | ||
setEmailSubjects(value); | ||
}; | ||
|
||
useEffect(() => { | ||
/** | ||
* Fetches the league data. | ||
* @returns The league data. | ||
*/ | ||
const fetchData = async (): Promise<void> => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be in a useEfect or can it just be called when the component mounts? |
||
await getLeagueData(); | ||
}; | ||
fetchData(); | ||
}, []); | ||
|
||
return ( | ||
<section data-testid="admin-notifications-content"> | ||
<p>{`This is where I'd put my notifation dashboard, IF I HAD ONE!`}</p> | ||
<section | ||
className="flex flex-col space-y-6" | ||
data-testid="admin-notifications-content" | ||
> | ||
<p> | ||
Choose the users you would like to email in{' '} | ||
<span className="font-bold text-orange-500">{leagueName}</span>. | ||
</p> | ||
<RadioGroupDefault | ||
defaultValue="all users" | ||
onValueChange={handleRadioChange} | ||
required | ||
> | ||
<div className="flex items-center space-x-2"> | ||
<RadioGroupDefaultItem | ||
value="all users" | ||
id="all" | ||
data-testid="all-users-option" | ||
/> | ||
<LabelText htmlFor="all">All users</LabelText> | ||
</div> | ||
<div className="flex items-center space-x-2"> | ||
<RadioGroupDefaultItem | ||
value="all survivors" | ||
id="survivors" | ||
data-testid="only-survivors-option" | ||
/> | ||
<LabelText htmlFor="survivors">Only the survivors</LabelText> | ||
</div> | ||
<div className="flex items-center space-x-2"> | ||
<RadioGroupDefaultItem | ||
value="all losers" | ||
id="losers" | ||
data-testid="only-losers-option" | ||
/> | ||
<LabelText htmlFor="losers">Only the losers</LabelText> | ||
</div> | ||
</RadioGroupDefault> | ||
<form | ||
onSubmit={handleSubmit} | ||
className="flex flex-col space-y-6 max-w-[80ch]" | ||
> | ||
<div className="flex gap-2 flex-col"> | ||
<LabelText htmlFor="subject" className="text-lg"> | ||
Subject: | ||
</LabelText> | ||
<Input | ||
data-testid="subject-text" | ||
id="subject" | ||
name="subject" | ||
onChange={(e) => setSubject(e.target.value)} | ||
type="text" | ||
/> | ||
</div> | ||
<div className="flex gap-2 flex-col"> | ||
<LabelText htmlFor="content" className="text-lg"> | ||
Message: | ||
</LabelText> | ||
<Textarea | ||
data-testid="content-text" | ||
id="content" | ||
name="content" | ||
onChange={(e) => setContent(e.target.value)} | ||
/> | ||
</div> | ||
<p> | ||
This email will be sent to{' '} | ||
<span className="font-bold text-orange-500"> | ||
{emailSubjects.toLowerCase()} | ||
</span>{' '} | ||
in <span className="font-bold text-orange-500">{leagueName}</span> | ||
</p> | ||
<Button | ||
className="md:max-w-fit" | ||
data-testid="send-email" | ||
label="Send email" | ||
type="submit" | ||
/> | ||
</form> | ||
</section> | ||
); | ||
}; | ||
|
52 changes: 52 additions & 0 deletions
52
app/(admin)/admin/notifications/serverFunctions/serverFunctionHelper.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright (c) Gridiron Survivor. | ||
// Licensed under the MIT License. | ||
|
||
'use server'; | ||
import { messaging } from '@/api/serverConfig'; | ||
import { sendEmailNotifications } from './serverFunctionHelper'; | ||
|
||
jest.mock('./serverFunctionHelper', () => ({ | ||
sendEmailNotifications: jest.fn(), | ||
})); | ||
|
||
jest.mock('@/api/serverConfig', () => ({ | ||
messaging: { | ||
createEmail: jest.fn(), | ||
}, | ||
})); | ||
|
||
describe('SendEmailNotification', () => { | ||
it('should send email with provided information', async () => { | ||
const content = 'Test'; | ||
const groupUsers = ['123456', '12345', '1234']; | ||
const subject = 'This is a test'; | ||
|
||
(sendEmailNotifications as jest.Mock).mockImplementation( | ||
async ({ content, groupUsers, subject }) => { | ||
await (messaging.createEmail as jest.Mock)( | ||
'1234567890', | ||
subject, | ||
content, | ||
[], | ||
'1234567890', | ||
[], | ||
[], | ||
groupUsers, | ||
); | ||
}, | ||
); | ||
|
||
await sendEmailNotifications({ content, groupUsers, subject }); | ||
|
||
expect(messaging.createEmail).toHaveBeenCalledWith( | ||
expect.any(String), | ||
subject, | ||
content, | ||
[], | ||
'1234567890', | ||
[], | ||
[], | ||
groupUsers, | ||
); | ||
}); | ||
}); |
62 changes: 62 additions & 0 deletions
62
app/(admin)/admin/notifications/serverFunctions/serverFunctionHelper.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright (c) Gridiron Survivor. | ||
// Licensed under the MIT License. | ||
|
||
'use server'; | ||
import { ID } from 'appwrite'; | ||
import { messaging, users } from '@/api/serverConfig'; | ||
|
||
/** | ||
* Function to send email. | ||
* @param props - subject, content. | ||
* @param props.content - The actual email you are wanting to send. | ||
* @param props.groupUsers - User id's being passed in from the notification page. | ||
* @param props.subject - The subject of the email. | ||
*/ | ||
export const sendEmailNotifications = async ({ | ||
content, | ||
groupUsers, | ||
subject, | ||
}: { | ||
content: string; | ||
groupUsers: string[]; | ||
subject: string; | ||
}): Promise<void> => { | ||
try { | ||
await messaging.createEmail( | ||
ID.unique(), | ||
subject, | ||
content, | ||
[], | ||
['672a3534000502297d88'], | ||
[], | ||
[], | ||
groupUsers, | ||
); | ||
} catch (error) { | ||
throw error; | ||
} | ||
}; | ||
|
||
/** | ||
* Function to take the userIDs and grab their targetIDs for emailing. | ||
* @param props - userIDs | ||
* @param props.userIDs - All the passed in userIDs. | ||
* @returns {Promise<void>} | ||
*/ | ||
export async function getUserTargets({ | ||
userIDs, | ||
}: { | ||
userIDs: string[]; | ||
}): Promise<string[]> { | ||
try { | ||
const userTargets = await Promise.all( | ||
userIDs.map(async (userID) => { | ||
const userTarget = await users.get(userID); | ||
return userTarget.targets.map((target) => target.$id); | ||
}), | ||
); | ||
return userTargets.flat(); | ||
} catch (error) { | ||
throw error; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { LabelText } from './LabelText'; | ||
import { render } from '@testing-library/react'; | ||
import React from 'react'; | ||
|
||
describe('LabelText Component', () => { | ||
it('renders properly', () => { | ||
const { getByTestId } = render(<LabelText>Test Label</LabelText>); | ||
expect(getByTestId('label-text')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders the correct text', () => { | ||
const { getByTestId } = render(<LabelText>Test Label</LabelText>); | ||
expect(getByTestId('label-text')).toHaveTextContent('Test Label'); | ||
}); | ||
|
||
it('applies disabled styles when peer-disabled', () => { | ||
const { getByTestId } = render( | ||
<LabelText className="peer-disabled">Test Label</LabelText>, | ||
); | ||
expect(getByTestId('label-text')).toHaveClass( | ||
'peer-disabled:cursor-not-allowed', | ||
); | ||
expect(getByTestId('label-text')).toHaveClass('peer-disabled:opacity-70'); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update this if you take my previous suggestions into account.