BOD-35 Refactored email screen to use TinyMCE + added templates schema + base level email generation
This commit is contained in:
@@ -4,10 +4,9 @@
|
||||
"private": true,
|
||||
"proxy": "https://localhost:5000",
|
||||
"dependencies": {
|
||||
"@ckeditor/ckeditor5-build-classic": "^18.0.0",
|
||||
"@ckeditor/ckeditor5-react": "^2.1.0",
|
||||
"@nivo/pie": "^0.61.1",
|
||||
"@tanem/react-nprogress": "^3.0.20",
|
||||
"@tinymce/tinymce-react": "^3.5.0",
|
||||
"aamva": "^1.2.0",
|
||||
"antd": "^4.1.0",
|
||||
"apollo-boost": "^0.4.4",
|
||||
@@ -19,7 +18,6 @@
|
||||
"axios": "^0.19.2",
|
||||
"dotenv": "^8.2.0",
|
||||
"firebase": "^7.13.1",
|
||||
"grapesjs-react": "^3.0.4",
|
||||
"graphql": "^14.6.0",
|
||||
"i18next": "^19.3.4",
|
||||
"node-sass": "^4.13.1",
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { Editor } from "@tinymce/tinymce-react";
|
||||
import { Col, Row } from "antd";
|
||||
import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { EmailSettings } from "../../emails/constants";
|
||||
import {
|
||||
startLoading,
|
||||
endLoading,
|
||||
startLoading,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||
import T, {
|
||||
Subject,
|
||||
} from "../../emails/templates/appointment-confirmation/appointment-confirmation.template";
|
||||
import { EMAIL_APPOINTMENT_CONFIRMATION } from "../../emails/templates/appointment-confirmation/appointment-confirmation.query";
|
||||
import axios from "axios";
|
||||
import CKEditor from "@ckeditor/ckeditor5-react";
|
||||
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
|
||||
import { Editor } from "grapesjs-react";
|
||||
import "grapesjs/dist/css/grapes.min.css";
|
||||
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
@@ -27,551 +23,133 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(function Test({ setEmailOptions, load, endload }) {
|
||||
const editorRef = useRef(null);
|
||||
const [state, setState] = useState("");
|
||||
)(function Test({ setEmailOptions, load, endload, bodyshop }) {
|
||||
const [state, setState] = useState(temp);
|
||||
|
||||
const handleEditorChange = (content, editor) => {
|
||||
setState(content);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<button
|
||||
onClick={() => {
|
||||
axios
|
||||
.post("/render", {
|
||||
view: state,
|
||||
context: {
|
||||
people: ["Yehuda Katz", "Alan Johnson", "Charles Jolley"],
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
var newWin = window.open(
|
||||
"url",
|
||||
"windowName",
|
||||
"height=300,width=300"
|
||||
);
|
||||
newWin.document.write(r.data);
|
||||
});
|
||||
}}
|
||||
>
|
||||
TinyMCE
|
||||
</button>
|
||||
<Editor
|
||||
value={state}
|
||||
apiKey="f3s2mjsd77ya5qvqkee9vgh612cm6h41e85efqakn2d0kknk"
|
||||
init={{
|
||||
height: 500,
|
||||
//menubar: false,
|
||||
encoding: "raw",
|
||||
extended_valid_elements: "span",
|
||||
|
||||
plugins: [
|
||||
"advlist autolink lists link image charmap print preview anchor",
|
||||
"searchreplace visualblocks code fullscreen",
|
||||
"insertdatetime media table paste code help wordcount",
|
||||
],
|
||||
toolbar:
|
||||
"undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help",
|
||||
}}
|
||||
onEditorChange={handleEditorChange}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
axios
|
||||
.post("/render", {
|
||||
view: testView2,
|
||||
context: {
|
||||
people: ["Yehuda Katz", "Alan Johnson", "Charles Jolley"],
|
||||
onClick={() =>
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
from: {
|
||||
name: bodyshop.shopname || EmailSettings.fromNameDefault,
|
||||
address: EmailSettings.fromAddress,
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
var newWin = window.open(
|
||||
"url",
|
||||
"windowName",
|
||||
"height=300,width=300"
|
||||
);
|
||||
newWin.document.write(r.data);
|
||||
});
|
||||
}}>
|
||||
Test render for mustache
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
axios
|
||||
.post("/render", {
|
||||
view: state,
|
||||
context: {
|
||||
people: ["Yehuda Katz", "Alan Johnson", "Charles Jolley"],
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
var newWin = window.open(
|
||||
"url",
|
||||
"windowName",
|
||||
"height=300,width=300"
|
||||
);
|
||||
newWin.document.write(r.data);
|
||||
});
|
||||
}}>
|
||||
Test render for mustache using state
|
||||
to: "patrickwf@gmail.com",
|
||||
replyTo: bodyshop.email,
|
||||
Subject: "TODO FIX ME",
|
||||
},
|
||||
template: {
|
||||
name: "appointment_reminder",
|
||||
variables: { id: "2b42336f-b8de-4f04-a053-d6bff034d384" },
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
Set email config.
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
from: { name: "Kavia Autobody", address: "noreply@bodyshop.app" },
|
||||
from: {
|
||||
name: bodyshop.shopname || EmailSettings.fromNameDefault,
|
||||
address: EmailSettings.fromAddress,
|
||||
},
|
||||
to: "patrickwf@gmail.com",
|
||||
replyTo: "snaptsoft@gmail.com",
|
||||
subject: Subject,
|
||||
replyTo: bodyshop.email,
|
||||
Subject: "TODO FIX ME",
|
||||
},
|
||||
template: {
|
||||
name: "appointment_reminder2222222",
|
||||
variables: { id: "2b42336f-b8de-4f04-a053-d6bff034d384" },
|
||||
},
|
||||
template: T,
|
||||
queryConfig: [
|
||||
EMAIL_APPOINTMENT_CONFIRMATION,
|
||||
{ variables: { id: "91bb31dd-ea87-4cfc-bbe2-2ec754dcb861" } },
|
||||
],
|
||||
})
|
||||
}>
|
||||
Set email config.
|
||||
}
|
||||
>
|
||||
Set email config. 222222
|
||||
</button>
|
||||
<CKEditor
|
||||
editor={ClassicEditor}
|
||||
data={view}
|
||||
onChange={(event, editor) => {
|
||||
// handleHtmlChange(editor.getData());
|
||||
//TODO Ensure that removing onchange never introduces a race condition
|
||||
}}
|
||||
onBlur={(event, editor) => {
|
||||
setState(editor.getData());
|
||||
}}
|
||||
/>
|
||||
<Editor
|
||||
blockManager={[
|
||||
{
|
||||
id: "section", // id is mandatory
|
||||
label: "<b>Section</b>", // You can use HTML/SVG inside labels
|
||||
attributes: { class: "gjs-block-section" },
|
||||
content: `<section>
|
||||
<h1>This is a simple title</h1>
|
||||
<div>This is just a Lorem text: Lorem ipsum dolor sit amet</div>
|
||||
</section>`,
|
||||
},
|
||||
{
|
||||
id: "text2",
|
||||
label: "Text2",
|
||||
content:
|
||||
'<div data-gjs-type="text">Insert your text here222222222</div>',
|
||||
},
|
||||
{
|
||||
id: "image",
|
||||
label: "Image",
|
||||
// Select the component once it's dropped
|
||||
select: true,
|
||||
// You can pass components as a JSON instead of a simple HTML string,
|
||||
// in this case we also use a defined component type `image`
|
||||
content: { type: "image" },
|
||||
// This triggers `active` event on dropped components and the `image`
|
||||
// reacts by opening the AssetManager
|
||||
activate: true,
|
||||
},
|
||||
]}
|
||||
onInit={(editor) => {
|
||||
console.log("editor", editor);
|
||||
editor.setComponents(view);
|
||||
}}
|
||||
storageManager={false}
|
||||
ref={editorRef}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const testView2 = `<!DOCTYPE html>
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Landing Page - Start Bootstrap Theme</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom fonts for this template -->
|
||||
<link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet">
|
||||
<link href="vendor/simple-line-icons/css/simple-line-icons.css" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="css/landing-page.min.css" rel="stylesheet">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-light bg-light static-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="#">Start Bootstrap</a>
|
||||
<a class="btn btn-primary" href="#">Sign In</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Masthead -->
|
||||
<header class="masthead text-white text-center">
|
||||
<div class="overlay"></div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xl-9 mx-auto">
|
||||
<h1 class="mb-5">Build a landing page for your business or project and generate more leads!</h1>
|
||||
</div>
|
||||
<div class="col-md-10 col-lg-8 col-xl-7 mx-auto">
|
||||
<form>
|
||||
<div class="form-row">
|
||||
<div class="col-12 col-md-9 mb-2 mb-md-0">
|
||||
<input type="email" class="form-control form-control-lg" placeholder="Enter your email...">
|
||||
</div>
|
||||
<div class="col-12 col-md-3">
|
||||
<button type="submit" class="btn btn-block btn-lg btn-primary">Sign up!</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Icons Grid -->
|
||||
<section class="features-icons bg-light text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="features-icons-item mx-auto mb-5 mb-lg-0 mb-lg-3">
|
||||
<div class="features-icons-icon d-flex">
|
||||
<i class="icon-screen-desktop m-auto text-primary"></i>
|
||||
</div>
|
||||
<h3>Fully Responsive</h3>
|
||||
<p class="lead mb-0">This theme will look great on any device, no matter the size!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="features-icons-item mx-auto mb-5 mb-lg-0 mb-lg-3">
|
||||
<div class="features-icons-icon d-flex">
|
||||
<i class="icon-layers m-auto text-primary"></i>
|
||||
</div>
|
||||
<h3>Bootstrap 4 Ready</h3>
|
||||
<p class="lead mb-0">Featuring the latest build of the new Bootstrap 4 framework!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4" data-vvveb-disabled="">
|
||||
<div class="features-icons-item mx-auto mb-0 mb-lg-3">
|
||||
<div class="features-icons-icon d-flex">
|
||||
<i class="icon-check m-auto text-primary"></i>
|
||||
</div>
|
||||
<h3>Easy to Use</h3>
|
||||
<p class="lead mb-0">Ready to use with your own content, or customize the source files!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Image Showcases -->
|
||||
<section class="showcase">
|
||||
<div class="container-fluid p-0">
|
||||
<div class="row no-gutters">
|
||||
|
||||
<div class="col-lg-6 order-lg-2 text-white showcase-img" style="background-image: url('img/bg-showcase-1.jpg');"></div>
|
||||
<div class="col-lg-6 order-lg-1 my-auto showcase-text">
|
||||
<h2>Fully Responsive Design</h2>
|
||||
<p class="lead mb-0">When you use a theme created by Start Bootstrap, you know that the theme will look great on any device, whether it's a phone, tablet, or desktop the page will behave responsively!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-gutters">
|
||||
<div class="col-lg-6 text-white showcase-img" style="background-image: url('img/bg-showcase-2.jpg');"></div>
|
||||
<div class="col-lg-6 my-auto showcase-text">
|
||||
<h2>Updated For Bootstrap 4</h2>
|
||||
<p class="lead mb-0">Newly improved, and full of great utility classes, Bootstrap 4 is leading the way in mobile responsive web development! All of the themes on Start Bootstrap are now using Bootstrap 4!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-gutters">
|
||||
<div class="col-lg-6 order-lg-2 text-white showcase-img" style="background-image: url('img/bg-showcase-3.jpg');"></div>
|
||||
<div class="col-lg-6 order-lg-1 my-auto showcase-text">
|
||||
<h2>Easy to Use & Customize</h2>
|
||||
<p class="lead mb-0">Landing Page is just HTML and CSS with a splash of SCSS for users who demand some deeper customization options. Out of the box, just add your content and images, and your new landing page will be ready to go!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Testimonials -->
|
||||
<section class="testimonials text-center bg-light">
|
||||
<div class="container">
|
||||
<h2 class="mb-5">What people are saying...</h2>
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="testimonial-item mx-auto mb-5 mb-lg-0">
|
||||
<img class="img-fluid rounded-circle mb-3" src="img/testimonials-1.jpg" alt="">
|
||||
<h5>Margaret E.</h5>
|
||||
<p class="font-weight-light mb-0">"This is fantastic! Thanks so much guys!"</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="testimonial-item mx-auto mb-5 mb-lg-0">
|
||||
<img class="img-fluid rounded-circle mb-3" src="img/testimonials-2.jpg" alt="">
|
||||
<h5>Fred S.</h5>
|
||||
<p class="font-weight-light mb-0">"Bootstrap is amazing. I've been using it to create lots of super nice landing pages."</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4" data-vvveb-disabled="">
|
||||
<div class="testimonial-item mx-auto mb-5 mb-lg-0">
|
||||
<img class="img-fluid rounded-circle mb-3" src="img/testimonials-3.jpg" alt="">
|
||||
<h5>Sarah W.</h5>
|
||||
<p class="font-weight-light mb-0">"Thanks so much for making these free resources available to us!"</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Call to Action -->
|
||||
<section class="call-to-action text-white text-center">
|
||||
<div class="overlay"></div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xl-9 mx-auto">
|
||||
<h2 class="mb-4">Ready to get started? Sign up now!</h2>
|
||||
</div>
|
||||
<div class="col-md-10 col-lg-8 col-xl-7 mx-auto">
|
||||
<form>
|
||||
<div class="form-row">
|
||||
<div class="col-12 col-md-9 mb-2 mb-md-0">
|
||||
<input type="email" class="form-control form-control-lg" placeholder="Enter your email...">
|
||||
</div>
|
||||
<div class="col-12 col-md-3">
|
||||
<button type="submit" class="btn btn-block btn-lg btn-primary">Sign up!</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer bg-light">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 h-100 text-center text-lg-left my-auto">
|
||||
<ul class="list-inline mb-2">
|
||||
<li class="list-inline-item">
|
||||
<a href="#">About</a>
|
||||
</li>
|
||||
<li class="list-inline-item">⋅</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#">Contact</a>
|
||||
</li>
|
||||
<li class="list-inline-item">⋅</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#">Terms of Use</a>
|
||||
</li>
|
||||
<li class="list-inline-item">⋅</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#">Privacy Policy</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-muted small mb-4 mb-lg-0">© Your Website 2018. All Rights Reserved.</p>
|
||||
</div>
|
||||
<div class="col-lg-6 h-100 text-center text-lg-right my-auto">
|
||||
<ul class="list-inline mb-0">
|
||||
<li class="list-inline-item mr-3">
|
||||
<a href="#">
|
||||
<i class="fab fa-facebook fa-2x fa-fw"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item mr-3">
|
||||
<a href="#">
|
||||
<i class="fab fa-twitter-square fa-2x fa-fw"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#">
|
||||
<i class="fab fa-instagram fa-2x fa-fw"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap core JavaScript -->
|
||||
<script src="vendor/jquery/jquery.min.js"></script>
|
||||
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const view = `<table class="main-body" style="box-sizing: border-box; min-height: 150px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; width: 100%; height: 100%; background-color: rgb(234, 236, 237);" width="100%" height="100%" bgcolor="rgb(234, 236, 237)">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr class="row" style="box-sizing: border-box; vertical-align: top;" valign="top">
|
||||
<td class="main-body-cell" style="box-sizing: border-box;">
|
||||
<table class="container" style="box-sizing: border-box; font-family: Helvetica, serif; min-height: 150px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; margin-top: auto; margin-right: auto; margin-bottom: auto; margin-left: auto; height: 0px; width: 90%; max-width: 550px;" width="90%" height="0">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="container-cell" style="box-sizing: border-box; vertical-align: top; font-size: medium; padding-bottom: 50px;" valign="top">
|
||||
<table class="table100 c1790" style="box-sizing: border-box; width: 100%; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; height: 0px; min-height: 30px; border-collapse: separate; margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px;" width="100%" height="0">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td id="c1793" class="top-cell" style="box-sizing: border-box; text-align: right; color: rgb(152, 156, 165);" align="right">
|
||||
<u id="c307" class="browser-link" style="box-sizing: border-box; font-size: 12px;">View in browser
|
||||
</u>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="c1766" style="box-sizing: border-box; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; width: 100%; min-height: 30px;" width="100%">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="cell c1769" style="box-sizing: border-box; width: 11%;" width="11%">
|
||||
<img src="//artf.github.io/grapesjs/img/grapesjs-logo.png" alt="GrapesJS." class="c926" style="box-sizing: border-box; color: rgb(158, 83, 129); width: 100%; font-size: 50px;">
|
||||
</td>
|
||||
<td class="cell c1776" style="box-sizing: border-box; width: 70%; vertical-align: middle;" width="70%" valign="middle">
|
||||
<div class="c1144" style="box-sizing: border-box; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; font-size: 17px; font-weight: 300;">GrapesJS Newsletter Builder
|
||||
<br style="box-sizing: border-box;">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="card" style="box-sizing: border-box; min-height: 150px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; margin-bottom: 20px; height: 0px;" height="0">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="card-cell" style="box-sizing: border-box; background-color: rgb(255, 255, 255); overflow-x: hidden; overflow-y: hidden; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-align: center;" bgcolor="rgb(255, 255, 255)" align="center">
|
||||
<img src="//artf.github.io/grapesjs/img/tmp-header-txt.jpg" alt="Big image here" class="c1271" style="box-sizing: border-box; width: 100%; margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; font-size: 50px; color: rgb(120, 197, 214); line-height: 250px; text-align: center;">
|
||||
<table class="table100 c1357" style="box-sizing: border-box; width: 100%; min-height: 150px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; height: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-collapse: collapse;" width="100%" height="0">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="card-content" style="box-sizing: border-box; font-size: 13px; line-height: 20px; color: rgb(111, 119, 125); padding-top: 10px; padding-right: 20px; padding-bottom: 0px; padding-left: 20px; vertical-align: top;" valign="top">
|
||||
<h1 class="card-title" style="box-sizing: border-box; font-size: 25px; font-weight: 300; color: rgb(68, 68, 68);">Build your newsletters faster than ever
|
||||
<br style="box-sizing: border-box;">
|
||||
</h1>
|
||||
<p class="card-text" style="box-sizing: border-box;">Import, build, test and export responsive newsletter templates faster than ever using the GrapesJS Newsletter Builder.
|
||||
</p>
|
||||
<div id="i3mqog" style="box-sizing: border-box; padding: 10px;"> {{#each people}}
|
||||
<br style="box-sizing: border-box;">
|
||||
</div>
|
||||
<div id="izgz11" style="box-sizing: border-box; padding: 10px;"> {{this}}
|
||||
<br style="box-sizing: border-box;">
|
||||
</div>
|
||||
<a class="button" style="box-sizing: border-box; font-size: 12px; padding-top: 10px; padding-right: 20px; padding-bottom: 10px; padding-left: 20px; background-color: rgb(217, 131, 166); color: rgb(255, 255, 255); text-align: center; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; font-weight: 300;">Button</a>
|
||||
<div id="ic5llo" style="box-sizing: border-box; padding: 10px;"> {{/each}}
|
||||
</div>
|
||||
<table class="c1542" style="box-sizing: border-box; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; width: 100%;" width="100%">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td id="c1545" class="card-footer" style="box-sizing: border-box; padding-top: 20px; padding-right: 0px; padding-bottom: 20px; padding-left: 0px; text-align: center;" align="center">
|
||||
<a href="https://github.com/artf/grapesjs" class="button" style="box-sizing: border-box; font-size: 12px; padding-top: 10px; padding-right: 20px; padding-bottom: 10px; padding-left: 20px; background-color: rgb(217, 131, 166); color: rgb(255, 255, 255); text-align: center; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; font-weight: 300;">Free and Open Source
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="list-item" style="box-sizing: border-box; height: auto; width: 100%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px;" width="100%">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="list-item-cell" style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; overflow-x: hidden; overflow-y: hidden; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;" bgcolor="rgb(255, 255, 255)">
|
||||
<table class="list-item-content" style="box-sizing: border-box; border-collapse: collapse; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; height: 150px; width: 100%;" width="100%" height="150">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr class="list-item-row" style="box-sizing: border-box;">
|
||||
<td class="list-cell-left" style="box-sizing: border-box; width: 30%; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;" width="30%">
|
||||
<img src="//artf.github.io/grapesjs/img/tmp-blocks.jpg" alt="Image1" class="list-item-image" style="box-sizing: border-box; color: rgb(217, 131, 166); font-size: 45px; width: 100%;">
|
||||
</td>
|
||||
<td class="list-cell-right" style="box-sizing: border-box; width: 70%; color: rgb(111, 119, 125); font-size: 13px; line-height: 20px; padding-top: 10px; padding-right: 20px; padding-bottom: 0px; padding-left: 20px;" width="70%">
|
||||
<h1 class="card-title" style="box-sizing: border-box; font-size: 25px; font-weight: 300; color: rgb(68, 68, 68);">Built-in Blocks
|
||||
</h1>
|
||||
<p class="card-text" style="box-sizing: border-box;">Drag and drop built-in blocks from the right panel and style them in a matter of seconds
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="list-item" style="box-sizing: border-box; height: auto; width: 100%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px;" width="100%">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="list-item-cell" style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; overflow-x: hidden; overflow-y: hidden; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;" bgcolor="rgb(255, 255, 255)">
|
||||
<table class="list-item-content" style="box-sizing: border-box; border-collapse: collapse; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; height: 150px; width: 100%;" width="100%" height="150">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr class="list-item-row" style="box-sizing: border-box;">
|
||||
<td class="list-cell-left" style="box-sizing: border-box; width: 30%; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;" width="30%">
|
||||
<img src="//artf.github.io/grapesjs/img/tmp-tgl-images.jpg" alt="Image2" class="list-item-image" style="box-sizing: border-box; color: rgb(217, 131, 166); font-size: 45px; width: 100%;">
|
||||
</td>
|
||||
<td class="list-cell-right" style="box-sizing: border-box; width: 70%; color: rgb(111, 119, 125); font-size: 13px; line-height: 20px; padding-top: 10px; padding-right: 20px; padding-bottom: 0px; padding-left: 20px;" width="70%">
|
||||
<h1 class="card-title" style="box-sizing: border-box; font-size: 25px; font-weight: 300; color: rgb(68, 68, 68);">Toggle images
|
||||
</h1>
|
||||
<p class="card-text" style="box-sizing: border-box;">Build a good looking newsletter even without images enabled by the email clients
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="grid-item-row" style="box-sizing: border-box; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; padding-top: 5px; padding-right: 0px; padding-bottom: 5px; padding-left: 0px; width: 100%;" width="100%">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="grid-item-cell2-l" style="box-sizing: border-box; vertical-align: top; padding-right: 10px; width: 50%;" width="50%" valign="top">
|
||||
<table class="grid-item-card" style="box-sizing: border-box; width: 100%; padding-top: 5px; padding-right: 0px; padding-bottom: 5px; padding-left: 0px; margin-bottom: 10px;" width="100%">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="grid-item-card-cell" style="box-sizing: border-box; background-color: rgb(255, 255, 255); overflow-x: hidden; overflow-y: hidden; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; text-align: center; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;" bgcolor="rgb(255, 255, 255)" align="center">
|
||||
<img src="//artf.github.io/grapesjs/img/tmp-send-test.jpg" alt="Image1" class="grid-item-image" style="box-sizing: border-box; line-height: 150px; font-size: 50px; color: rgb(120, 197, 214); margin-bottom: 15px; width: 100%;">
|
||||
<table class="grid-item-card-body" style="box-sizing: border-box;">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="grid-item-card-content" style="box-sizing: border-box; font-size: 13px; color: rgb(111, 119, 125); padding-top: 0px; padding-right: 10px; padding-bottom: 20px; padding-left: 10px; width: 100%; line-height: 20px;" width="100%">
|
||||
<h1 class="card-title" style="box-sizing: border-box; font-size: 25px; font-weight: 300; color: rgb(68, 68, 68);">Test it
|
||||
</h1>
|
||||
<p class="card-text" style="box-sizing: border-box;">You can send email tests directly from the editor and check how are looking on your email clients
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td class="grid-item-cell2-r" style="box-sizing: border-box; vertical-align: top; padding-left: 10px; width: 50%;" width="50%" valign="top">
|
||||
<table class="grid-item-card" style="box-sizing: border-box; width: 100%; padding-top: 5px; padding-right: 0px; padding-bottom: 5px; padding-left: 0px; margin-bottom: 10px;" width="100%">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="grid-item-card-cell" style="box-sizing: border-box; background-color: rgb(255, 255, 255); overflow-x: hidden; overflow-y: hidden; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; text-align: center; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;" bgcolor="rgb(255, 255, 255)" align="center">
|
||||
<img src="//artf.github.io/grapesjs/img/tmp-devices.jpg" alt="Image2" class="grid-item-image" style="box-sizing: border-box; line-height: 150px; font-size: 50px; color: rgb(120, 197, 214); margin-bottom: 15px; width: 100%;">
|
||||
<table class="grid-item-card-body" style="box-sizing: border-box;">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="grid-item-card-content" style="box-sizing: border-box; font-size: 13px; color: rgb(111, 119, 125); padding-top: 0px; padding-right: 10px; padding-bottom: 20px; padding-left: 10px; width: 100%; line-height: 20px;" width="100%">
|
||||
<h1 class="card-title" style="box-sizing: border-box; font-size: 25px; font-weight: 300; color: rgb(68, 68, 68);">Responsive
|
||||
</h1>
|
||||
<p class="card-text" style="box-sizing: border-box;">Using the device manager you'll always send a fully responsive contents
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="footer" style="box-sizing: border-box; margin-top: 50px; color: rgb(152, 156, 165); text-align: center; font-size: 11px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px;" align="center">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="footer-cell" style="box-sizing: border-box;">
|
||||
<div class="c2577" style="box-sizing: border-box; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;">
|
||||
<p class="footer-info" style="box-sizing: border-box;">GrapesJS Newsletter Builder is a free and open source preset (plugin) used on top of the GrapesJS core library.
|
||||
For more information about and how to integrate it inside your applications check
|
||||
</p>
|
||||
<p style="box-sizing: border-box;">
|
||||
<a href="https://github.com/artf/grapesjs-preset-newsletter" class="link" style="box-sizing: border-box; color: rgb(217, 131, 166);">GrapesJS Newsletter Preset</a>
|
||||
<br style="box-sizing: border-box;">
|
||||
</p>
|
||||
</div>
|
||||
<div class="c2421" style="box-sizing: border-box; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;">
|
||||
MADE BY
|
||||
<a href="https://github.com/artf" class="link" style="box-sizing: border-box; color: rgb(217, 131, 166);">ARTUR ARSENIEV</a>
|
||||
<p style="box-sizing: border-box;">
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
const temp = `<div style="font-family: Arial, Helvetica, sans-serif;">
|
||||
<p style="text-align: center;"><span>→<span> This is a full-featured editor demo. </span>Please explore! ←</span></p>
|
||||
<p style="text-align: center;"> </p>
|
||||
<h2 style="text-align: center;"><span>TinyMCE is the world's most customizable, and flexible, rich text editor.</span></h2>
|
||||
<p style="text-align: center;"><span><strong> A featherweight download, TinyMCE can handle any challenge you throw at it. </strong></span></p>
|
||||
<p style="text-align: center;"> </p>
|
||||
<p> </p>
|
||||
<table style="border-collapse: collapse; width: 85%; height: 86px; border-color: initial; border-style: solid; margin-left: auto; margin-right: auto;">
|
||||
<tbody>
|
||||
<tr style="height: 22px;">
|
||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px;"><span>🛠 50+ Plugins</span></td>
|
||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px;"><span>💡 Premium Support</span></td>
|
||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px;"><span>🖍 Custom Skins</span></td>
|
||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px;"><span>⚙ Full API Access</span></td>
|
||||
</tr>
|
||||
<tr style="height: 21px; display: none;">
|
||||
<td style="height: 21px; display: none;"><span>{{#each people}}</span></td>
|
||||
</tr>
|
||||
<tr style="height: 22px;">
|
||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px; border-style: solid; border-width: 1px;"><span>{{this}}</span></td>
|
||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px; border-style: solid; border-width: 1px;"><span>{{this}}</span></td>
|
||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px; border-style: solid; border-width: 1px;"><span>{{this}}</span></td>
|
||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px; border-style: solid; border-width: 1px;"><span>{{this}}</span></td>
|
||||
</tr>
|
||||
<tr style="height: 21px; display: none;">
|
||||
<td style="height: 21px;"><span>{{/each}}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`;
|
||||
</table>
|
||||
</div>`;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from "react";
|
||||
import { Editor } from "@tinymce/tinymce-react";
|
||||
import { Input } from "antd";
|
||||
import CKEditor from "@ckeditor/ckeditor5-react";
|
||||
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
|
||||
|
||||
import React from "react";
|
||||
export default function EmailOverlayComponent({
|
||||
messageOptions,
|
||||
handleConfigChange,
|
||||
handleHtmlChange
|
||||
handleHtmlChange,
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
@@ -27,17 +25,24 @@ export default function EmailOverlayComponent({
|
||||
onChange={handleConfigChange}
|
||||
name="subject"
|
||||
/>
|
||||
<CKEditor
|
||||
editor={ClassicEditor}
|
||||
data={messageOptions.html}
|
||||
onChange={(event, editor) => {
|
||||
// handleHtmlChange(editor.getData());
|
||||
//TODO Ensure that removing onchange never introduces a race condition
|
||||
}}
|
||||
onBlur={(event, editor) => {
|
||||
console.log("Blur.");
|
||||
handleHtmlChange(editor.getData());
|
||||
<Editor
|
||||
value={messageOptions.html}
|
||||
apiKey="f3s2mjsd77ya5qvqkee9vgh612cm6h41e85efqakn2d0kknk"
|
||||
init={{
|
||||
height: 500,
|
||||
//menubar: false,
|
||||
encoding: "raw",
|
||||
extended_valid_elements: "span",
|
||||
//entity_encoding: "raw",
|
||||
plugins: [
|
||||
"advlist autolink lists link image charmap print preview anchor",
|
||||
"searchreplace visualblocks code fullscreen",
|
||||
"insertdatetime media table paste code help wordcount",
|
||||
],
|
||||
toolbar:
|
||||
"undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help",
|
||||
}}
|
||||
onEditorChange={handleHtmlChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,104 +1,127 @@
|
||||
import { useApolloClient } from "@apollo/react-hooks";
|
||||
import { Modal, notification } from "antd";
|
||||
import { gql } from "apollo-boost";
|
||||
import axios from "axios";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useLazyQuery } from "@apollo/react-hooks";
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_TEMPLATES_BY_NAME } from "../../graphql/templates.queries";
|
||||
import { toggleEmailOverlayVisible } from "../../redux/email/email.actions";
|
||||
import {
|
||||
selectEmailConfig,
|
||||
selectEmailVisible
|
||||
selectEmailVisible,
|
||||
} from "../../redux/email/email.selectors.js";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import EmailOverlayComponent from "./email-overlay.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
modalVisible: selectEmailVisible,
|
||||
emailConfig: selectEmailConfig
|
||||
emailConfig: selectEmailConfig,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
toggleEmailOverlayVisible: () => dispatch(toggleEmailOverlayVisible())
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleEmailOverlayVisible: () => dispatch(toggleEmailOverlayVisible()),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(function EmailOverlayContainer({
|
||||
export function EmailOverlayContainer({
|
||||
emailConfig,
|
||||
modalVisible,
|
||||
toggleEmailOverlayVisible
|
||||
toggleEmailOverlayVisible,
|
||||
bodyshop,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [messageOptions, setMessageOptions] = useState(
|
||||
emailConfig.messageOptions
|
||||
);
|
||||
const client = useApolloClient();
|
||||
|
||||
useEffect(() => {
|
||||
setMessageOptions(emailConfig.messageOptions);
|
||||
}, [setMessageOptions, emailConfig.messageOptions]);
|
||||
const renderEmail = () => {
|
||||
client
|
||||
.query({
|
||||
query: QUERY_TEMPLATES_BY_NAME,
|
||||
variables: { name: emailConfig.template.name },
|
||||
fetchPolicy: "network-only",
|
||||
})
|
||||
.then(({ data: templateRecords }) => {
|
||||
let templateToUse;
|
||||
if (templateRecords.templates.length === 1) {
|
||||
console.log("Only 1 Template found.");
|
||||
templateToUse = templateRecords.templates[0];
|
||||
} else if (templateRecords.templates.length === 2) {
|
||||
console.log("2 Templates found..");
|
||||
templateToUse = templateRecords.templates.filter(
|
||||
(t) => !!t.bodyshopid
|
||||
);
|
||||
} else {
|
||||
//No template found.Uh oh.
|
||||
alert("Templating Error!");
|
||||
}
|
||||
|
||||
const [executeQuery, { called, loading, data }] = useLazyQuery(
|
||||
emailConfig.queryConfig[0],
|
||||
{
|
||||
...emailConfig.queryConfig[1],
|
||||
fetchPolicy: "network-only"
|
||||
}
|
||||
);
|
||||
client
|
||||
.query({
|
||||
query: gql(templateToUse.query),
|
||||
variables: { ...emailConfig.template.variables },
|
||||
fetchPolicy: "network-only",
|
||||
})
|
||||
.then(({ data: contextData }) => {
|
||||
handleRender(contextData, templateToUse.html);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (
|
||||
emailConfig.queryConfig[0] &&
|
||||
emailConfig.queryConfig[1] &&
|
||||
modalVisible &&
|
||||
!called
|
||||
) {
|
||||
executeQuery();
|
||||
}
|
||||
|
||||
if (data && !messageOptions.html && emailConfig.template) {
|
||||
setMessageOptions({
|
||||
...messageOptions,
|
||||
html: ReactDOMServer.renderToStaticMarkup(
|
||||
<emailConfig.template data={data} />
|
||||
)
|
||||
});
|
||||
}
|
||||
const handleRender = (contextData, html) => {
|
||||
axios
|
||||
.post("/render", {
|
||||
view: html,
|
||||
context: { ...contextData, bodyshop: bodyshop },
|
||||
})
|
||||
.then((r) => {
|
||||
setMessageOptions({ ...messageOptions, html: r.data });
|
||||
});
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
//sendEmail(messageOptions);
|
||||
axios
|
||||
.post("/sendemail", messageOptions)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
console.log(JSON.stringify(response));
|
||||
notification["success"]({ message: t("emails.successes.sent") });
|
||||
toggleEmailOverlayVisible();
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.log(JSON.stringify(error));
|
||||
notification["error"]({
|
||||
message: t("emails.errors.notsent", { message: error.message })
|
||||
message: t("emails.errors.notsent", { message: error.message }),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfigChange = event => {
|
||||
const handleConfigChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setMessageOptions({ ...messageOptions, [name]: value });
|
||||
};
|
||||
const handleHtmlChange = text => {
|
||||
const handleHtmlChange = (text) => {
|
||||
setMessageOptions({ ...messageOptions, html: text });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (modalVisible) renderEmail();
|
||||
}, [modalVisible]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose={true}
|
||||
visible={modalVisible}
|
||||
width={"80%"}
|
||||
onOk={handleOk}
|
||||
onCancel={() => toggleEmailOverlayVisible()}
|
||||
onCancel={() => {
|
||||
toggleEmailOverlayVisible();
|
||||
}}
|
||||
>
|
||||
<LoadingSpinner loading={loading}>
|
||||
<LoadingSpinner loading={false}>
|
||||
<EmailOverlayComponent
|
||||
handleConfigChange={handleConfigChange}
|
||||
messageOptions={messageOptions}
|
||||
@@ -115,4 +138,8 @@ export default connect(
|
||||
</LoadingSpinner>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(EmailOverlayContainer);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectPartsOrder } from "../../redux/modals/modals.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
@@ -24,12 +24,12 @@ import { EmailSettings } from "../../emails/constants";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
bodyshop: selectBodyshop,
|
||||
partsOrderModal: selectPartsOrder
|
||||
partsOrderModal: selectPartsOrder,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setEmailOptions: e => dispatch(setEmailOptions(e)),
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder"))
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
|
||||
});
|
||||
|
||||
export function PartsOrderModalContainer({
|
||||
@@ -37,7 +37,7 @@ export function PartsOrderModalContainer({
|
||||
toggleModalVisible,
|
||||
currentUser,
|
||||
bodyshop,
|
||||
setEmailOptions
|
||||
setEmailOptions,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -50,12 +50,12 @@ export function PartsOrderModalContainer({
|
||||
const sendType = sendTypeState[0];
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, {
|
||||
skip: !visible
|
||||
skip: !visible,
|
||||
});
|
||||
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
|
||||
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
||||
|
||||
const handleFinish = values => {
|
||||
const handleFinish = (values) => {
|
||||
insertPartOrder({
|
||||
variables: {
|
||||
po: [
|
||||
@@ -63,21 +63,21 @@ export function PartsOrderModalContainer({
|
||||
...values,
|
||||
jobid: jobId,
|
||||
user_email: currentUser.email,
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*"
|
||||
}
|
||||
]
|
||||
}
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.then(r => {
|
||||
.then((r) => {
|
||||
updateJobLines({
|
||||
variables: {
|
||||
ids: values.parts_order_lines.data.map(item => item.job_line_id),
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*"
|
||||
}
|
||||
ids: values.parts_order_lines.data.map((item) => item.job_line_id),
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
notification["success"]({
|
||||
message: t("parts_orders.successes.created")
|
||||
message: t("parts_orders.successes.created"),
|
||||
});
|
||||
if (refetch) refetch();
|
||||
toggleModalVisible();
|
||||
@@ -88,38 +88,38 @@ export function PartsOrderModalContainer({
|
||||
messageOptions: {
|
||||
from: {
|
||||
name: bodyshop.shopname || EmailSettings.fromNameDefault,
|
||||
address: EmailSettings.fromAddress
|
||||
address: EmailSettings.fromAddress,
|
||||
},
|
||||
to:
|
||||
data.vendors.filter(item => item.id === values.id)[0] ||
|
||||
data.vendors.filter((item) => item.id === values.id)[0] ||
|
||||
null,
|
||||
replyTo: bodyshop.shopname || null
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: PartsOrderEmailTemplate,
|
||||
queryConfig: [
|
||||
REPORT_QUERY_PARTS_ORDER_BY_PK,
|
||||
{
|
||||
variables: {
|
||||
id: r.data.insert_parts_orders.returning[0].id
|
||||
}
|
||||
}
|
||||
]
|
||||
id: r.data.insert_parts_orders.returning[0].id,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
notification["error"]({
|
||||
message: t("parts_orders.errors.creating"),
|
||||
description: error.message
|
||||
description: error.message,
|
||||
});
|
||||
});
|
||||
|
||||
//end no good
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
notification["error"]({
|
||||
message: t("parts_orders.errors.creating"),
|
||||
description: error.message
|
||||
description: error.message,
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -133,7 +133,7 @@ export function PartsOrderModalContainer({
|
||||
db_price: value.db_price,
|
||||
act_price: value.act_price,
|
||||
job_line_id: value.id,
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*"
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||
});
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
@@ -3,7 +3,7 @@ import ScheduleJobModalComponent from "./schedule-job-modal.component";
|
||||
import { useMutation, useQuery } from "@apollo/react-hooks";
|
||||
import {
|
||||
INSERT_APPOINTMENT,
|
||||
QUERY_APPOINTMENTS_BY_JOBID
|
||||
QUERY_APPOINTMENTS_BY_JOBID,
|
||||
} from "../../graphql/appointments.queries";
|
||||
import moment from "moment";
|
||||
import { notification, Modal } from "antd";
|
||||
@@ -18,31 +18,29 @@ import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
scheduleModal: selectSchedule
|
||||
scheduleModal: selectSchedule,
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("schedule"))
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
|
||||
});
|
||||
export function ScheduleJobModalContainer({
|
||||
scheduleModal,
|
||||
bodyshop,
|
||||
toggleModalVisible
|
||||
toggleModalVisible,
|
||||
}) {
|
||||
const { visible, context, actions } = scheduleModal;
|
||||
const { jobId } = context;
|
||||
const { refetch } = actions;
|
||||
|
||||
const [appData, setAppData] = useState({
|
||||
jobid: jobId,
|
||||
start: null,
|
||||
bodyshopid: bodyshop.id
|
||||
});
|
||||
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
|
||||
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS, {
|
||||
variables: {
|
||||
jobId: jobId,
|
||||
status: bodyshop.md_ro_statuses.default_scheduled
|
||||
}
|
||||
status: bodyshop.md_ro_statuses.default_scheduled,
|
||||
},
|
||||
});
|
||||
const [formData, setFormData] = useState({ notifyCustomer: false });
|
||||
const { t } = useTranslation();
|
||||
@@ -50,20 +48,25 @@ export function ScheduleJobModalContainer({
|
||||
const existingAppointments = useQuery(QUERY_APPOINTMENTS_BY_JOBID, {
|
||||
variables: { jobid: jobId },
|
||||
fetchPolicy: "network-only",
|
||||
skip: !visible
|
||||
skip: !visible,
|
||||
});
|
||||
|
||||
//TODO Customize the amount of minutes it will add.
|
||||
const handleOk = () => {
|
||||
insertAppointment({
|
||||
variables: {
|
||||
app: { ...appData, end: moment(appData.start).add(60, "minutes") }
|
||||
}
|
||||
app: {
|
||||
...appData,
|
||||
jobid: jobId,
|
||||
bodyshopid: bodyshop.id,
|
||||
end: moment(appData.start).add(60, "minutes"),
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(r => {
|
||||
updateJobStatus().then(r => {
|
||||
.then((r) => {
|
||||
updateJobStatus().then((r) => {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.created")
|
||||
message: t("appointments.successes.created"),
|
||||
});
|
||||
|
||||
if (formData.notifyCustomer) {
|
||||
@@ -74,11 +77,11 @@ export function ScheduleJobModalContainer({
|
||||
if (refetch) refetch();
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
notification["error"]({
|
||||
message: t("appointments.errors.saving", {
|
||||
message: error.message
|
||||
})
|
||||
message: error.message,
|
||||
}),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
function Column({ children }) {
|
||||
return <td>{children}</td>;
|
||||
}
|
||||
|
||||
function Row({ children }) {
|
||||
return (
|
||||
<tr>
|
||||
{React.Children.map(children, el => {
|
||||
if (el.type === Column) return el;
|
||||
|
||||
return <td>{el}</td>;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
function Grid({ children }) {
|
||||
return (
|
||||
<table>
|
||||
<tbody>
|
||||
{React.Children.map(children, el => {
|
||||
if (!el) return;
|
||||
|
||||
if (el.type === Row) return el;
|
||||
|
||||
if (el.type === Column) {
|
||||
return <tr>{el}</tr>;
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>{el}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
Grid.Row = Row;
|
||||
Grid.Column = Column;
|
||||
|
||||
export default Grid;
|
||||
@@ -24,6 +24,7 @@ export const QUERY_BODYSHOP = gql`
|
||||
region_config
|
||||
md_responsibility_centers
|
||||
messagingservicesid
|
||||
template_header
|
||||
employees {
|
||||
id
|
||||
first_name
|
||||
|
||||
13
client/src/graphql/templates.queries.js
Normal file
13
client/src/graphql/templates.queries.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { gql } from "apollo-boost";
|
||||
|
||||
export const QUERY_TEMPLATES_BY_NAME = gql`
|
||||
query QUERY_TEMPLATES_BY_NAME($name: String!) {
|
||||
templates(where: { name: { _eq: $name } }) {
|
||||
id
|
||||
html
|
||||
name
|
||||
query
|
||||
bodyshopid
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -5,14 +5,13 @@ const INITIAL_STATE = {
|
||||
messageOptions: {
|
||||
from: { name: "ShopName", address: "noreply@bodyshop.app" },
|
||||
to: null,
|
||||
replyTo: null
|
||||
replyTo: null,
|
||||
},
|
||||
template: null,
|
||||
queryConfig: [null, { variables: null }]
|
||||
template: { name: null, variables: {} },
|
||||
},
|
||||
|
||||
visible: false,
|
||||
error: null
|
||||
error: null,
|
||||
};
|
||||
|
||||
const emailReducer = (state = INITIAL_STATE, action) => {
|
||||
@@ -20,13 +19,13 @@ const emailReducer = (state = INITIAL_STATE, action) => {
|
||||
case EmailActionTypes.TOGGLE_EMAIL_OVERLAY_VISIBLE:
|
||||
return {
|
||||
...state,
|
||||
visible: !state.visible
|
||||
visible: !state.visible,
|
||||
};
|
||||
case EmailActionTypes.SET_EMAIL_OPTIONS:
|
||||
return {
|
||||
...state,
|
||||
emailConfig: { ...action.payload },
|
||||
visible: true
|
||||
visible: true,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import { createSelector } from "reselect";
|
||||
|
||||
const selectEmail = state => state.email;
|
||||
const selectEmailConfigMessageOptions = state =>
|
||||
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;
|
||||
const selectEmailConfigTemplate = (state) => state.email.emailConfig.template;
|
||||
|
||||
export const selectEmailVisible = createSelector(
|
||||
[selectEmail],
|
||||
email => email.visible
|
||||
(email) => email.visible
|
||||
);
|
||||
|
||||
export const selectEmailConfig = createSelector(
|
||||
[
|
||||
selectEmailConfigMessageOptions,
|
||||
selectEmailConfigTemplate,
|
||||
selectEmailConfigQuery
|
||||
],
|
||||
(messageOptions, template, queryConfig) => ({
|
||||
[selectEmailConfigMessageOptions, selectEmailConfigTemplate],
|
||||
(messageOptions, template) => ({
|
||||
messageOptions,
|
||||
template,
|
||||
queryConfig
|
||||
})
|
||||
);
|
||||
|
||||
946
client/yarn.lock
946
client/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "template_header";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "template_header" text NULL;
|
||||
type: run_sql
|
||||
@@ -0,0 +1,44 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- address1
|
||||
- address2
|
||||
- city
|
||||
- country
|
||||
- created_at
|
||||
- email
|
||||
- federal_tax_id
|
||||
- id
|
||||
- insurance_vendor_id
|
||||
- logo_img_path
|
||||
- md_order_statuses
|
||||
- md_responsibility_centers
|
||||
- md_ro_statuses
|
||||
- messagingservicesid
|
||||
- region_config
|
||||
- shopname
|
||||
- shoprates
|
||||
- state
|
||||
- state_tax_id
|
||||
- updated_at
|
||||
- zip_post
|
||||
computed_fields: []
|
||||
filter:
|
||||
associations:
|
||||
bodyshop:
|
||||
associations:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,45 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- address1
|
||||
- address2
|
||||
- city
|
||||
- country
|
||||
- created_at
|
||||
- email
|
||||
- federal_tax_id
|
||||
- id
|
||||
- insurance_vendor_id
|
||||
- logo_img_path
|
||||
- md_order_statuses
|
||||
- md_responsibility_centers
|
||||
- md_ro_statuses
|
||||
- messagingservicesid
|
||||
- region_config
|
||||
- shopname
|
||||
- shoprates
|
||||
- state
|
||||
- state_tax_id
|
||||
- template_header
|
||||
- updated_at
|
||||
- zip_post
|
||||
computed_fields: []
|
||||
filter:
|
||||
associations:
|
||||
bodyshop:
|
||||
associations:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,45 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- address1
|
||||
- address2
|
||||
- city
|
||||
- country
|
||||
- created_at
|
||||
- email
|
||||
- federal_tax_id
|
||||
- id
|
||||
- insurance_vendor_id
|
||||
- logo_img_path
|
||||
- md_order_statuses
|
||||
- md_responsibility_centers
|
||||
- md_ro_statuses
|
||||
- region_config
|
||||
- shopname
|
||||
- shoprates
|
||||
- state
|
||||
- state_tax_id
|
||||
- updated_at
|
||||
- zip_post
|
||||
filter:
|
||||
associations:
|
||||
bodyshop:
|
||||
associations:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,46 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- address1
|
||||
- address2
|
||||
- city
|
||||
- country
|
||||
- created_at
|
||||
- email
|
||||
- federal_tax_id
|
||||
- id
|
||||
- insurance_vendor_id
|
||||
- logo_img_path
|
||||
- md_order_statuses
|
||||
- md_responsibility_centers
|
||||
- md_ro_statuses
|
||||
- messagingservicesid
|
||||
- region_config
|
||||
- shopname
|
||||
- shoprates
|
||||
- state
|
||||
- state_tax_id
|
||||
- updated_at
|
||||
- zip_post
|
||||
filter:
|
||||
associations:
|
||||
bodyshop:
|
||||
associations:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: DROP TABLE "public"."templates";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,24 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
type: run_sql
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: "CREATE TABLE \"public\".\"templates\"(\"id\" uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||
\"created_at\" timestamptz NOT NULL DEFAULT now(), \"updated_at\" timestamptz
|
||||
NOT NULL DEFAULT now(), \"bodyshopid\" uuid, \"name\" text NOT NULL, \"html\"
|
||||
text NOT NULL, \"query\" text NOT NULL, PRIMARY KEY (\"id\") , FOREIGN KEY (\"bodyshopid\")
|
||||
REFERENCES \"public\".\"bodyshops\"(\"id\") ON UPDATE restrict ON DELETE restrict);\nCREATE
|
||||
OR REPLACE FUNCTION \"public\".\"set_current_timestamp_updated_at\"()\nRETURNS
|
||||
TRIGGER AS $$\nDECLARE\n _new record;\nBEGIN\n _new := NEW;\n _new.\"updated_at\"
|
||||
= NOW();\n RETURN _new;\nEND;\n$$ LANGUAGE plpgsql;\nCREATE TRIGGER \"set_public_templates_updated_at\"\nBEFORE
|
||||
UPDATE ON \"public\".\"templates\"\nFOR EACH ROW\nEXECUTE PROCEDURE \"public\".\"set_current_timestamp_updated_at\"();\nCOMMENT
|
||||
ON TRIGGER \"set_public_templates_updated_at\" ON \"public\".\"templates\" \nIS
|
||||
'trigger to set value of column \"updated_at\" to current timestamp on row update';"
|
||||
type: run_sql
|
||||
- args:
|
||||
name: templates
|
||||
schema: public
|
||||
type: add_existing_table_or_view
|
||||
@@ -0,0 +1,12 @@
|
||||
- args:
|
||||
relationship: templates
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: drop_relationship
|
||||
- args:
|
||||
relationship: bodyshop
|
||||
table:
|
||||
name: templates
|
||||
schema: public
|
||||
type: drop_relationship
|
||||
@@ -0,0 +1,20 @@
|
||||
- args:
|
||||
name: templates
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bodyshopid
|
||||
table:
|
||||
name: templates
|
||||
schema: public
|
||||
type: create_array_relationship
|
||||
- args:
|
||||
name: bodyshop
|
||||
table:
|
||||
name: templates
|
||||
schema: public
|
||||
using:
|
||||
foreign_key_constraint_on: bodyshopid
|
||||
type: create_object_relationship
|
||||
@@ -0,0 +1,6 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: templates
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
@@ -0,0 +1,30 @@
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- html
|
||||
- name
|
||||
- query
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
computed_fields: []
|
||||
filter:
|
||||
_or:
|
||||
- bodyshopid:
|
||||
_is_null: true
|
||||
- bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
limit: null
|
||||
role: user
|
||||
table:
|
||||
name: templates
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,6 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: templates
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
@@ -0,0 +1,31 @@
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- html
|
||||
- name
|
||||
- query
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
filter:
|
||||
_or:
|
||||
- bodyshopid:
|
||||
_is_null: true
|
||||
- bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: templates
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -3,7 +3,14 @@ const Handlebars = require("handlebars");
|
||||
|
||||
exports.render = (req, res) => {
|
||||
//Perform request validation
|
||||
console.log("Got a render request.");
|
||||
var template = Handlebars.compile(req.body.view);
|
||||
let view;
|
||||
if (req.body.context.bodyshop.template_header) {
|
||||
console.log("[HJS Render] Including Header");
|
||||
view = `${req.body.context.bodyshop.template_header}${req.body.view}`;
|
||||
} else {
|
||||
console.log("[HJS Render] No header to include.");
|
||||
view = req.body.view;
|
||||
}
|
||||
var template = Handlebars.compile(view);
|
||||
res.send(template(req.body.context));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user