Added deleting of custom templates for BOD-85.
This commit is contained in:
@@ -218,6 +218,11 @@ function Header({
|
||||
<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")}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { DownOutlined } from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/react-hooks";
|
||||
import { Dropdown, Menu } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_TEMPLATE } from "../../graphql/templates.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { TemplateList } from "../../utils/constants";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null)(ShopTemplateAddComponent);
|
||||
|
||||
export function ShopTemplateAddComponent({
|
||||
bodyshop,
|
||||
shopTemplateList,
|
||||
refetch,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
const [insertTemplate] = useMutation(INSERT_TEMPLATE);
|
||||
|
||||
const shopTemplateKeys = shopTemplateList.map((template) => template.name);
|
||||
const availableTemplateKeys = Object.keys(TemplateList).filter(
|
||||
(tkey) => !shopTemplateKeys.includes(tkey)
|
||||
);
|
||||
|
||||
const handleAdd = async (item) => {
|
||||
const result = await insertTemplate({
|
||||
variables: {
|
||||
template: {
|
||||
name: item.key,
|
||||
bodyshopid: bodyshop.id,
|
||||
html: `<div>Insert your custom template here.</div>`,
|
||||
query: `query JOBS {
|
||||
jobs{
|
||||
id
|
||||
ro_number
|
||||
}
|
||||
}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const returningId = result.data.insert_templates.returning[0].id;
|
||||
search.customTemplateId = returningId;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
if (!!refetch) refetch();
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<Menu onClick={handleAdd}>
|
||||
{availableTemplateKeys.length > 0 ? (
|
||||
availableTemplateKeys.map((tkey) => (
|
||||
<Menu.Item key={tkey}>{TemplateList[tkey].title}</Menu.Item>
|
||||
))
|
||||
) : (
|
||||
<div>{t("bodyshop.labels.notemplatesavailable")}</div>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown overlay={menu}>
|
||||
<span>
|
||||
{t("bodyshop.actions.addtemplate")} <DownOutlined />
|
||||
</span>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useMutation } from "@apollo/react-hooks";
|
||||
import { Button, notification, Popconfirm } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { DELETE_TEMPLATE } from "../../graphql/templates.queries";
|
||||
|
||||
export default function ShopTemplateDeleteComponent({ templateId, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
const [deleteTemplate] = useMutation(DELETE_TEMPLATE);
|
||||
|
||||
const handleDelete = async () => {
|
||||
const result = await deleteTemplate({
|
||||
variables: {
|
||||
templateId: templateId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("bodyshop.errors.deletingtemplate", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
delete search.customTemplateId;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
if (!!refetch) refetch();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Popconfirm
|
||||
title={t("general.labels.areyousure")}
|
||||
okText={t("general.labels.yes")}
|
||||
cancelText={t("general.labels.no")}
|
||||
onConfirm={handleDelete}>
|
||||
<Button>{t("general.actions.delete")}</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
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";
|
||||
export default function ShopTemplateEditorComponent({
|
||||
templateId,
|
||||
html,
|
||||
@@ -11,14 +11,12 @@ export default function ShopTemplateEditorComponent({
|
||||
const [editorContent, seteditorContent] = editorState;
|
||||
|
||||
useEffect(() => {
|
||||
console.log("HTML UE");
|
||||
seteditorContent((prevstate) => {
|
||||
return { ...prevstate, html: html };
|
||||
});
|
||||
}, [html, seteditorContent]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("gql UE");
|
||||
seteditorContent((prevstate) => {
|
||||
return { ...prevstate, gql: gql };
|
||||
});
|
||||
@@ -26,13 +24,11 @@ export default function ShopTemplateEditorComponent({
|
||||
|
||||
return (
|
||||
<div>
|
||||
Editor Here. template Editor
|
||||
<ShopTemplateEditorSaveButton
|
||||
templateId={templateId}
|
||||
html={editorContent.html}
|
||||
gql={editorContent.gql}
|
||||
/>
|
||||
TEMPLATE
|
||||
<Editor
|
||||
value={editorContent.html}
|
||||
apiKey='f3s2mjsd77ya5qvqkee9vgh612cm6h41e85efqakn2d0kknk' //TODO Pull this into app var
|
||||
@@ -55,37 +51,13 @@ export default function ShopTemplateEditorComponent({
|
||||
}
|
||||
/>
|
||||
QUERY
|
||||
<Editor
|
||||
<Input.TextArea
|
||||
value={editorContent.gql}
|
||||
apiKey='f3s2mjsd77ya5qvqkee9vgh612cm6h41e85efqakn2d0kknk' //TODO Pull this into app var
|
||||
init={{
|
||||
height: 500,
|
||||
//menubar: false,
|
||||
encoding: "raw",
|
||||
extended_valid_elements: "span",
|
||||
//entity_encoding: "raw",
|
||||
plugins: [],
|
||||
toolbar: "undo redo",
|
||||
}}
|
||||
onEditorChange={(text) =>
|
||||
seteditorContent({ ...editorContent, gql: text })
|
||||
rows={8}
|
||||
onChange={(e) =>
|
||||
seteditorContent({ ...editorContent, gql: e.target.value })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// <GraphiQL
|
||||
// fetcher={async (graphQLParams) => {
|
||||
// const data = await fetch(process.env.REACT_APP_GRAPHQL_ENDPOINT, {
|
||||
// method: "POST",
|
||||
// headers: {
|
||||
// Accept: "application/json",
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// body: JSON.stringify(graphQLParams),
|
||||
// credentials: "same-origin",
|
||||
// });
|
||||
// return data.json().catch(() => data.text());
|
||||
// }}
|
||||
// />
|
||||
|
||||
@@ -23,10 +23,11 @@ export default function ShopTemplateEditorContainer() {
|
||||
|
||||
return (
|
||||
<LoadingSpinner loading={loading}>
|
||||
{data && data.templates_by_pk ? data.templates_by_pk.name : ""}
|
||||
<ShopTemplateEditorComponent
|
||||
templateId={search.customTemplateId}
|
||||
html={data ? data.templates_by_pk.html : ""}
|
||||
gql={data ? data.templates_by_pk.query : ""}
|
||||
html={data && data.templates_by_pk ? data.templates_by_pk.html : ""}
|
||||
gql={data && data.templates_by_pk ? data.templates_by_pk.query : ""}
|
||||
editorState={editorState}
|
||||
/>
|
||||
</LoadingSpinner>
|
||||
|
||||
@@ -7,9 +7,12 @@ import Skeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import queryString from "query-string";
|
||||
import { TemplateList } from "../../utils/constants";
|
||||
import ShopTemplateAdd from "../shop-template-add/shop-template-add.component";
|
||||
import ShopTemplateDeleteComponent from "../shop-template-delete/shop-template-delete.component";
|
||||
|
||||
export default function ShopTemplatesListContainer() {
|
||||
const { loading, error, data } = useQuery(QUERY_CUSTOM_TEMPLATES);
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_CUSTOM_TEMPLATES);
|
||||
const { t } = useTranslation();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
@@ -29,7 +32,11 @@ export default function ShopTemplatesListContainer() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>CUSTOM TEMPLATES</div>
|
||||
<div>{t("bodyshop.labels.customtemplates")}</div>
|
||||
<ShopTemplateAdd
|
||||
shopTemplateList={data ? data.templates : []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<List
|
||||
loading={loading}
|
||||
itemLayout='horizontal'
|
||||
@@ -40,12 +47,15 @@ export default function ShopTemplatesListContainer() {
|
||||
<Button onClick={() => handleEdit(item)}>
|
||||
{t("general.actions.edit")}
|
||||
</Button>,
|
||||
<Button>{t("general.actions.delete")}</Button>,
|
||||
<ShopTemplateDeleteComponent
|
||||
templateId={item.id}
|
||||
refetch={refetch}
|
||||
/>,
|
||||
]}>
|
||||
<Skeleton title={false} loading={item.loading} active>
|
||||
<List.Item.Meta
|
||||
title={item.name}
|
||||
description='TODO CROSS MATCH DESCRIPTION'
|
||||
title={TemplateList[item.name].title}
|
||||
description={TemplateList[item.name].description}
|
||||
/>
|
||||
</Skeleton>
|
||||
</List.Item>
|
||||
|
||||
@@ -25,6 +25,7 @@ export const QUERY_TEMPLATE_BY_PK = gql`
|
||||
query QUERY_TEMPLATE_BY_PK($templateId: uuid!) {
|
||||
templates_by_pk(id: $templateId) {
|
||||
id
|
||||
name
|
||||
query
|
||||
html
|
||||
}
|
||||
@@ -42,3 +43,21 @@ export const UPDATE_TEMPLATE = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const INSERT_TEMPLATE = gql`
|
||||
mutation INSERT_TEMPLATE($template: templates_insert_input!) {
|
||||
insert_templates(objects: [$template]) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_TEMPLATE = gql`
|
||||
mutation DELETE_TEMPLATE($templateId: uuid!) {
|
||||
delete_templates(where: { id: { _eq: $templateId } }) {
|
||||
affected_rows
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ShopTemplatesListContainer from "../../components/shop-templates-list/shop-templates-list.container";
|
||||
import ShopTemplateEditor from "../../components/shop-template-editor/shop-template-editor.container";
|
||||
|
||||
import { Row, Col } from "antd";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
@@ -28,10 +28,16 @@ export function ShopTemplatesContainer({ setBreadcrumbs, bodyshop }) {
|
||||
}, [t, setBreadcrumbs, bodyshop.shopname]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ShopTemplatesListContainer />
|
||||
<ShopTemplateEditor />
|
||||
</div>
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<ShopTemplatesListContainer />
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<div>
|
||||
<ShopTemplateEditor />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
},
|
||||
"bodyshop": {
|
||||
"actions": {
|
||||
"addtemplate": "Add Template",
|
||||
"newstatus": "Add Status"
|
||||
},
|
||||
"errors": {
|
||||
@@ -129,7 +130,9 @@
|
||||
"labels": {
|
||||
"alljobstatuses": "All Job Statuses",
|
||||
"allopenjobstatuses": "All Open Job Statuses",
|
||||
"customtemplates": "Custom Templates",
|
||||
"jobstatuses": "Job Statuses",
|
||||
"notemplatesavailable": "No templates available to add.",
|
||||
"orderstatuses": "Order Statuses",
|
||||
"responsibilitycenters": {
|
||||
"costs": "Cost Centers",
|
||||
@@ -305,6 +308,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"actions": "Actions",
|
||||
"areyousure": "Are you sure?",
|
||||
"barcode": "Barcode",
|
||||
"in": "In",
|
||||
"loading": "Loading...",
|
||||
@@ -312,9 +316,11 @@
|
||||
"loadingshop": "Loading shop data...",
|
||||
"loggingin": "Logging you in...",
|
||||
"na": "N/A",
|
||||
"no": "No",
|
||||
"out": "Out",
|
||||
"search": "Search...",
|
||||
"unknown": "Unknown"
|
||||
"unknown": "Unknown",
|
||||
"yes": "Yes"
|
||||
},
|
||||
"languages": {
|
||||
"english": "English",
|
||||
@@ -639,6 +645,7 @@
|
||||
"schedule": "Schedule",
|
||||
"shop": "My Shop",
|
||||
"shop_config": "Configuration",
|
||||
"shop_templates": "Templates",
|
||||
"shop_vendors": "Vendors",
|
||||
"vehicles": "Vehicles"
|
||||
},
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
},
|
||||
"bodyshop": {
|
||||
"actions": {
|
||||
"addtemplate": "",
|
||||
"newstatus": ""
|
||||
},
|
||||
"errors": {
|
||||
@@ -129,7 +130,9 @@
|
||||
"labels": {
|
||||
"alljobstatuses": "",
|
||||
"allopenjobstatuses": "",
|
||||
"customtemplates": "",
|
||||
"jobstatuses": "",
|
||||
"notemplatesavailable": "",
|
||||
"orderstatuses": "",
|
||||
"responsibilitycenters": {
|
||||
"costs": "",
|
||||
@@ -305,6 +308,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"actions": "Comportamiento",
|
||||
"areyousure": "",
|
||||
"barcode": "código de barras",
|
||||
"in": "en",
|
||||
"loading": "Cargando...",
|
||||
@@ -312,9 +316,11 @@
|
||||
"loadingshop": "Cargando datos de la tienda ...",
|
||||
"loggingin": "Iniciando sesión ...",
|
||||
"na": "N / A",
|
||||
"no": "",
|
||||
"out": "Afuera",
|
||||
"search": "Buscar...",
|
||||
"unknown": "Desconocido"
|
||||
"unknown": "Desconocido",
|
||||
"yes": ""
|
||||
},
|
||||
"languages": {
|
||||
"english": "Inglés",
|
||||
@@ -639,6 +645,7 @@
|
||||
"schedule": "Programar",
|
||||
"shop": "Mi tienda",
|
||||
"shop_config": "Configuración",
|
||||
"shop_templates": "",
|
||||
"shop_vendors": "Vendedores",
|
||||
"vehicles": "Vehículos"
|
||||
},
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
},
|
||||
"bodyshop": {
|
||||
"actions": {
|
||||
"addtemplate": "",
|
||||
"newstatus": ""
|
||||
},
|
||||
"errors": {
|
||||
@@ -129,7 +130,9 @@
|
||||
"labels": {
|
||||
"alljobstatuses": "",
|
||||
"allopenjobstatuses": "",
|
||||
"customtemplates": "",
|
||||
"jobstatuses": "",
|
||||
"notemplatesavailable": "",
|
||||
"orderstatuses": "",
|
||||
"responsibilitycenters": {
|
||||
"costs": "",
|
||||
@@ -305,6 +308,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"actions": "actes",
|
||||
"areyousure": "",
|
||||
"barcode": "code à barre",
|
||||
"in": "dans",
|
||||
"loading": "Chargement...",
|
||||
@@ -312,9 +316,11 @@
|
||||
"loadingshop": "Chargement des données de la boutique ...",
|
||||
"loggingin": "Vous connecter ...",
|
||||
"na": "N / A",
|
||||
"no": "",
|
||||
"out": "En dehors",
|
||||
"search": "Chercher...",
|
||||
"unknown": "Inconnu"
|
||||
"unknown": "Inconnu",
|
||||
"yes": ""
|
||||
},
|
||||
"languages": {
|
||||
"english": "Anglais",
|
||||
@@ -639,6 +645,7 @@
|
||||
"schedule": "Programme",
|
||||
"shop": "Mon magasin",
|
||||
"shop_config": "Configuration",
|
||||
"shop_templates": "",
|
||||
"shop_vendors": "Vendeurs",
|
||||
"vehicles": "Véhicules"
|
||||
},
|
||||
|
||||
@@ -12,9 +12,12 @@ export default async function RenderTemplate(templateObject, client, bodyshop) {
|
||||
let templateToUse;
|
||||
|
||||
if (templateRecords.templates.length === 1) {
|
||||
console.log("[ITE] Using OOTB template.");
|
||||
templateToUse = templateRecords.templates[0];
|
||||
} else if (templateRecords.templates.length === 2) {
|
||||
templateToUse = templateRecords.templates.filter((t) => !!t.bodyshopid);
|
||||
console.log("[ITE] Found custom template.");
|
||||
templateToUse = templateRecords.templates.filter((t) => !!t.bodyshopid)[0];
|
||||
console.log("templateToUse", templateToUse);
|
||||
} else {
|
||||
//No template found.Uh oh.
|
||||
alert("Template key does not exist.");
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
export const EmailSettings = {
|
||||
fromNameDefault: "Bodyshop.app",
|
||||
fromAddress: "noreply@bodyshop.app"
|
||||
fromAddress: "noreply@bodyshop.app",
|
||||
};
|
||||
|
||||
export const TemplateList = {
|
||||
appointment_reminder: {
|
||||
title: "Appointment Reminder",
|
||||
description: "Sent to a customer as a reminder of an upcoming appointment.",
|
||||
drivingId: "Appointment Id",
|
||||
},
|
||||
parts_order_confirmation: {
|
||||
title: "Parts Order Confirmation",
|
||||
description: "Parts order template including part details",
|
||||
drivingId: "Parts order Id",
|
||||
},
|
||||
test_Template: {
|
||||
title: "Test",
|
||||
description: "Test - does nto exist.",
|
||||
drivingId: "test does not exist.",
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user