Skip to content

Commit

Permalink
feat: add success and failure toasts
Browse files Browse the repository at this point in the history
  • Loading branch information
RRanath authored and ccbc-service-account committed Oct 3, 2024
1 parent 018dc75 commit f29240b
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 10 deletions.
18 changes: 17 additions & 1 deletion app/components/AnalystDashboard/AssignmentEmailModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Modal from 'components/Modal';
import * as Sentry from '@sentry/nextjs';
import { useCreateEmailNotificationsMutation } from 'schema/mutations/application/createEmailNotifications';
import { useToast } from 'components/AppProvider';
import { useState } from 'react';

interface Props {
isOpen: boolean;
Expand All @@ -24,6 +26,8 @@ const AssignmentEmailModal: React.FC<Props> = ({
assignments = [],
}) => {
const [createEmailNotifications] = useCreateEmailNotificationsMutation();
const [isLoading, setIsLoading] = useState(false);
const { showToast, hideToast } = useToast();

const analystsToNotify = Array.from(
new Set(assignments.map((assignment) => assignment.assignedTo))
Expand Down Expand Up @@ -63,6 +67,8 @@ const AssignmentEmailModal: React.FC<Props> = ({
};

const notifyAnalysts = async () => {
setIsLoading(true);
hideToast();
try {
const response = await fetch('/api/email/assessmentAssigneeChange', {
method: 'POST',
Expand All @@ -80,11 +86,20 @@ const AssignmentEmailModal: React.FC<Props> = ({
emailRecordResults,
Object.values(details.assessmentsGrouped)
);
showToast('Email notification sent successfully', 'success', 5000);
} catch (error) {
Sentry.captureException({
name: 'Notify Analysts Error',
message: error.message,
});
showToast(
'Email notification did not work, please try again',
'error',
5000
);
} finally {
setIsLoading(false);
onSave();
}
};

Expand All @@ -97,16 +112,17 @@ const AssignmentEmailModal: React.FC<Props> = ({
actions={[
{
id: 'email-confirm-btn',
isLoading,
label: saveLabel,
onClick: async () => {
notifyAnalysts();
onSave();
},
},
{
id: 'email-cancel-btn',
label: cancelLabel,
onClick: () => onCancel(),
disabled: isLoading,
variant: 'secondary',
},
]}
Expand Down
6 changes: 3 additions & 3 deletions app/components/LoadingSpinner.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const LoadingSpinner = ({ color = '#fff' }) => (
const LoadingSpinner = ({ color = '#fff', width = '24', height = '24' }) => (
<svg
width="24"
height="24"
width={width}
height={height}
viewBox="0 0 38 38"
xmlns="http://www.w3.org/2000/svg"
aria-label="loading"
Expand Down
7 changes: 5 additions & 2 deletions app/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import IconButton from '@mui/material/IconButton';
import CloseIcon from '@mui/icons-material/Close';
import { Breakpoint, DialogActions, DialogTitle } from '@mui/material';
import { Button } from '@button-inc/bcgov-theme';
import LoadingSpinner from './LoadingSpinner';

interface Props {
children: React.ReactNode;
Expand All @@ -21,6 +22,7 @@ interface ActionProps {
onClick: Function;
variant?: 'primary' | 'secondary';
disabled?: boolean;
isLoading?: boolean;
}

const Modal: React.FC<Props> = ({
Expand Down Expand Up @@ -67,9 +69,10 @@ const Modal: React.FC<Props> = ({
key={action.label}
onClick={action.onClick}
variant={action.variant || 'primary'}
disabled={action.disabled}
disabled={action.disabled || action.isLoading}
>
{action.label}
{action.label}{' '}
{action.isLoading && <LoadingSpinner width="20" height="20" />}
</Button>
))}
</DialogActions>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, fireEvent, screen } from '@testing-library/react';
import { act, fireEvent, screen, waitFor } from '@testing-library/react';
import { graphql } from 'react-relay';
import ComponentTestingHelper from 'tests/utils/componentTestingHelper';
import AssessmentAssignmentTable, {
Expand Down Expand Up @@ -552,10 +552,38 @@ describe('The AssessmentAssignmentTable component', () => {
expect(notifyButton).toBeDisabled();
});

it('should call correct endpoint when send email notifications confirmed', async () => {
it('should call the correct endpoint when email notifications are confirmed', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({}),
json: () =>
Promise.resolve({
success: true,
emailRecordResults: [
{
messageId: '123',
to: ['tester@gov.bc.ca'],
cc: ['tester@gov.bc.ca'],
contexts: {},
body: 'Test Analyst GIS has assigned you one or more assessment(s)',
subject: 'Assessment Assignee Change Notification',
},
],
details: {
assessmentsGrouped: {
'analyst@gov.bc.ca': [
{
ccbcNumber: 'CCBC-00001',
applicationId: 123,
notificationConnectionId: 'connectionID',
updatedBy: 1,
assignedTo: 'Analyst GIS',
assessmentType: 'technical',
},
],
},
},
}),
status: 200,
})
) as jest.Mock;

Expand All @@ -580,10 +608,56 @@ describe('The AssessmentAssignmentTable component', () => {
fireEvent.click(confirmBtn);
});

expect(fetch).toHaveBeenCalledWith(
expect(global.fetch).toHaveBeenCalledWith(
'/api/email/assessmentAssigneeChange',
expect.objectContaining({ method: 'POST' })
);

await waitFor(() => {
expect(
screen.getByText(/Email notification sent successfully/)
).toBeInTheDocument();
});
});

it('should show the error toast when email notification to CHES failed', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.reject(new Error('oops')),
})
) as jest.Mock;

componentTestingHelper.loadQuery();
componentTestingHelper.renderComponent();

const notifyButton = screen.getByRole('button', {
name: 'Notify by email',
});

await act(async () => {
fireEvent.click(notifyButton);
});

const confirmBtn = screen.getByRole('button', {
name: 'Yes',
});

expect(confirmBtn).toBeInTheDocument();

await act(async () => {
fireEvent.click(confirmBtn);
});

expect(global.fetch).toHaveBeenCalledWith(
'/api/email/assessmentAssigneeChange',
expect.objectContaining({ method: 'POST' })
);

await waitFor(() => {
expect(
screen.getByText(/Email notification did not work, please try again/)
).toBeInTheDocument();
});
});
});

Expand Down

0 comments on commit f29240b

Please sign in to comment.