Skip to content

Commit e6c995f

Browse files
Kyrylo Hudym-LevkovychKyrylo Hudym-Levkovych
authored andcommitted
feat: same titles for sent email groups
1 parent 10f906b commit e6c995f

File tree

10 files changed

+96
-23
lines changed

10 files changed

+96
-23
lines changed

src/components/bulk-email-tool/BulkEmailTool.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ export default function BulkEmailTool() {
4040
/>
4141
</div>
4242
<div className="row py-5">
43-
<BulkEmailTaskManager courseId={courseId} />
43+
<BulkEmailTaskManager
44+
courseId={courseId}
45+
courseModes={courseMetadata.courseModes}
46+
/>
4447
</div>
4548
</Container>
4649
</BulkEmailProvider>

src/components/bulk-email-tool/bulk-email-form/BulkEmailForm.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
} from './data/actions';
2929
import { editScheduledEmailThunk, postBulkEmailThunk } from './data/thunks';
3030
import { getScheduledBulkEmailThunk } from '../bulk-email-task-manager/bulk-email-scheduled-emails-table/data/thunks';
31+
import { getDisplayText } from '../utils';
3132

3233
import './bulkEmailForm.scss';
3334

@@ -219,7 +220,7 @@ function BulkEmailForm(props) {
219220
<p>{intl.formatMessage(messages.bulkEmailTaskAlertRecipients, { subject: editor.emailSubject })}</p>
220221
<ul className="list-unstyled">
221222
{editor.emailRecipients.map((group) => (
222-
<li key={group}>{group}</li>
223+
<li key={group}>{getDisplayText(group, courseModes)}</li>
223224
))}
224225
</ul>
225226
{!isScheduled && (
@@ -246,7 +247,7 @@ function BulkEmailForm(props) {
246247
<p>{intl.formatMessage(messages.bulkEmailTaskAlertEditingTo)}</p>
247248
<ul className="list-unstyled">
248249
{editor.emailRecipients.map((group) => (
249-
<li key={group}>{group}</li>
250+
<li key={group}>{getDisplayText(group, courseModes)}</li>
250251
))}
251252
</ul>
252253
<p>{intl.formatMessage(messages.bulkEmailTaskAlertEditingWarning)}</p>

src/components/bulk-email-tool/bulk-email-form/bulk-email-recipient/BulkEmailRecipient.jsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { Form } from '@openedx/paragon';
44
import { FormattedMessage } from '@edx/frontend-platform/i18n';
5+
import { RECIPIENTS_DISPLAY_NAMES } from '../../utils';
56

67
import './bulkEmailRecepient.scss';
78

@@ -41,7 +42,7 @@ export default function BulkEmailRecipient(props) {
4142
<Form.Checkbox key="myself" value="myself" className="mt-2.5 col col-lg-4 col-sm-6 col-12">
4243
<FormattedMessage
4344
id="bulk.email.form.recipients.myself"
44-
defaultMessage="Myself"
45+
defaultMessage={RECIPIENTS_DISPLAY_NAMES.myself}
4546
description="A selectable choice from a list of potential email recipients"
4647
/>
4748
</Form.Checkbox>
@@ -52,7 +53,7 @@ export default function BulkEmailRecipient(props) {
5253
>
5354
<FormattedMessage
5455
id="bulk.email.form.recipients.staff"
55-
defaultMessage="Staff/Administrators"
56+
defaultMessage={RECIPIENTS_DISPLAY_NAMES.staff}
5657
description="A selectable choice from a list of potential email recipients"
5758
/>
5859
</Form.Checkbox>
@@ -99,7 +100,7 @@ export default function BulkEmailRecipient(props) {
99100
>
100101
<FormattedMessage
101102
id="bulk.email.form.recipients.learners"
102-
defaultMessage="All Learners"
103+
defaultMessage={RECIPIENTS_DISPLAY_NAMES.learners}
103104
description="A selectable choice from a list of potential email recipients"
104105
/>
105106
</Form.Checkbox>

src/components/bulk-email-tool/bulk-email-form/test/BulkEmailForm.test.jsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { BulkEmailContext, BulkEmailProvider } from '../../bulk-email-context';
1313
import { formatDate } from '../../../../utils/formatDateAndTime';
1414
import cohortFactory from '../data/__factories__/bulkEmailFormCohort.factory';
1515
import courseModeFactory from '../data/__factories__/bulkEmailFormCourseMode.factory';
16+
import { RECIPIENTS_DISPLAY_NAMES } from '../../utils';
1617

1718
jest.mock('../../text-editor/TextEditor');
1819

@@ -75,8 +76,8 @@ describe('bulk-email-form', () => {
7576
success: true,
7677
});
7778
render(renderBulkEmailForm());
78-
fireEvent.click(screen.getByRole('checkbox', { name: 'Myself' }));
79-
expect(screen.getByRole('checkbox', { name: 'Myself' })).toBeChecked();
79+
fireEvent.click(screen.getByRole('checkbox', { name: RECIPIENTS_DISPLAY_NAMES.myself }));
80+
expect(screen.getByRole('checkbox', { name: RECIPIENTS_DISPLAY_NAMES.myself })).toBeChecked();
8081
fireEvent.change(screen.getByRole('textbox', { name: 'Subject' }), { target: { value: 'test subject' } });
8182
fireEvent.change(screen.getByTestId('textEditor'), { target: { value: 'test body' } });
8283
fireEvent.click(screen.getByText('Send email'));
@@ -90,7 +91,7 @@ describe('bulk-email-form', () => {
9091
axiosMock.onPost(`${getConfig().LMS_BASE_URL}/courses/test/instructor/api/send_email`).reply(500);
9192
render(renderBulkEmailForm());
9293
const subjectLine = screen.getByRole('textbox', { name: 'Subject' });
93-
const recipient = screen.getByRole('checkbox', { name: 'Myself' });
94+
const recipient = screen.getByRole('checkbox', { name: RECIPIENTS_DISPLAY_NAMES.myself });
9495
fireEvent.click(recipient);
9596
fireEvent.change(subjectLine, { target: { value: 'test subject' } });
9697
fireEvent.change(screen.getByTestId('textEditor'), { target: { value: 'test body' } });
@@ -99,9 +100,9 @@ describe('bulk-email-form', () => {
99100
fireEvent.click(await screen.findByRole('button', { name: /continue/i }));
100101
expect(await screen.findByText('An error occured while attempting to send the email.')).toBeInTheDocument();
101102
});
102-
test('Checking "All Learners" disables each learner group', async () => {
103+
test('Checking "All Students" disables each learner group', async () => {
103104
render(renderBulkEmailForm());
104-
fireEvent.click(screen.getByRole('checkbox', { name: 'All Learners' }));
105+
fireEvent.click(screen.getByRole('checkbox', { name: RECIPIENTS_DISPLAY_NAMES.learners }));
105106
const verifiedLearners = screen.getByRole('checkbox', { name: 'Learners in the Verified Certificate Track' });
106107
const auditLearners = screen.getByRole('checkbox', { name: 'Learners in the Audit Track' });
107108
const { cohorts } = cohortFactory.build();
@@ -130,7 +131,7 @@ describe('bulk-email-form', () => {
130131
test('Adds scheduling data to POST requests when schedule is selected', async () => {
131132
const postBulkEmailInstructorTask = jest.spyOn(bulkEmailFormApi, 'postBulkEmailInstructorTask');
132133
render(renderBulkEmailForm());
133-
fireEvent.click(screen.getByRole('checkbox', { name: 'Myself' }));
134+
fireEvent.click(screen.getByRole('checkbox', { name: RECIPIENTS_DISPLAY_NAMES.myself }));
134135
fireEvent.change(screen.getByRole('textbox', { name: 'Subject' }), { target: { value: 'test subject' } });
135136
fireEvent.change(screen.getByTestId('textEditor'), { target: { value: 'test body' } });
136137
const scheduleCheckbox = screen.getByText('Schedule this email for a future date');

src/components/bulk-email-tool/bulk-email-task-manager/BulkEmailContentHistory.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import messages from './messages';
1313
import { getSentEmailHistory } from './data/api';
1414
import BulkEmailTaskManagerTable from './BulkEmailHistoryTable';
1515
import ViewEmailModal from './ViewEmailModal';
16+
import { RECIPIENTS_DISPLAY_NAMES } from '../utils';
1617

1718
function BulkEmailContentHistory({ intl }) {
1819
const { courseId } = useParams();
@@ -55,7 +56,7 @@ function BulkEmailContentHistory({ intl }) {
5556
const tableData = emailHistoryData?.map((item) => ({
5657
...item,
5758
subject: item.email.subject,
58-
sent_to: item.sent_to.join(', '),
59+
sent_to: item.sent_to.map((recipient) => RECIPIENTS_DISPLAY_NAMES[recipient] || recipient).join(', '),
5960
created: new Date(item.created).toLocaleString(),
6061
}));
6162
return tableData || [];

src/components/bulk-email-tool/bulk-email-task-manager/BulkEmailTaskManager.jsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import messages from './messages';
99
import BulkEmailScheduledEmailsTable from './bulk-email-scheduled-emails-table';
1010
import BulkEmailPendingTasksAlert from './BulkEmailPendingTasksAlert';
1111

12-
function BulkEmailTaskManager({ intl, courseId }) {
12+
function BulkEmailTaskManager({ intl, courseId, courseModes }) {
1313
return (
1414
<div className="w-100">
1515
{getConfig().SCHEDULE_EMAIL_SECTION && (
1616
<div>
1717
<h2 className="h3 text-primary-500">{intl.formatMessage(messages.scheduledEmailsTableHeader)}</h2>
18-
<BulkEmailScheduledEmailsTable />
18+
<BulkEmailScheduledEmailsTable courseModes={courseModes} />
1919
</div>
2020
)}
2121
<div>
@@ -36,6 +36,12 @@ function BulkEmailTaskManager({ intl, courseId }) {
3636
BulkEmailTaskManager.propTypes = {
3737
intl: intlShape.isRequired,
3838
courseId: PropTypes.string.isRequired,
39+
courseModes: PropTypes.arrayOf(
40+
PropTypes.shape({
41+
slug: PropTypes.string.isRequired,
42+
name: PropTypes.string.isRequired,
43+
}),
44+
).isRequired,
3945
};
4046

4147
export default injectIntl(BulkEmailTaskManager);

src/components/bulk-email-tool/bulk-email-task-manager/ViewEmailModal.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function ViewEmailModal({
3333
<p className="pl-2">{messageContent.created}</p>
3434
</div>
3535
<div className="d-flex flex-row">
36-
<p>{intl.formatMessage(messages.modalMessageSentTo)}</p>
36+
<p className="flex-shrink-0">{intl.formatMessage(messages.modalMessageSentTo)}</p>
3737
<p className="pl-2">{messageContent.sent_to}</p>
3838
</div>
3939
<hr className="py-2" />

src/components/bulk-email-tool/bulk-email-task-manager/bulk-email-scheduled-emails-table/BulkEmailScheduledEmailsTable.jsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import React, {
55
useCallback, useContext, useState, useEffect,
66
} from 'react';
7+
import PropTypes from 'prop-types';
78
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
89
import {
910
Alert, DataTable, Icon, IconButton, useToggle,
@@ -19,20 +20,22 @@ import ViewEmailModal from '../ViewEmailModal';
1920
import { copyToEditor } from '../../bulk-email-form/data/actions';
2021
import TaskAlertModal from '../../task-alert-modal';
2122
import { formatDate, formatTime } from '../../../../utils/formatDateAndTime';
23+
import { getDisplayText, getRecipientFromDisplayText } from '../../utils';
2224

23-
function flattenScheduledEmailsArray(emails) {
25+
function flattenScheduledEmailsArray(emails, courseModes) {
2426
return emails.map((email) => ({
2527
schedulingId: email.id,
2628
emailId: email.courseEmail.id,
2729
task: email.task,
2830
taskDue: new Date(email.taskDue).toLocaleString(),
2931
taskDueUTC: email.taskDue,
3032
...email.courseEmail,
31-
targets: email.courseEmail.targets.join(', '),
33+
targets: email.courseEmail.targets
34+
.map((recipient) => getDisplayText(recipient, courseModes)).join(', '),
3235
}));
3336
}
3437

35-
function BulkEmailScheduledEmailsTable({ intl }) {
38+
function BulkEmailScheduledEmailsTable({ intl, courseModes }) {
3639
const { courseId } = useParams();
3740
const [{ scheduledEmailsTable }, dispatch] = useContext(BulkEmailContext);
3841
const [tableData, setTableData] = useState([]);
@@ -44,8 +47,8 @@ function BulkEmailScheduledEmailsTable({ intl }) {
4447
const [currentTask, setCurrentTask] = useState({});
4548

4649
useEffect(() => {
47-
setTableData(flattenScheduledEmailsArray(scheduledEmailsTable.results));
48-
}, [scheduledEmailsTable.results]);
50+
setTableData(flattenScheduledEmailsArray(scheduledEmailsTable.results, courseModes));
51+
}, [scheduledEmailsTable.results, courseModes]);
4952

5053
const fetchTableData = useCallback((args) => {
5154
dispatch(getScheduledBulkEmailThunk(courseId, args.pageIndex + 1));
@@ -96,7 +99,8 @@ function BulkEmailScheduledEmailsTable({ intl }) {
9699
},
97100
} = row;
98101
const dateTime = new Date(taskDueUTC);
99-
const emailRecipients = targets.replaceAll('-', ':').split(', ');
102+
const emailRecipients = targets
103+
.split(', ').map((recipient) => getRecipientFromDisplayText(recipient, courseModes));
100104
const scheduleDate = formatDate(dateTime);
101105
const scheduleTime = formatTime(dateTime);
102106
dispatch(
@@ -198,6 +202,12 @@ function BulkEmailScheduledEmailsTable({ intl }) {
198202

199203
BulkEmailScheduledEmailsTable.propTypes = {
200204
intl: intlShape.isRequired,
205+
courseModes: PropTypes.arrayOf(
206+
PropTypes.shape({
207+
slug: PropTypes.string.isRequired,
208+
name: PropTypes.string.isRequired,
209+
}),
210+
).isRequired,
201211
};
202212

203213
export default injectIntl(BulkEmailScheduledEmailsTable);

src/components/bulk-email-tool/bulk-email-task-manager/bulk-email-scheduled-emails-table/test/BulkEmailScheduledEmailsTable.test.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { BulkEmailProvider } from '../../../bulk-email-context';
1313
import BulkEmailScheduledEmailsTable from '..';
1414
import scheduledEmailsFactory from './__factories__/scheduledEmails.factory';
1515
import * as actions from '../../../bulk-email-form/data/actions';
16+
import { RECIPIENTS_DISPLAY_NAMES } from '../../../utils';
1617

1718
jest.mock('react-router-dom', () => ({
1819
...jest.requireActual('react-router-dom'),
@@ -42,7 +43,7 @@ describe('BulkEmailScheduledEmailsTable', () => {
4243
.onGet(`${getConfig().LMS_BASE_URL}/api/instructor_task/v1/schedules/test-id/bulk_email/?page=1`)
4344
.reply(200, scheduledEmailsFactory.build(1));
4445
render(renderBulkEmailScheduledEmailsTable());
45-
expect(await screen.findByText('learners')).toBeTruthy();
46+
expect(await screen.findByText(RECIPIENTS_DISPLAY_NAMES.learners)).toBeTruthy();
4647
expect(await screen.findByText('subject')).toBeTruthy();
4748
expect(await screen.findByText('edx')).toBeTruthy();
4849
expect(await screen.findByLabelText('View')).toBeTruthy();
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
export const RECIPIENTS_DISPLAY_NAMES = {
2+
myself: 'Myself',
3+
staff: 'Staff/Administrators',
4+
learners: 'All Learners',
5+
'Staff and instructors': 'Staff/Administrators',
6+
'All students': 'All Learners',
7+
};
8+
9+
// Output: { 'Myself': 'myself', 'Staff/Administrators': 'staff', 'All Learners': 'learners' }
10+
export const REVERSE_RECIPIENTS_DISPLAY_NAMES = Object.fromEntries(
11+
Object.entries(RECIPIENTS_DISPLAY_NAMES).map(([key, value]) => [value, key]),
12+
);
13+
14+
export const getDisplayText = (recipient, courseModes) => {
15+
const normalizedRecipient = recipient.replace(/-/, ':');
16+
17+
if (normalizedRecipient.startsWith('track') && courseModes) {
18+
const courseModeSlug = normalizedRecipient.split(':')[1];
19+
const courseMode = courseModes.find((mode) => mode.slug === courseModeSlug);
20+
if (courseMode) {
21+
return `Learners in the ${courseMode.name} Track`;
22+
}
23+
}
24+
25+
if (normalizedRecipient.startsWith('cohort')) {
26+
const cohort = normalizedRecipient.replace(/:/, ': ');
27+
return cohort.charAt(0).toUpperCase() + cohort.slice(1);
28+
}
29+
30+
return RECIPIENTS_DISPLAY_NAMES[recipient] || recipient;
31+
};
32+
33+
export const getRecipientFromDisplayText = (displayText, courseModes) => {
34+
const trackMatch = displayText.match(/^Learners in the (.+) Track$/);
35+
if (trackMatch) {
36+
const courseModeName = trackMatch[1];
37+
const courseMode = courseModes.find(mode => mode.name === courseModeName);
38+
if (courseMode) {
39+
return `track:${courseMode.slug}`;
40+
}
41+
}
42+
43+
const cohortMatch = displayText.match(/^Cohort: (.+)$/);
44+
if (cohortMatch) {
45+
return `cohort:${cohortMatch[1].replace(' ', '-')}`;
46+
}
47+
48+
return REVERSE_RECIPIENTS_DISPLAY_NAMES[displayText] || displayText;
49+
};

0 commit comments

Comments
 (0)