Added new HTML editor with some breaking changes.

This commit is contained in:
Patrick Fic
2020-08-14 15:50:36 -07:00
parent 57cbc6961c
commit 873e81556d
20 changed files with 574 additions and 358 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.7.1"> <babeledit_project be_version="2.7.1" version="1.2">
<!-- <!--
BabelEdit project file BabelEdit project file

View File

@@ -36,6 +36,7 @@
"react-big-calendar": "^0.26.0", "react-big-calendar": "^0.26.0",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-drag-listview": "^0.1.7", "react-drag-listview": "^0.1.7",
"react-email-editor": "^1.1.1",
"react-ga": "^3.1.2", "react-ga": "^3.1.2",
"react-grid-gallery": "^0.5.5", "react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.0.0", "react-grid-layout": "^1.0.0",

View File

@@ -45,7 +45,7 @@ export default function FormsFieldChanged({ form }) {
/> />
</div> </div>
); );
return null; return <div style={{ display: "none" }}></div>;
}} }}
</Form.Item> </Form.Item>
); );

View File

@@ -6,13 +6,23 @@ import Icon, {
FileFilled, FileFilled,
GlobalOutlined, GlobalOutlined,
HomeFilled, HomeFilled,
ImportOutlined,
LineChartOutlined,
ScheduleOutlined,
TeamOutlined, TeamOutlined,
UnorderedListOutlined,
UserOutlined, UserOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Avatar, Col, Layout, Menu, Row } from "antd"; import { Avatar, Layout, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { FaCalendarAlt, FaCarCrash, FaCreditCard } from "react-icons/fa"; import { BsKanban } from "react-icons/bs";
import {
FaCalendarAlt,
FaCarCrash,
FaCreditCard,
FaFileInvoiceDollar,
} from "react-icons/fa";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -42,30 +52,6 @@ const mapDispatchToProps = (dispatch) => ({
signOutStart: () => dispatch(signOutStart()), signOutStart: () => dispatch(signOutStart()),
}); });
const logoSpan = {
xs: {
span: 0,
},
sm: { span: 0 },
md: {
span: 0,
},
lg: {
span: 2,
},
};
const menuSpan = {
md: {
span: 24,
//offset: 1,
},
lg: {
span: 21,
offset: 1,
},
};
function Header({ function Header({
bodyshop, bodyshop,
handleMenuClick, handleMenuClick,
@@ -81,280 +67,276 @@ function Header({
return ( return (
<Header> <Header>
<Row> <Menu
<Col {...logoSpan}> mode="horizontal"
<img theme="dark"
className="header-shop-logo" className="header-main-menu"
alt={bodyshop ? bodyshop.shopname : "ImEX Online Logo"} selectedKeys={["home"]}
src={ onClick={handleMenuClick}
bodyshop && bodyshop.logo_img_path >
? bodyshop.logo_img_path <Menu.Item key="home">
: "./logo192.png" <Link to="/manage">
} <HomeFilled />
/> {t("menus.header.home")}
</Col> </Link>
<Col {...menuSpan}> </Menu.Item>
<Menu <Menu.Item key="schedule">
mode="horizontal" <Link to="/manage/schedule">
theme="dark" <Icon component={FaCalendarAlt} />
className="header-main-menu" {t("menus.header.schedule")}
selectedKeys={["home"]} </Link>
onClick={handleMenuClick} </Menu.Item>
<Menu.SubMenu
title={
<span>
<Icon component={FaCarCrash} />
<span>{t("menus.header.jobs")}</span>
</span>
}
>
<Menu.Item key="activejobs">
<FileFilled />
<Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
</Menu.Item>
<Menu.Item key="availablejobs">
<Link to="/manage/available">
<ImportOutlined /> {t("menus.header.availablejobs")}
</Link>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="alljobs">
<UnorderedListOutlined />
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="productionlist">
<Link to="/manage/production/list">
<ScheduleOutlined />
{t("menus.header.productionlist")}
</Link>
</Menu.Item>
<Menu.Item key="productionboard">
<Link to="/manage/production/board">
<Icon component={BsKanban} />
{t("menus.header.productionboard")}
</Link>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="scoreboard">
<LineChartOutlined />
<Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
title={
<span>
<UserOutlined />
<span>{t("menus.header.customers")}</span>
</span>
}
>
<Menu.Item key="owners">
<Link to="/manage/owners">
<TeamOutlined />
{t("menus.header.owners")}
</Link>
</Menu.Item>
<Menu.Item key="vehicles">
<Link to="/manage/vehicles">
<CarFilled />
{t("menus.header.vehicles")}
</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
title={
<span>
<CarFilled />
<span>{t("menus.header.courtesycars")}</span>
</span>
}
>
<Menu.Item key="courtesycarsall">
<Link to="/manage/courtesycars">
<CarFilled />
{t("menus.header.courtesycars-all")}
</Link>
</Menu.Item>
<Menu.Item key="contracts">
<Link to="/manage/courtesycars/contracts">
<FileFilled />
{t("menus.header.courtesycars-contracts")}
</Link>
</Menu.Item>
<Menu.Item key="newcontract">
<Link to="/manage/courtesycars/contracts/new">
<FileAddFilled />
{t("menus.header.courtesycars-newcontract")}
</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
title={
<span>
<DollarCircleFilled />
<span>{t("menus.header.accounting")}</span>
</span>
}
>
<Menu.Item key="invoices">
<Link to="/manage/invoices">{t("menus.header.invoices")}</Link>
</Menu.Item>
<Menu.Item
key="enterinvoices"
onClick={() => {
setInvoiceEnterContext({
actions: {},
context: {},
});
}}
> >
<Menu.Item key="home"> <Icon component={FaFileInvoiceDollar} />
<Link to="/manage"> {t("menus.header.enterinvoices")}
<HomeFilled /> </Menu.Item>
{t("menus.header.home")} <Menu.Divider />
<Menu.Item key="allpayments">
<Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
</Menu.Item>
<Menu.Item
key="enterpayments"
onClick={() => {
setPaymentContext({
actions: {},
context: {},
});
}}
>
<Icon component={FaCreditCard} />
{t("menus.header.enterpayment")}
</Menu.Item>
<Menu.Divider />
<Menu.Item key="timetickets">
<Link to="/manage/timetickets">
{t("menus.header.timetickets")}
</Link>
</Menu.Item>
<Menu.Item
key="entertimetickets"
onClick={() => {
setTimeTicketContext({
actions: {},
context: {},
});
}}
>
{t("menus.header.entertimeticket")}
</Menu.Item>
<Menu.Divider />
<Menu.SubMenu title={t("menus.header.export")}>
<Menu.Item key="receivables">
<Link to="/manage/accounting/receivables">
{t("menus.header.accounting-receivables")}
</Link> </Link>
</Menu.Item> </Menu.Item>
<Menu.SubMenu <Menu.Item key="payables">
title={ <Link to="/manage/accounting/payables">
<span> {t("menus.header.accounting-payables")}
<Icon component={FaCarCrash} /> </Link>
<span>{t("menus.header.jobs")}</span>
</span>
}
>
<Menu.Item key="schedule">
<Link to="/manage/schedule">
<Icon component={FaCalendarAlt} />
{t("menus.header.schedule")}
</Link>
</Menu.Item>
<Menu.Item key="productionlist">
<Link to="/manage/production/list">
<Icon component={FaCalendarAlt} />
{t("menus.header.productionlist")}
</Link>
</Menu.Item>
<Menu.Item key="productionboard">
<Link to="/manage/production/board">
{t("menus.header.productionboard")}
</Link>
</Menu.Item>
<Menu.Item key="scoreboard">
<Link to="/manage/scoreboard">
{t("menus.header.scoreboard")}
</Link>
</Menu.Item>
<Menu.Item key="activejobs">
<Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
</Menu.Item>
<Menu.Item key="alljobs">
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
</Menu.Item>
<Menu.Item key="availablejobs">
<Link to="/manage/available">
{t("menus.header.availablejobs")}
</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu title={t("menus.header.customers")}>
<Menu.Item key="owners">
<Link to="/manage/owners">
<TeamOutlined />
{t("menus.header.owners")}
</Link>
</Menu.Item>
<Menu.Item key="vehicles">
<Link to="/manage/vehicles">
<CarFilled />
{t("menus.header.vehicles")}
</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
title={
<span>
<CarFilled />
<span>{t("menus.header.courtesycars")}</span>
</span>
}
>
<Menu.Item key="courtesycarsall">
<Link to="/manage/courtesycars">
<CarFilled />
{t("menus.header.courtesycars-all")}
</Link>
</Menu.Item>
<Menu.Item key="contracts">
<Link to="/manage/courtesycars/contracts">
<FileFilled />
{t("menus.header.courtesycars-contracts")}
</Link>
</Menu.Item>
<Menu.Item key="newcontract">
<Link to="/manage/courtesycars/contracts/new">
<FileAddFilled />
{t("menus.header.courtesycars-newcontract")}
</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
title={
<span>
<DollarCircleFilled />
<span>{t("menus.header.accounting")}</span>
</span>
}
>
<Menu.Item
key="enterpayments"
onClick={() => {
setPaymentContext({
actions: {},
context: {},
});
}}
>
<Icon component={FaCreditCard} />
{t("menus.header.enterpayment")}
</Menu.Item>
<Menu.Item
key="enterinvoices"
onClick={() => {
setInvoiceEnterContext({
actions: {},
context: {},
});
}}
>
{t("menus.header.enterinvoices")}
</Menu.Item>
<Menu.Item key="invoices">
<Link to="/manage/invoices">{t("menus.header.invoices")}</Link>
</Menu.Item>
<Menu.Item key="timetickets">
<Link to="/manage/timetickets">
{t("menus.header.timetickets")}
</Link>
</Menu.Item>
<Menu.Item
key="entertimetickets"
onClick={() => {
setTimeTicketContext({
actions: {},
context: {},
});
}}
>
{t("menus.header.entertimeticket")}
</Menu.Item>
<Menu.SubMenu title={t("menus.header.export")}>
<Menu.Item key="receivables">
<Link to="/manage/accounting/receivables">
{t("menus.header.accounting-receivables")}
</Link>
</Menu.Item>
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}
</Link>
</Menu.Item>
<Menu.Item key="payments">
<Link to="/manage/accounting/payments">
{t("menus.header.accounting-payments")}
</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.Item key="allpayments">
<Link to="/manage/payments">
{t("menus.header.allpayments")}
</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu title={t("menus.header.shop")}>
<Menu.Item key="shop">
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
</Menu.Item>
<Menu.Item key="shop-templates">
<Link to="/manage/shop/templates">
{t("menus.header.shop_templates")}
</Link>
</Menu.Item>
<Menu.Item key="shop-vendors">
<Link to="/manage/shop/vendors">
{t("menus.header.shop_vendors")}
</Link>
</Menu.Item>
<Menu.Item key="shop-csi">
<Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.Item>
<GlobalSearch />
</Menu.Item> </Menu.Item>
<Menu.SubMenu title={<ClockCircleFilled />}> <Menu.Item key="payments">
{recentItems.map((i, idx) => ( <Link to="/manage/accounting/payments">
<Menu.Item key={idx}> {t("menus.header.accounting-payments")}
<Link to={i.url}>{i.label}</Link> </Link>
</Menu.Item> </Menu.Item>
))} </Menu.SubMenu>
</Menu.SubMenu> </Menu.SubMenu>
<Menu.SubMenu <Menu.SubMenu title={t("menus.header.shop")}>
title={ <Menu.Item key="shop">
<div> <Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
{currentUser.photoURL ? ( </Menu.Item>
<Avatar
src={currentUser.photoURL}
style={{
margin: "10px",
}}
/>
) : (
<Avatar
style={{
backgroundColor: "#87d068",
margin: "10px",
}}
icon={<UserOutlined />}
/>
)}
{currentUser.displayName || t("general.labels.unknown")} <Menu.Item key="shop-templates">
</div> <Link to="/manage/shop/templates">
} {t("menus.header.shop_templates")}
> </Link>
<Menu.Item danger onClick={() => signOutStart()}> </Menu.Item>
{t("user.actions.signout")}
</Menu.Item> <Menu.Item key="shop-vendors">
<Menu.Item key="shiftclock"> <Link to="/manage/shop/vendors">
<Link to="/manage/shiftclock"> {t("menus.header.shop_vendors")}
{t("menus.header.shiftclock")} </Link>
</Link> </Menu.Item>
</Menu.Item> <Menu.Item key="shop-csi">
<Menu.Item> <Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
<Link to="/manage/profile"> </Menu.Item>
{t("menus.currentuser.profile")} </Menu.SubMenu>
</Link> <Menu.Item>
</Menu.Item> <GlobalSearch />
<Menu.SubMenu </Menu.Item>
title={ <Menu.SubMenu title={<ClockCircleFilled />}>
<span> {recentItems.map((i, idx) => (
<GlobalOutlined /> <Menu.Item key={idx}>
<span>{t("menus.currentuser.languageselector")}</span> <Link to={i.url}>{i.label}</Link>
</span> </Menu.Item>
} ))}
> </Menu.SubMenu>
<Menu.Item actiontype="lang-select" key="en-US"> <Menu.SubMenu
{t("general.languages.english")} title={
</Menu.Item> <div>
<Menu.Item actiontype="lang-select" key="fr-CA"> {currentUser.photoURL ? (
{t("general.languages.french")} <Avatar
</Menu.Item> src={currentUser.photoURL}
<Menu.Item actiontype="lang-select" key="es-MX"> style={{
{t("general.languages.spanish")} margin: "10px",
</Menu.Item> }}
</Menu.SubMenu> />
</Menu.SubMenu> ) : (
</Menu> <Avatar
</Col> style={{
</Row> backgroundColor: "#87d068",
margin: "10px",
}}
icon={<UserOutlined />}
/>
)}
{currentUser.displayName || t("general.labels.unknown")}
</div>
}
>
<Menu.Item danger onClick={() => signOutStart()}>
{t("user.actions.signout")}
</Menu.Item>
<Menu.Item key="shiftclock">
<Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
</Menu.Item>
<Menu.Item>
<Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
</Menu.Item>
<Menu.SubMenu
title={
<span>
<GlobalOutlined />
<span>{t("menus.currentuser.languageselector")}</span>
</span>
}
>
<Menu.Item actiontype="lang-select" key="en-US">
{t("general.languages.english")}
</Menu.Item>
<Menu.Item actiontype="lang-select" key="fr-CA">
{t("general.languages.french")}
</Menu.Item>
<Menu.Item actiontype="lang-select" key="es-MX">
{t("general.languages.spanish")}
</Menu.Item>
</Menu.SubMenu>
</Menu.SubMenu>
</Menu>
</Header> </Header>
); );
} }

View File

@@ -5,28 +5,41 @@ import { useTranslation } from "react-i18next";
import { UPDATE_TEMPLATE } from "../../graphql/templates.queries"; import { UPDATE_TEMPLATE } from "../../graphql/templates.queries";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
export default function ShopTemplateSaveButton({ templateId, html, gql }) { export default function ShopTemplateSaveButton({
templateId,
gql,
emailEditorRef,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateTemplate] = useMutation(UPDATE_TEMPLATE); const [updateTemplate] = useMutation(UPDATE_TEMPLATE);
const handleSave = async () => { const handleSave = async () => {
logImEXEvent("shop_template_update"); logImEXEvent("shop_template_update");
const result = await updateTemplate({ emailEditorRef.current.exportHtml(async (data) => {
variables: { console.log("handleSave -> html", data);
templateId: templateId, const result = await updateTemplate({
template: { query: gql, html }, variables: {
}, templateId: templateId,
}); template: {
if (!!!result.errors) { query: gql,
notification["success"]({ message: t("templates.successes.updated") }); html: data.html,
} else { jsontemplate: data.design,
notification["error"]({ },
message: t("templates.errors.updating", { },
error: JSON.stringify(result.errors),
}),
}); });
} if (!!!result.errors) {
notification["success"]({
message: t("templates.successes.updated"),
});
} else {
notification["error"]({
message: t("templates.errors.updating", {
error: JSON.stringify(result.errors),
}),
});
}
});
}; };
return ( return (

View File

@@ -1,20 +1,22 @@
import { Editor } from "@tinymce/tinymce-react";
import React, { useEffect } from "react";
import ShopTemplateEditorSaveButton from "../shop-template-editor-save-button/shop-template-editor-save-button.component";
import { Input } from "antd"; import { Input } from "antd";
import React, { useEffect, useRef } from "react";
import EmailEditor from "react-email-editor";
import ShopTemplateEditorSaveButton from "../shop-template-editor-save-button/shop-template-editor-save-button.component";
export default function ShopTemplateEditorComponent({ export default function ShopTemplateEditorComponent({
templateId, templateId,
html, html,
gql, gql,
json,
editorState, editorState,
}) { }) {
const [editorContent, seteditorContent] = editorState; const [editorContent, seteditorContent] = editorState;
const emailEditorRef = useRef(null);
useEffect(() => { useEffect(() => {
seteditorContent((prevstate) => { if (json && Object.keys(json).length > 0)
return { ...prevstate, html: html }; emailEditorRef.current.loadDesign(json);
}); }, [json, emailEditorRef]);
}, [html, seteditorContent]);
useEffect(() => { useEffect(() => {
seteditorContent((prevstate) => { seteditorContent((prevstate) => {
@@ -22,34 +24,29 @@ export default function ShopTemplateEditorComponent({
}); });
}, [gql, seteditorContent]); }, [gql, seteditorContent]);
const exportJson = (props) => {
emailEditorRef.current.saveDesign((js) => {
seteditorContent({ ...editorContent, JSON: js });
console.log(js);
});
};
const exportHtml = (props) => {
emailEditorRef.current.exportHtml((js) => {
console.log(js);
});
};
return ( return (
<div> <div>
<ShopTemplateEditorSaveButton <ShopTemplateEditorSaveButton
templateId={templateId} templateId={templateId}
html={editorContent.html}
gql={editorContent.gql} gql={editorContent.gql}
emailEditorRef={emailEditorRef}
/> />
<Editor <button onClick={exportJson}>json</button>
value={editorContent.html} <button onClick={exportHtml}>html</button>
apiKey='f3s2mjsd77ya5qvqkee9vgh612cm6h41e85efqakn2d0kknk' //TODO Pull this into app var <EmailEditor ref={emailEditorRef} />
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={(text) =>
seteditorContent({ ...editorContent, html: text })
}
/>
QUERY QUERY
<Input.TextArea <Input.TextArea
value={editorContent.gql} value={editorContent.gql}

View File

@@ -19,14 +19,14 @@ export default function ShopTemplateEditorContainer() {
}); });
if (!!!search.customTemplateId) return <span>No selection.</span>; if (!!!search.customTemplateId) return <span>No selection.</span>;
if (error) return <AlertComponent message={error.message} type='error' />; if (error) return <AlertComponent message={error.message} type="error" />;
return ( return (
<LoadingSpinner loading={loading}> <LoadingSpinner loading={loading}>
{data && data.templates_by_pk ? data.templates_by_pk.name : ""} {data && data.templates_by_pk ? data.templates_by_pk.name : ""}
<ShopTemplateEditorComponent <ShopTemplateEditorComponent
templateId={search.customTemplateId} templateId={search.customTemplateId}
html={data && data.templates_by_pk ? data.templates_by_pk.html : ""} json={data && data.templates_by_pk && data.templates_by_pk.jsontemplate}
gql={data && data.templates_by_pk ? data.templates_by_pk.query : ""} gql={data && data.templates_by_pk ? data.templates_by_pk.query : ""}
editorState={editorState} editorState={editorState}
/> />

View File

@@ -4,9 +4,9 @@ export const QUERY_TEMPLATES_BY_NAME = gql`
query QUERY_TEMPLATES_BY_NAME($name: String!) { query QUERY_TEMPLATES_BY_NAME($name: String!) {
templates(where: { name: { _eq: $name } }) { templates(where: { name: { _eq: $name } }) {
id id
html
name name
query query
html
bodyshopid bodyshopid
} }
} }
@@ -28,6 +28,7 @@ export const QUERY_TEMPLATE_BY_PK = gql`
name name
query query
html html
jsontemplate
} }
} }
`; `;

View File

@@ -32,10 +32,10 @@ export function ShopTemplatesContainer({ setBreadcrumbs, bodyshop }) {
return ( return (
<RbacWrapper action="shop:templates"> <RbacWrapper action="shop:templates">
<Row> <Row>
<Col span={6}> <Col span={4}>
<ShopTemplatesListContainer /> <ShopTemplatesListContainer />
</Col> </Col>
<Col span={18}> <Col span={20}>
<div> <div>
<ShopTemplateEditor /> <ShopTemplateEditor />
</div> </div>

View File

@@ -938,8 +938,8 @@
"invoices": "Invoices", "invoices": "Invoices",
"jobs": "Jobs", "jobs": "Jobs",
"owners": "Owners", "owners": "Owners",
"productionboard": "Production Board", "productionboard": "Production Board - Visual",
"productionlist": "Production - List", "productionlist": "Production Board - List",
"recent": "Recent Items", "recent": "Recent Items",
"schedule": "Schedule", "schedule": "Schedule",
"scoreboard": "Scoreboard", "scoreboard": "Scoreboard",
@@ -983,7 +983,7 @@
"joblookup": "Job Lookup", "joblookup": "Job Lookup",
"login": "Login", "login": "Login",
"logout": "Logout", "logout": "Logout",
"productionboard": "Production Board", "productionboard": "Production Board - Visual",
"productionlist": "Production List" "productionlist": "Production List"
} }
}, },
@@ -1301,8 +1301,8 @@
"owner-detail": "{{name}}", "owner-detail": "{{name}}",
"owners": "Owners", "owners": "Owners",
"payments-all": "All Payments", "payments-all": "All Payments",
"productionboard": "Production Board", "productionboard": "Production Board - Visual",
"productionlist": "Production - List", "productionlist": "Production Board - List",
"schedule": "Schedule", "schedule": "Schedule",
"scoreboard": "Scoreboard", "scoreboard": "Scoreboard",
"shop": "Manage my Shop ({{shopname}})", "shop": "Manage my Shop ({{shopname}})",
@@ -1332,7 +1332,7 @@
"owners-detail": "{{name}} | $t(titles.app)", "owners-detail": "{{name}} | $t(titles.app)",
"payments-all": "Payments | $t(titles.app)", "payments-all": "Payments | $t(titles.app)",
"productionboard": "Production - Board", "productionboard": "Production - Board",
"productionlist": "Production - List View | $t(titles.app)", "productionlist": "Production Board - List | $t(titles.app)",
"profile": "My Profile | $t(titles.app)", "profile": "My Profile | $t(titles.app)",
"resetpassword": "Reset Password", "resetpassword": "Reset Password",
"resetpasswordvalidate": "Enter New Password", "resetpasswordvalidate": "Enter New Password",

View File

@@ -10876,6 +10876,11 @@ react-draggable@^4.0.0, react-draggable@^4.0.3:
classnames "^2.2.5" classnames "^2.2.5"
prop-types "^15.6.0" prop-types "^15.6.0"
react-email-editor@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/react-email-editor/-/react-email-editor-1.1.1.tgz#5a809c5588cf0a2042cf144771ef340cded36f92"
integrity sha512-8V4Iba3dSw0eTmNQlmhisPv2EsiWAwEBVvKsWFiW/BcQ1x42vchbWCNCX7ss0njMQ9r7uReSptrcy10f4MtVEA==
react-error-overlay@^6.0.7: react-error-overlay@^6.0.7:
version "6.0.7" version "6.0.7"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."templates" DROP COLUMN "jsontemplate";
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."templates" ADD COLUMN "jsontemplate" jsonb NULL DEFAULT
jsonb_build_object();
type: run_sql

View File

@@ -0,0 +1,31 @@
- args:
role: user
table:
name: templates
schema: public
type: drop_insert_permission
- args:
permission:
check:
_or:
- bodyshopid:
_is_null: true
- bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- bodyshopid
- html
- name
- query
set: {}
role: user
table:
name: templates
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,32 @@
- args:
role: user
table:
name: templates
schema: public
type: drop_insert_permission
- args:
permission:
check:
_or:
- bodyshopid:
_is_null: true
- bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- bodyshopid
- html
- jsontemplate
- name
- query
set: {}
role: user
table:
name: templates
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,35 @@
- args:
role: user
table:
name: templates
schema: public
type: drop_select_permission
- 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
role: user
table:
name: templates
schema: public
type: create_select_permission

View File

@@ -0,0 +1,36 @@
- args:
role: user
table:
name: templates
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- bodyshopid
- created_at
- html
- id
- jsontemplate
- name
- query
- updated_at
computed_fields: []
filter:
_or:
- bodyshopid:
_is_null: true
- bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: templates
schema: public
type: create_select_permission

View File

@@ -0,0 +1,34 @@
- args:
role: user
table:
name: templates
schema: public
type: drop_update_permission
- 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
set: {}
role: user
table:
name: templates
schema: public
type: create_update_permission

View File

@@ -0,0 +1,35 @@
- args:
role: user
table:
name: templates
schema: public
type: drop_update_permission
- args:
permission:
columns:
- bodyshopid
- created_at
- html
- id
- jsontemplate
- name
- query
- updated_at
filter:
_or:
- bodyshopid:
_is_null: true
- bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: templates
schema: public
type: create_update_permission

View File

@@ -3569,19 +3569,21 @@ tables:
columns: columns:
- bodyshopid - bodyshopid
- html - html
- jsontemplate
- name - name
- query - query
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- bodyshopid
- created_at
- html - html
- id
- jsontemplate
- name - name
- query - query
- created_at
- updated_at - updated_at
- bodyshopid
- id
filter: filter:
_or: _or:
- bodyshopid: - bodyshopid:
@@ -3598,13 +3600,14 @@ tables:
- role: user - role: user
permission: permission:
columns: columns:
- bodyshopid
- created_at
- html - html
- id
- jsontemplate
- name - name
- query - query
- created_at
- updated_at - updated_at
- bodyshopid
- id
filter: filter:
_or: _or:
- bodyshopid: - bodyshopid: