Refactord email popup screen to use Redx + implement all email redux/saga scaffolding.

This commit is contained in:
Patrick Fic
2020-02-20 16:48:25 -08:00
parent b845e62070
commit cf461b0536
13 changed files with 322 additions and 118 deletions

View File

@@ -0,0 +1,37 @@
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setEmailOptions } from "../../redux/email/email.actions";
import T from "../../emails/parts-order/parts-order.email";
import { REPORT_QUERY_PARTS_ORDER_BY_PK } from "../../emails/parts-order/parts-order.query";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = dispatch => ({
setEmailOptions: e => dispatch(setEmailOptions(e))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function Test({ setEmailOptions }) {
return (
<button
onClick={() =>
setEmailOptions({
messageOptions: {
from: { name: "Kavia Autobdoy", address: "noreply@bodyshop.app" },
to: "patrickwf@gmail.com",
replyTo: "snaptsoft@gmail.com"
},
template: T,
queryConfig: [
REPORT_QUERY_PARTS_ORDER_BY_PK,
{ variables: { id: "46f3aa34-c3bd-46c8-83fc-c93b7ce84f46" } }
]
})
}>
Set email config.
</button>
);
});

View File

@@ -4,35 +4,34 @@ import CKEditor from "@ckeditor/ckeditor5-react";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
export default function SendEmailButtonComponent({
emailConfig,
messageOptions,
handleConfigChange,
handleHtmlChange
}) {
return (
<div>
THis is where the text editing will happen To
<Input
defaultValue={emailConfig.to}
defaultValue={messageOptions.to}
onChange={handleConfigChange}
name='to'
/>
CC
<Input
defaultValue={emailConfig.cc}
defaultValue={messageOptions.cc}
onChange={handleConfigChange}
name='cc'
/>
Subject
<Input
defaultValue={emailConfig.subject}
defaultValue={messageOptions.subject}
onChange={handleConfigChange}
name='subject'
/>
<CKEditor
editor={ClassicEditor}
data={emailConfig.html}
data={messageOptions.html}
onInit={editor => {
// You can store the "editor" and use when it is needed.
//You can store the "editor" and use when it is needed.
console.log("Editor is ready to use!", editor);
}}
onChange={(event, editor) => {

View File

@@ -0,0 +1,100 @@
import { Button, Modal } from "antd";
import React, { useEffect, useState } from "react";
import { useLazyQuery } from "react-apollo";
import { renderEmail } from "react-html-email";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
sendEmail,
toggleEmailOverlayVisible
} from "../../redux/email/email.actions";
import {
selectEmailConfig,
selectEmailVisible
} from "../../redux/email/email.selectors.js";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import EmailOverlayComponent from "./email-overlay.component";
const mapStateToProps = createStructuredSelector({
modalVisible: selectEmailVisible,
emailConfig: selectEmailConfig
});
const mapDispatchToProps = dispatch => ({
toggleEmailOverlayVisible: () => dispatch(toggleEmailOverlayVisible()),
sendEmail: email => dispatch(sendEmail(email))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function SendEmail({
emailConfig,
modalVisible,
sendEmail,
toggleEmailOverlayVisible
}) {
const [messageOptions, setMessageOptions] = useState(
emailConfig.messageOptions
);
useEffect(() => {
setMessageOptions(emailConfig.messageOptions);
}, [setMessageOptions, emailConfig.messageOptions]);
const [executeQuery, { called, loading, data }] = useLazyQuery(
emailConfig.queryConfig[0],
{
...emailConfig.queryConfig[1],
fetchPolicy: "network-only"
}
);
if (
emailConfig.queryConfig[0] &&
emailConfig.queryConfig[1] &&
modalVisible &&
!called
) {
executeQuery();
}
if (data && !messageOptions.html && emailConfig.template) {
//console.log(ReactDOMServer.renderToStaticMarkup(<Template data={data} />));
setMessageOptions({
...messageOptions,
//html: ReactDOMServer.renderToStaticMarkup(<Template data={data} />)
html: renderEmail(<emailConfig.template data={data} />)
});
}
const handleOk = () => {
sendEmail("Clicked OK");
toggleEmailOverlayVisible();
};
const handleConfigChange = event => {
const { name, value } = event.target;
setMessageOptions({ ...messageOptions, [name]: value });
};
const handleHtmlChange = text => {
setMessageOptions({ ...messageOptions, html: text });
};
return (
<div>
<Modal
destroyOnClose={true}
visible={modalVisible}
width={"80%"}
onOk={handleOk}
onCancel={() => toggleEmailOverlayVisible()}>
<LoadingSpinner loading={loading}>
<EmailOverlayComponent
handleConfigChange={handleConfigChange}
messageOptions={messageOptions}
handleHtmlChange={handleHtmlChange}
/>
</LoadingSpinner>
</Modal>
<Button onClick={() => toggleEmailOverlayVisible()}>Show</Button>
</div>
);
});

View File

@@ -1,72 +0,0 @@
import { Button, Modal } from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useQuery } from "react-apollo";
//Message options has the to & from details
//Query Config is what can get popped into UseQuery(QUERY_NAME, {variables: {vars}, fetchonly})
//Template Which template should be used to send the email.
import ReactDOMServer from "react-dom/server";
import { renderEmail } from "react-html-email";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import SendEmailButtonComponent from "./send-email-button.component";
export default function SendEmail({
MessageOptions,
QueryConfig,
Template,
...otherProps
}) {
const [modalVisible, setModalVisible] = useState(false);
const [emailConfig, setEmailConfig] = useState({ ...MessageOptions });
const [gqlQuery, vars] = QueryConfig;
const { loading, data } = useQuery(gqlQuery, {
...vars,
fetchPolicy: "network-only",
skip: !modalVisible
});
if (data && !emailConfig.html) {
console.log(ReactDOMServer.renderToStaticMarkup(<Template data={data} />));
setEmailConfig({
...emailConfig,
//html: ReactDOMServer.renderToStaticMarkup(<Template data={data} />)
html: renderEmail(<Template data={data} />)
});
}
const handleConfigChange = event => {
const { name, value } = event.target;
setEmailConfig({ ...emailConfig, [name]: value });
};
const handleHtmlChange = text => {
setEmailConfig({ ...emailConfig, html: text });
};
const sendEmail = () => {
axios.post("/sendemail", emailConfig).then(response => {
alert(JSON.stringify(response));
});
};
return (
<div>
<Modal
destroyOnClose={true}
visible={modalVisible}
width={"80%"}
onOk={sendEmail}
onCancel={() => setModalVisible(false)}>
<LoadingSpinner loading={loading}>
<SendEmailButtonComponent
handleConfigChange={handleConfigChange}
emailConfig={emailConfig}
handleHtmlChange={handleHtmlChange}
/>
</LoadingSpinner>
</Modal>
<Button onClick={() => setModalVisible(true)}>
{otherProps.children}
</Button>
</div>
);
}

View File

@@ -1,29 +1,28 @@
import React from "react";
import SendEmailButton from "../../components/send-email-button/send-email-button.container";
import PartsOrderEmail from "../../emails/parts-order/parts-order.email";
import { REPORT_QUERY_PARTS_ORDER_BY_PK } from "../../emails/parts-order/parts-order.query";
export default function ManageRootPageComponent() {
//const client = useApolloClient();
return (
<div>
<SendEmailButton
MessageOptions={{
from: {
name: "Kavia"
},
to: "patrickwf@gmail.com",
replyTo: "patrickwf@gmail.com"
}}
Template={PartsOrderEmail}
QueryConfig={[
REPORT_QUERY_PARTS_ORDER_BY_PK,
{
variables: { id: "ebe0fb6b-6ec4-4ae0-8fdc-49bdf1e37ff3" }
}
]}>
Send an Email in new Window
</SendEmailButton>
{
// <SendEmailButton
// MessageOptions={{
// from: {
// name: "Kavia"
// },
// to: "patrickwf@gmail.com",
// replyTo: "patrickwf@gmail.com"
// }}
// Template={PartsOrderEmail}
// QueryConfig={[
// REPORT_QUERY_PARTS_ORDER_BY_PK,
// {
// variables: { id: "ebe0fb6b-6ec4-4ae0-8fdc-49bdf1e37ff3" }
// }
// ]}>
// Send an Email in new Window
// </SendEmailButton>
}
</div>
);
}

View File

@@ -8,6 +8,7 @@ import FooterComponent from "../../components/footer/footer.component";
import HeaderContainer from "../../components/header/header.container";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import "./manage.page.styles.scss";
import Test from "../../components/_test/test.component";
const ManageRootPage = lazy(() =>
import("../manage-root/manage-root.page.container")
@@ -44,6 +45,10 @@ const ShopVendorPageContainer = lazy(() =>
import("../shop-vendor/shop-vendor.page.container")
);
const EmailOverlayContainer = lazy(() =>
import("../../components/email-overlay/email-overlay.container.jsx")
);
const { Header, Content, Footer } = Layout;
export default function Manage({ match }) {
@@ -67,10 +72,14 @@ export default function Manage({ match }) {
fallback={
<LoadingSpinner message={t("general.labels.loadingapp")} />
}>
<div>
DELETE THESE
<Test />
<EmailOverlayContainer />
</div>
<Route exact path={`${match.path}`} component={ManageRootPage} />
<Route exact path={`${match.path}/jobs`} component={JobsPage} />
<Route
exact
path={`${match.path}/jobs/:jobId`}
@@ -111,13 +120,11 @@ export default function Manage({ match }) {
path={`${match.path}/schedule`}
component={ScheduleContainer}
/>
<Route
exact
path={`${match.path}/available`}
component={JobsAvailablePage}
/>
<Route exact path={`${match.path}/shop/`} component={ShopPage} />
<Route
exact

View File

@@ -0,0 +1,24 @@
import EmailActionTypes from "./email.types";
export const toggleEmailOverlayVisible = () => ({
type: EmailActionTypes.TOGGLE_EMAIL_OVERLAY_VISIBLE
});
export const setEmailOptions = options => ({
type: EmailActionTypes.SET_EMAIL_OPTIONS,
payload: options
});
export const sendEmail = email => ({
type: EmailActionTypes.SEND_EMAIL,
payload: email
});
export const sendEmailSuccess = options => ({
type: EmailActionTypes.SEND_EMAIL_SUCCESS
});
export const sendEmailFailure = error => ({
type: EmailActionTypes.SEND_EMAIL_FAILURE,
payload: error
});

View File

@@ -0,0 +1,35 @@
import EmailActionTypes from "./email.types";
const INITIAL_STATE = {
emailConfig: {
messageOptions: {
from: { name: "ShopName", address: "noreply@bodyshop.app" },
to: null,
replyTo: null
},
template: null,
queryConfig: [null, { variables: null }]
},
visible: false,
error: null
};
const emailReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case EmailActionTypes.TOGGLE_EMAIL_OVERLAY_VISIBLE:
return {
...state,
visible: !state.visible
};
case EmailActionTypes.SET_EMAIL_OPTIONS:
return {
...state,
emailConfig: { ...action.payload }
};
default:
return state;
}
};
export default emailReducer;

View File

@@ -0,0 +1,50 @@
import { all, call, put, takeLatest } from "redux-saga/effects";
import { sendEmailFailure, sendEmailSuccess } from "./email.actions";
import EmailActionTypes from "./email.types";
export function* onSendEmail() {
yield takeLatest(EmailActionTypes.SEND_EMAIL, sendEmail);
}
export function* sendEmail(payload) {
try {
console.log("Sending thta email", payload);
// // axios.post("/sendemail", emailConfig).then(response => {
// alert(JSON.stringify(response));
// });
yield put(sendEmailSuccess());
} catch (error) {
console.log("Error in sendEmail saga.");
yield put(sendEmailFailure(error.message));
}
}
export function* onSendEmailSuccess() {
yield takeLatest(EmailActionTypes.SEND_EMAIL_SUCCESS, sendEmailSuccessSaga);
}
export function* sendEmailSuccessSaga() {
try {
console.log("Send email success.");
} catch (error) {
console.log("Error in sendEmailSuccess saga.");
yield put(sendEmailFailure(error.message));
}
}
export function* onSendEmailFailure() {
yield takeLatest(EmailActionTypes.SEND_EMAIL_FAILURE, sendEmailFailureSaga);
}
export function* sendEmailFailureSaga(error) {
try {
yield console.log(error);
} catch (error) {
console.log("Error in sendEmailFailure saga.", error.message);
}
}
export function* emailSagas() {
yield all([
call(onSendEmail),
call(onSendEmailFailure),
call(onSendEmailSuccess)
]);
}

View File

@@ -0,0 +1,25 @@
import { createSelector } from "reselect";
const selectEmail = state => state.email;
const selectEmailConfigMessageOptions = state =>
state.email.emailConfig.messageOptions;
const selectEmailConfigTemplate = state => state.email.emailConfig.template;
const selectEmailConfigQuery = state => state.email.emailConfig.queryConfig;
export const selectEmailVisible = createSelector(
[selectEmail],
email => email.visible
);
export const selectEmailConfig = createSelector(
[
selectEmailConfigMessageOptions,
selectEmailConfigTemplate,
selectEmailConfigQuery
],
(messageOptions, template, queryConfig) => ({
messageOptions,
template,
queryConfig
})
);

View File

@@ -0,0 +1,8 @@
const EmailActionTypes = {
TOGGLE_EMAIL_OVERLAY_VISIBLE: "TOGGLE_EMAIL_OVERLAY_VISIBLE",
SET_EMAIL_OPTIONS: "SET_EMAIL_OPTIONS",
SEND_EMAIL: "SEND_EMAIL",
SEND_EMAIL_SUCCESS: "SEND_EMAIL_SUCCESS",
SEND_EMAIL_FAILURE: "SEND_EMAIL_FAILURE"
};
export default EmailActionTypes;

View File

@@ -4,23 +4,19 @@ import storage from "redux-persist/lib/storage";
import userReducer from "./user/user.reducer";
import messagingReducer from "./messaging/messaging.reducer";
// import cartReducer from './cart/cart.reducer';
// import directoryReducer from './directory/directory.reducer';
// import shopReducer from './shop/shop.reducer';
import emailReducer from "./email/email.reducer";
const persistConfig = {
key: "root",
storage,
//whitelist: ["cart"]
blacklist: ["user"]
//whitelist: ["user"]
blacklist: ["user", "email"]
};
const rootReducer = combineReducers({
user: userReducer,
messaging: messagingReducer
// cart: cartReducer,
// directory: directoryReducer,
// shop: shopReducer
messaging: messagingReducer,
email: emailReducer
});
export default persistReducer(persistConfig, rootReducer);

View File

@@ -1,12 +1,8 @@
import { all, call } from "redux-saga/effects";
//List of all Sagas
// import { shopSagas } from "./shop/shop.sagas";
import { userSagas } from "./user/user.sagas";
import { messagingSagas } from "./messaging/messaging.sagas";
//import { cartSagas } from "./cart/cart.sagas";
import { emailSagas } from "./email/email.sagas";
export default function* rootSaga() {
//All starts all the Sagas concurrently.
yield all([call(userSagas), call(messagingSagas)]);
yield all([call(userSagas), call(messagingSagas), call(emailSagas)]);
}