feat: [Microba-1620] Add email form to BulkEmailTool (#8)
* feat: [MB-1620] Add email body editor Add email body and subject line to bulk email tool.
This commit is contained in:
19
package-lock.json
generated
19
package-lock.json
generated
@@ -7176,6 +7176,15 @@
|
||||
"loader-utils": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@tinymce/tinymce-react": {
|
||||
"version": "3.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@tinymce/tinymce-react/-/tinymce-react-3.13.0.tgz",
|
||||
"integrity": "sha512-8+OHYIUP9W5D5z7gknausaG48ovQtEvjcK4c+zpha7QppRVVX0ltaINpo10V6Vb4qj9Jf7ZFfZpMRxxcFL2YvQ==",
|
||||
"requires": {
|
||||
"prop-types": "^15.6.2",
|
||||
"tinymce": "^5.5.1"
|
||||
}
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
@@ -22486,6 +22495,16 @@
|
||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
||||
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
||||
},
|
||||
"tinymce": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-5.10.2.tgz",
|
||||
"integrity": "sha512-5QhnZ6c8F28fYucLLc00MM37fZoAZ4g7QCYzwIl38i5TwJR5xGqzOv6YMideyLM4tytCzLCRwJoQen2LI66p5A=="
|
||||
},
|
||||
"tinymce-language-selector": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/tinymce-language-selector/-/tinymce-language-selector-1.0.4.tgz",
|
||||
"integrity": "sha512-Ko2n2oRFGIZAtWqoG8O8B5XHxZEWZ/zRabPZ5rVeMzDTZhnbVX9oOgW+Ie18PRNMTNtOpI15OUhvrT07c0rVkw=="
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@fortawesome/react-fontawesome": "0.1.16",
|
||||
"@tinymce/tinymce-react": "^3.13.0",
|
||||
"classnames": "^2.3.1",
|
||||
"core-js": "3.15.2",
|
||||
"prop-types": "15.7.2",
|
||||
"react": "16.14.0",
|
||||
@@ -53,7 +55,9 @@
|
||||
"react-router": "5.2.1",
|
||||
"react-router-dom": "5.3.0",
|
||||
"redux": "4.1.2",
|
||||
"regenerator-runtime": "0.13.9"
|
||||
"regenerator-runtime": "0.13.9",
|
||||
"tinymce": "^5.10.2",
|
||||
"tinymce-language-selector": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/frontend-build": "8.1.6",
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { Button, Form } from '@edx/paragon';
|
||||
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import TextEditor from './TextEditor';
|
||||
|
||||
export default function BulkEmailBody() {
|
||||
return <div />;
|
||||
const editorRef = useRef(null);
|
||||
const onInit = (event, editor) => { editorRef.current = editor; };
|
||||
|
||||
return (
|
||||
<div className="w-100 m-auto p-lg-4 p-2.5">
|
||||
<Form>
|
||||
<Form.Group controlId="emailSubject">
|
||||
<Form.Label>
|
||||
<FormattedMessage
|
||||
id="bulk.email.subject.label"
|
||||
defaultMessage="Subject:"
|
||||
description="Email subject line input label. Meant to have colon or equivilant punctuation."
|
||||
/>
|
||||
</Form.Label>
|
||||
<Form.Control className="w-lg-50" />
|
||||
</Form.Group>
|
||||
<Form.Group controlId="emailBody">
|
||||
<Form.Label>
|
||||
<FormattedMessage
|
||||
id="bulk.email.body.label"
|
||||
defaultMessage="Body:"
|
||||
description="Email Body label. Meant to have colon or equivilant punctuation."
|
||||
/>
|
||||
</Form.Label>
|
||||
<TextEditor onInit={onInit} />
|
||||
</Form.Group>
|
||||
<Button variant="primary" type="submit">
|
||||
<FormattedMessage
|
||||
id="bulk.email.submit.button"
|
||||
defaultMessage="Submit"
|
||||
description="Submit/Send email button"
|
||||
/>
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Spinner } from '@edx/paragon';
|
||||
@@ -8,15 +9,26 @@ import BulkEmailBody from './BulkEmailBody';
|
||||
import BulkEmailTaskManager from './bulk-email-task-manager/BulkEmailTaskManager';
|
||||
import Navigationtabs from '../navigation-tabs/NavigationTabs';
|
||||
import { getCourseHomeCourseMetadata } from './api';
|
||||
import useMobileResponsive from '../../utils/useMobileResponsive';
|
||||
|
||||
export default function BulkEmailTool() {
|
||||
const { courseId } = useParams();
|
||||
|
||||
const [courseMetadata, setCourseMetadata] = useState();
|
||||
const isMobile = useMobileResponsive();
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchTabData() {
|
||||
const data = await getCourseHomeCourseMetadata(courseId);
|
||||
let data;
|
||||
try {
|
||||
data = await getCourseHomeCourseMetadata(courseId);
|
||||
} catch (e) {
|
||||
setCourseMetadata({
|
||||
isStaff: false,
|
||||
tabs: [],
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { tabs, is_staff: isStaff } = data;
|
||||
setCourseMetadata({
|
||||
isStaff,
|
||||
@@ -31,7 +43,7 @@ export default function BulkEmailTool() {
|
||||
courseMetadata.isStaff ? (
|
||||
<div>
|
||||
<Navigationtabs courseId={courseId} tabData={courseMetadata.tabs} />
|
||||
<div className="border border-primary-200">
|
||||
<div className={classnames({ 'border border-primary-200': !isMobile })}>
|
||||
<div className="row">
|
||||
<BulkEmailRecepient courseId={courseId} />
|
||||
</div>
|
||||
|
||||
59
src/components/bulk-email-tool/TextEditor.jsx
Normal file
59
src/components/bulk-email-tool/TextEditor.jsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { Editor } from '@tinymce/tinymce-react';
|
||||
import PropTypes from 'prop-types';
|
||||
import 'tinymce';
|
||||
|
||||
import 'tinymce/icons/default';
|
||||
import 'tinymce/themes/silver';
|
||||
import 'tinymce/skins/ui/oxide/skin.css';
|
||||
import 'tinymce/plugins/advlist';
|
||||
import 'tinymce/plugins/code';
|
||||
import 'tinymce/plugins/emoticons';
|
||||
import 'tinymce/plugins/emoticons/js/emojis';
|
||||
import 'tinymce/plugins/link';
|
||||
import 'tinymce/plugins/lists';
|
||||
import 'tinymce/plugins/table';
|
||||
import 'tinymce-language-selector';
|
||||
|
||||
import contentUiCss from 'tinymce/skins/ui/oxide/content.css';
|
||||
import contentCss from 'tinymce/skins/content/default/content.css';
|
||||
|
||||
export default function TextEditor(props) {
|
||||
const {
|
||||
onChange, onKeyUp, onInit, disabled,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Editor
|
||||
initialValue=""
|
||||
init={{
|
||||
selector: 'textarea#editor',
|
||||
height: 600,
|
||||
plugins: 'advlist code emoticons link lists table',
|
||||
toolbar: 'bold italic | bullist numlist | link emoticons',
|
||||
skin: false,
|
||||
content_css: false,
|
||||
content_style: `${contentUiCss.toString()}\n${contentCss.toString()}`,
|
||||
extended_valid_elements: 'span[lang|id] -span',
|
||||
}}
|
||||
onChange={onChange}
|
||||
onKeyUp={onKeyUp}
|
||||
onInit={onInit}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
TextEditor.defaultProps = {
|
||||
onChange: () => {},
|
||||
onKeyUp: () => {},
|
||||
onInit: () => {},
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
TextEditor.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
onKeyUp: PropTypes.func,
|
||||
onInit: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
42
src/utils/useMobileResponsive.js
Normal file
42
src/utils/useMobileResponsive.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
// NOTE: These are the breakpoints used in Bootstrap v4.0.0 as seen in
|
||||
// the documentation (https://getbootstrap.com/docs/4.0/layout/overview/#responsive-breakpoints)
|
||||
const breakpoints = {
|
||||
extraSmall: {
|
||||
maxWidth: 575.98,
|
||||
},
|
||||
small: {
|
||||
minWidth: 576,
|
||||
maxWidth: 767.98,
|
||||
},
|
||||
medium: {
|
||||
minWidth: 768,
|
||||
maxWidth: 991.98,
|
||||
},
|
||||
large: {
|
||||
minWidth: 992,
|
||||
maxWidth: 1199.98,
|
||||
},
|
||||
extraLarge: {
|
||||
minWidth: 1200,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A react hook used to determine if the current window is mobile or not.
|
||||
* returns true if the window is of mobile size.
|
||||
*/
|
||||
export default function useMobileResponsive(breakpoint) {
|
||||
const [isMobileWindow, setIsMobileWindow] = useState();
|
||||
const checkForMobile = () => {
|
||||
setIsMobileWindow(window.matchMedia(`(max-width: ${breakpoint || breakpoints.small.maxWidth}px)`).matches);
|
||||
};
|
||||
useEffect(() => {
|
||||
checkForMobile();
|
||||
window.addEventListener('resize', checkForMobile);
|
||||
// return this function here to clean up the event listener
|
||||
return () => window.removeEventListener('resize', checkForMobile);
|
||||
}, []);
|
||||
return isMobileWindow;
|
||||
}
|
||||
Reference in New Issue
Block a user