- Merge client update into test-beta
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { Tooltip } from "antd";
|
||||
import moment from "moment";
|
||||
import dayjs from "../utils/day";
|
||||
import React from "react";
|
||||
|
||||
export function DateFormatter(props) {
|
||||
return props.children
|
||||
? moment(props.children).format(
|
||||
? dayjs(props.children).format(
|
||||
props.includeDay ? "ddd MM/DD/YYYY" : "MM/DD/YYYY"
|
||||
)
|
||||
: null;
|
||||
@@ -12,19 +12,19 @@ export function DateFormatter(props) {
|
||||
|
||||
export function DateTimeFormatter(props) {
|
||||
return props.children
|
||||
? moment(props.children).format(
|
||||
? dayjs(props.children).format(
|
||||
props.format ? props.format : "MM/DD/YYYY hh:mm a"
|
||||
)
|
||||
: null;
|
||||
}
|
||||
export function TimeFormatter(props) {
|
||||
return props.children
|
||||
? moment(props.children).format(props.format ? props.format : "hh:mm a")
|
||||
? dayjs(props.children).format(props.format ? props.format : "hh:mm a")
|
||||
: null;
|
||||
}
|
||||
|
||||
export function TimeAgoFormatter(props) {
|
||||
const m = moment(props.children);
|
||||
const m = dayjs(props.children);
|
||||
return props.children ? (
|
||||
<Tooltip placement="top" title={m.format("MM/DD/YYY hh:mm A")}>
|
||||
{m.fromNow()}
|
||||
@@ -33,5 +33,5 @@ export function TimeAgoFormatter(props) {
|
||||
}
|
||||
|
||||
export function DateTimeFormat(value) {
|
||||
return moment(value).format("MM/DD/YYYY hh:mm A");
|
||||
return dayjs(value).format("MM/DD/YYYY hh:mm A");
|
||||
}
|
||||
|
||||
@@ -1,27 +1,62 @@
|
||||
import moment from "moment";
|
||||
const range = {
|
||||
Today: [moment(), moment()],
|
||||
"Last 14 days": [moment().subtract(14, "days"), moment()],
|
||||
"Last 7 days": [moment().subtract(7, "days"), moment()],
|
||||
"Next 7 days": [moment(), moment().add(7, "days")],
|
||||
"Next 14 days": [moment(), moment().add(14, "days")],
|
||||
"Last Month": [
|
||||
moment().startOf("month").subtract(1, "month"),
|
||||
moment().startOf("month").subtract(1, "month").endOf("month"),
|
||||
],
|
||||
"This Month": [moment().startOf("month"), moment().endOf("month")],
|
||||
"Next Month": [
|
||||
moment().startOf("month").add(1, "month"),
|
||||
moment().startOf("month").add(1, "month").endOf("month"),
|
||||
],
|
||||
"Last Quarter": [
|
||||
moment().startOf("quarter").subtract(1, "quarters"),
|
||||
moment().startOf("quarter").subtract(1, "day"),
|
||||
],
|
||||
"This Quarter": [
|
||||
moment().startOf("quarter"),
|
||||
moment().startOf("quarter").add(1, "quarter").subtract(1, "day"),
|
||||
],
|
||||
"Last 90 Days": [moment().add(-90, "days"), moment()],
|
||||
};
|
||||
import dayjs from "./day";
|
||||
|
||||
const range = [
|
||||
{
|
||||
label: 'Today',
|
||||
value: [dayjs(), dayjs()]
|
||||
},
|
||||
{
|
||||
label: 'Last 14 days',
|
||||
value: [dayjs().subtract(14, "day"), dayjs()]
|
||||
},
|
||||
{
|
||||
label: 'Last 7 days',
|
||||
value: [dayjs().subtract(7, "day"), dayjs()]
|
||||
},
|
||||
{
|
||||
label: 'Next 7 days',
|
||||
value: [dayjs(), dayjs().add(7, "day")]
|
||||
},
|
||||
{
|
||||
label: 'Next 14 days',
|
||||
value: [dayjs(), dayjs().add(14, "day")],
|
||||
},
|
||||
{
|
||||
label: 'Last Month',
|
||||
value: [
|
||||
dayjs().startOf("month").subtract(1, "month"),
|
||||
dayjs().startOf("month").subtract(1, "month").endOf("month"),
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'This Month',
|
||||
value: [dayjs().startOf("month"), dayjs().endOf("month")]
|
||||
},
|
||||
{
|
||||
label: 'Next Month',
|
||||
value: [
|
||||
dayjs().startOf("month").add(1, "month"),
|
||||
dayjs().startOf("month").add(1, "month").endOf("month"),
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Last Quarter',
|
||||
value: [
|
||||
dayjs().startOf("quarter").subtract(1, "quarter"),
|
||||
dayjs().startOf("quarter").subtract(1, "day"),
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'This Quarter',
|
||||
value: [
|
||||
dayjs().startOf("quarter"),
|
||||
dayjs().startOf("quarter").add(1, "quarter").subtract(1, "day"),
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Last 90 Days',
|
||||
value: [dayjs().add(-90, "day"), dayjs()],
|
||||
}
|
||||
]
|
||||
|
||||
export default range;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import {gql} from "@apollo/client";
|
||||
import jsreport from "@jsreport/browser-client";
|
||||
import { notification } from "antd";
|
||||
import {notification} from "antd";
|
||||
import axios from "axios";
|
||||
import _ from "lodash";
|
||||
import { auth } from "../firebase/firebase.utils";
|
||||
import { setEmailOptions } from "../redux/email/email.actions";
|
||||
import { store } from "../redux/store";
|
||||
import {auth} from "../firebase/firebase.utils";
|
||||
import {setEmailOptions} from "../redux/email/email.actions";
|
||||
import {store} from "../redux/store";
|
||||
import client from "../utils/GraphQLClient";
|
||||
import cleanAxios from "./CleanAxios";
|
||||
import { TemplateList } from "./TemplateConstants";
|
||||
import {TemplateList} from "./TemplateConstants";
|
||||
|
||||
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
|
||||
|
||||
jsreport.serverUrl = server;
|
||||
@@ -16,343 +17,343 @@ jsreport.serverUrl = server;
|
||||
const Templates = TemplateList();
|
||||
|
||||
export default async function RenderTemplate(
|
||||
templateObject,
|
||||
bodyshop,
|
||||
renderAsHtml = false,
|
||||
renderAsExcel = false,
|
||||
renderAsText = false
|
||||
) {
|
||||
if (window.jsr3) {
|
||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||
}
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
|
||||
jsreport.headers["Authorization"] = jsrAuth;
|
||||
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
||||
templateObject,
|
||||
jsrAuth
|
||||
);
|
||||
|
||||
const { ignoreCustomMargins } = Templates[templateObject.name];
|
||||
|
||||
let reportRequest = {
|
||||
template: {
|
||||
name: useShopSpecificTemplate
|
||||
? `/${bodyshop.imexshopid}/${templateObject.name}`
|
||||
: `/${templateObject.name}`,
|
||||
...(renderAsHtml
|
||||
? {}
|
||||
: {
|
||||
recipe: "chrome-pdf",
|
||||
...(!ignoreCustomMargins && {
|
||||
chrome: {
|
||||
marginTop:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.headerMargin &&
|
||||
bodyshop.logo_img_path.headerMargin > 36
|
||||
? bodyshop.logo_img_path.headerMargin
|
||||
: "36px",
|
||||
marginBottom:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.footerMargin &&
|
||||
bodyshop.logo_img_path.footerMargin > 50
|
||||
? bodyshop.logo_img_path.footerMargin
|
||||
: "50px",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
...(renderAsExcel ? { recipe: "html-to-xlsx" } : {}),
|
||||
...(renderAsText ? { recipe: "text" } : {}),
|
||||
},
|
||||
data: {
|
||||
...contextData,
|
||||
...templateObject.variables,
|
||||
...templateObject.context,
|
||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
||||
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
||||
bodyshop: bodyshop,
|
||||
offset: bodyshop.timezone, //moment().utcOffset(),
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const render = await jsreport.render(reportRequest);
|
||||
|
||||
if (!renderAsHtml) {
|
||||
render.download(
|
||||
(Templates[templateObject.name] &&
|
||||
Templates[templateObject.name].title) ||
|
||||
""
|
||||
);
|
||||
} else {
|
||||
let pdf;
|
||||
if (bodyshop.attach_pdf_to_email) {
|
||||
const pdfRequest = _.cloneDeep(reportRequest); //Updates to spread in the header details.
|
||||
pdfRequest.template = {
|
||||
...pdfRequest.template,
|
||||
...{
|
||||
recipe: "chrome-pdf",
|
||||
...(!ignoreCustomMargins && {
|
||||
chrome: {
|
||||
marginTop:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.headerMargin &&
|
||||
bodyshop.logo_img_path.headerMargin > 36
|
||||
? bodyshop.logo_img_path.headerMargin
|
||||
: "36px",
|
||||
marginBottom:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.footerMargin &&
|
||||
bodyshop.logo_img_path.footerMargin > 50
|
||||
? bodyshop.logo_img_path.footerMargin
|
||||
: "50px",
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const pdfRender = await jsreport.render(pdfRequest);
|
||||
pdf = await pdfRender.toDataURI();
|
||||
}
|
||||
const html = await render.toString();
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve({
|
||||
pdf,
|
||||
filename:
|
||||
Templates[templateObject.name] &&
|
||||
Templates[templateObject.name].title,
|
||||
html,
|
||||
});
|
||||
});
|
||||
bodyshop,
|
||||
renderAsHtml = false,
|
||||
renderAsExcel = false,
|
||||
renderAsText = false
|
||||
) {
|
||||
if (window.jsr3) {
|
||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||
}
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
|
||||
jsreport.headers["Authorization"] = jsrAuth;
|
||||
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let {contextData, useShopSpecificTemplate} = await fetchContextData(
|
||||
templateObject,
|
||||
jsrAuth
|
||||
);
|
||||
|
||||
const {ignoreCustomMargins} = Templates[templateObject.name];
|
||||
|
||||
let reportRequest = {
|
||||
template: {
|
||||
name: useShopSpecificTemplate
|
||||
? `/${bodyshop.imexshopid}/${templateObject.name}`
|
||||
: `/${templateObject.name}`,
|
||||
...(renderAsHtml
|
||||
? {}
|
||||
: {
|
||||
recipe: "chrome-pdf",
|
||||
...(!ignoreCustomMargins && {
|
||||
chrome: {
|
||||
marginTop:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.headerMargin &&
|
||||
bodyshop.logo_img_path.headerMargin > 36
|
||||
? bodyshop.logo_img_path.headerMargin
|
||||
: "36px",
|
||||
marginBottom:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.footerMargin &&
|
||||
bodyshop.logo_img_path.footerMargin > 50
|
||||
? bodyshop.logo_img_path.footerMargin
|
||||
: "50px",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
...(renderAsExcel ? {recipe: "html-to-xlsx"} : {}),
|
||||
...(renderAsText ? {recipe: "text"} : {}),
|
||||
},
|
||||
data: {
|
||||
...contextData,
|
||||
...templateObject.variables,
|
||||
...templateObject.context,
|
||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
||||
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
||||
bodyshop: bodyshop,
|
||||
offset: bodyshop.timezone, //dayjs().utcOffset(),
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const render = await jsreport.render(reportRequest);
|
||||
|
||||
if (!renderAsHtml) {
|
||||
render.download(
|
||||
(Templates[templateObject.name] &&
|
||||
Templates[templateObject.name].title) ||
|
||||
""
|
||||
);
|
||||
} else {
|
||||
let pdf;
|
||||
if (bodyshop.attach_pdf_to_email) {
|
||||
const pdfRequest = _.cloneDeep(reportRequest); //Updates to spread in the header details.
|
||||
pdfRequest.template = {
|
||||
...pdfRequest.template,
|
||||
...{
|
||||
recipe: "chrome-pdf",
|
||||
...(!ignoreCustomMargins && {
|
||||
chrome: {
|
||||
marginTop:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.headerMargin &&
|
||||
bodyshop.logo_img_path.headerMargin > 36
|
||||
? bodyshop.logo_img_path.headerMargin
|
||||
: "36px",
|
||||
marginBottom:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.footerMargin &&
|
||||
bodyshop.logo_img_path.footerMargin > 50
|
||||
? bodyshop.logo_img_path.footerMargin
|
||||
: "50px",
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const pdfRender = await jsreport.render(pdfRequest);
|
||||
pdf = await pdfRender.toDataURI();
|
||||
}
|
||||
const html = await render.toString();
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve({
|
||||
pdf,
|
||||
filename:
|
||||
Templates[templateObject.name] &&
|
||||
Templates[templateObject.name].title,
|
||||
html,
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification["error"]({message: JSON.stringify(error)});
|
||||
}
|
||||
} catch (error) {
|
||||
notification["error"]({ message: JSON.stringify(error) });
|
||||
}
|
||||
}
|
||||
|
||||
export async function RenderTemplates(
|
||||
templateObjects,
|
||||
bodyshop,
|
||||
renderAsHtml = false
|
||||
templateObjects,
|
||||
bodyshop,
|
||||
renderAsHtml = false
|
||||
) {
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let unsortedTemplatesAndData = [];
|
||||
let proms = [];
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
jsreport.headers["Authorization"] = jsrAuth;
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let unsortedTemplatesAndData = [];
|
||||
let proms = [];
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
jsreport.headers["Authorization"] = jsrAuth;
|
||||
|
||||
templateObjects.forEach((template) => {
|
||||
proms.push(
|
||||
(async () => {
|
||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
||||
template,
|
||||
jsrAuth
|
||||
templateObjects.forEach((template) => {
|
||||
proms.push(
|
||||
(async () => {
|
||||
let {contextData, useShopSpecificTemplate} = await fetchContextData(
|
||||
template,
|
||||
jsrAuth
|
||||
);
|
||||
unsortedTemplatesAndData.push({
|
||||
templateObject: template,
|
||||
contextData,
|
||||
useShopSpecificTemplate,
|
||||
});
|
||||
})()
|
||||
);
|
||||
unsortedTemplatesAndData.push({
|
||||
templateObject: template,
|
||||
contextData,
|
||||
useShopSpecificTemplate,
|
||||
});
|
||||
})()
|
||||
);
|
||||
});
|
||||
await Promise.all(proms);
|
||||
});
|
||||
await Promise.all(proms);
|
||||
|
||||
//Re-order list to follow speedprint.
|
||||
// var templateAndData = _.sortBy(unsortedTemplatesAndData, function (item) {
|
||||
// return templateObjects.findIndex(
|
||||
// (template) => template.name === item.templateObject.name
|
||||
// );
|
||||
// });
|
||||
if (window.jsr3) {
|
||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||
}
|
||||
|
||||
unsortedTemplatesAndData.sort(function (a, b) {
|
||||
return (
|
||||
templateObjects.findIndex((x) => x.name === a.templateObject.name) -
|
||||
templateObjects.findIndex((x) => x.name === b.templateObject.name)
|
||||
);
|
||||
});
|
||||
const templateAndData = unsortedTemplatesAndData;
|
||||
|
||||
let rootTemplate = templateAndData.shift();
|
||||
|
||||
let reportRequest = {
|
||||
template: {
|
||||
name: rootTemplate.useShopSpecificTemplate
|
||||
? `/${bodyshop.imexshopid}/${rootTemplate.templateObject.name}`
|
||||
: `/${rootTemplate.templateObject.name}`,
|
||||
...(renderAsHtml
|
||||
? {}
|
||||
: {
|
||||
recipe: "chrome-pdf",
|
||||
chrome: {
|
||||
marginTop:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.headerMargin &&
|
||||
bodyshop.logo_img_path.headerMargin > 36
|
||||
? bodyshop.logo_img_path.headerMargin
|
||||
: "36px",
|
||||
marginBottom:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.footerMargin &&
|
||||
bodyshop.logo_img_path.footerMargin > 50
|
||||
? bodyshop.logo_img_path.footerMargin
|
||||
: "50px",
|
||||
},
|
||||
}),
|
||||
pdfOperations: [
|
||||
{
|
||||
template: {
|
||||
name: "/components/Header-Footer",
|
||||
recipe: "chrome-pdf",
|
||||
engine: "handlebars",
|
||||
},
|
||||
type: "merge",
|
||||
},
|
||||
...templateAndData.map((template) => {
|
||||
return {
|
||||
template: {
|
||||
chrome: {
|
||||
marginTop:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.headerMargin &&
|
||||
bodyshop.logo_img_path.headerMargin > 36
|
||||
? bodyshop.logo_img_path.headerMargin
|
||||
: "36px",
|
||||
marginBottom:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.footerMargin &&
|
||||
bodyshop.logo_img_path.footerMargin > 50
|
||||
? bodyshop.logo_img_path.footerMargin
|
||||
: "50px",
|
||||
},
|
||||
name: template.useShopSpecificTemplate
|
||||
? `/${bodyshop.imexshopid}/${template.templateObject.name}`
|
||||
: `/${template.templateObject.name}`,
|
||||
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
|
||||
},
|
||||
type: "append",
|
||||
|
||||
// mergeWholeDocument: true,
|
||||
// renderForEveryPage: true,
|
||||
};
|
||||
}),
|
||||
],
|
||||
},
|
||||
data: {
|
||||
...extend(
|
||||
rootTemplate.contextData,
|
||||
...templateAndData.map((temp) => temp.contextData)
|
||||
),
|
||||
|
||||
// ...rootTemplate.templateObject.variables,
|
||||
// ...rootTemplate.templateObject.context,
|
||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
||||
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
||||
bodyshop: bodyshop,
|
||||
offset: bodyshop.timezone,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const render = await jsreport.render(reportRequest);
|
||||
if (!renderAsHtml) {
|
||||
render.download("Speed Print");
|
||||
} else {
|
||||
return render.toString();
|
||||
//Re-order list to follow speedprint.
|
||||
// var templateAndData = _.sortBy(unsortedTemplatesAndData, function (item) {
|
||||
// return templateObjects.findIndex(
|
||||
// (template) => template.name === item.templateObject.name
|
||||
// );
|
||||
// });
|
||||
if (window.jsr3) {
|
||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||
}
|
||||
|
||||
unsortedTemplatesAndData.sort(function (a, b) {
|
||||
return (
|
||||
templateObjects.findIndex((x) => x.name === a.templateObject.name) -
|
||||
templateObjects.findIndex((x) => x.name === b.templateObject.name)
|
||||
);
|
||||
});
|
||||
const templateAndData = unsortedTemplatesAndData;
|
||||
|
||||
let rootTemplate = templateAndData.shift();
|
||||
|
||||
let reportRequest = {
|
||||
template: {
|
||||
name: rootTemplate.useShopSpecificTemplate
|
||||
? `/${bodyshop.imexshopid}/${rootTemplate.templateObject.name}`
|
||||
: `/${rootTemplate.templateObject.name}`,
|
||||
...(renderAsHtml
|
||||
? {}
|
||||
: {
|
||||
recipe: "chrome-pdf",
|
||||
chrome: {
|
||||
marginTop:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.headerMargin &&
|
||||
bodyshop.logo_img_path.headerMargin > 36
|
||||
? bodyshop.logo_img_path.headerMargin
|
||||
: "36px",
|
||||
marginBottom:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.footerMargin &&
|
||||
bodyshop.logo_img_path.footerMargin > 50
|
||||
? bodyshop.logo_img_path.footerMargin
|
||||
: "50px",
|
||||
},
|
||||
}),
|
||||
pdfOperations: [
|
||||
{
|
||||
template: {
|
||||
name: "/components/Header-Footer",
|
||||
recipe: "chrome-pdf",
|
||||
engine: "handlebars",
|
||||
},
|
||||
type: "merge",
|
||||
},
|
||||
...templateAndData.map((template) => {
|
||||
return {
|
||||
template: {
|
||||
chrome: {
|
||||
marginTop:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.headerMargin &&
|
||||
bodyshop.logo_img_path.headerMargin > 36
|
||||
? bodyshop.logo_img_path.headerMargin
|
||||
: "36px",
|
||||
marginBottom:
|
||||
bodyshop.logo_img_path &&
|
||||
bodyshop.logo_img_path.footerMargin &&
|
||||
bodyshop.logo_img_path.footerMargin > 50
|
||||
? bodyshop.logo_img_path.footerMargin
|
||||
: "50px",
|
||||
},
|
||||
name: template.useShopSpecificTemplate
|
||||
? `/${bodyshop.imexshopid}/${template.templateObject.name}`
|
||||
: `/${template.templateObject.name}`,
|
||||
...(renderAsHtml ? {} : {recipe: "chrome-pdf"}),
|
||||
},
|
||||
type: "append",
|
||||
|
||||
// mergeWholeDocument: true,
|
||||
// renderForEveryPage: true,
|
||||
};
|
||||
}),
|
||||
],
|
||||
},
|
||||
data: {
|
||||
...extend(
|
||||
rootTemplate.contextData,
|
||||
...templateAndData.map((temp) => temp.contextData)
|
||||
),
|
||||
|
||||
// ...rootTemplate.templateObject.variables,
|
||||
// ...rootTemplate.templateObject.context,
|
||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
||||
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
||||
bodyshop: bodyshop,
|
||||
offset: bodyshop.timezone,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const render = await jsreport.render(reportRequest);
|
||||
if (!renderAsHtml) {
|
||||
render.download("Speed Print");
|
||||
} else {
|
||||
return render.toString();
|
||||
}
|
||||
} catch (error) {
|
||||
notification["error"]({message: JSON.stringify(error)});
|
||||
}
|
||||
} catch (error) {
|
||||
notification["error"]({ message: JSON.stringify(error) });
|
||||
}
|
||||
}
|
||||
|
||||
export const GenerateDocument = async (
|
||||
template,
|
||||
messageOptions,
|
||||
sendType,
|
||||
jobid
|
||||
template,
|
||||
messageOptions,
|
||||
sendType,
|
||||
jobid
|
||||
) => {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
if (sendType === "e") {
|
||||
store.dispatch(
|
||||
setEmailOptions({
|
||||
jobid,
|
||||
messageOptions: {
|
||||
...messageOptions,
|
||||
to: Array.isArray(messageOptions.to)
|
||||
? messageOptions.to
|
||||
: [messageOptions.to],
|
||||
},
|
||||
template,
|
||||
})
|
||||
);
|
||||
} else if (sendType === "x") {
|
||||
console.log("excel");
|
||||
await RenderTemplate(template, bodyshop, false, true);
|
||||
} else if (sendType === "text") {
|
||||
await RenderTemplate(template, bodyshop, false, false, true);
|
||||
} else {
|
||||
await RenderTemplate(template, bodyshop);
|
||||
}
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
if (sendType === "e") {
|
||||
store.dispatch(
|
||||
setEmailOptions({
|
||||
jobid,
|
||||
messageOptions: {
|
||||
...messageOptions,
|
||||
to: Array.isArray(messageOptions.to)
|
||||
? messageOptions.to
|
||||
: [messageOptions.to],
|
||||
},
|
||||
template,
|
||||
})
|
||||
);
|
||||
} else if (sendType === "x") {
|
||||
console.log("excel");
|
||||
await RenderTemplate(template, bodyshop, false, true);
|
||||
} else if (sendType === "text") {
|
||||
await RenderTemplate(template, bodyshop, false, false, true);
|
||||
} else {
|
||||
await RenderTemplate(template, bodyshop);
|
||||
}
|
||||
};
|
||||
|
||||
export const GenerateDocuments = async (templates) => {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
await RenderTemplates(templates, bodyshop);
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
await RenderTemplates(templates, bodyshop);
|
||||
};
|
||||
|
||||
const fetchContextData = async (templateObject, jsrAuth) => {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
|
||||
jsreport.headers["FirebaseAuthorization"] =
|
||||
"Bearer " + (await auth.currentUser.getIdToken());
|
||||
jsreport.headers["FirebaseAuthorization"] =
|
||||
"Bearer " + (await auth.currentUser.getIdToken());
|
||||
|
||||
const folders = await cleanAxios.get(`${server}/odata/folders`, {
|
||||
headers: { Authorization: jsrAuth },
|
||||
});
|
||||
const shopSpecificFolder = folders.data.value.find(
|
||||
(f) => f.name === bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const jsReportQueries = await cleanAxios.get(
|
||||
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
|
||||
{ headers: { Authorization: jsrAuth } }
|
||||
);
|
||||
|
||||
let templateQueryToExecute;
|
||||
let useShopSpecificTemplate = false;
|
||||
// let shopSpecificTemplate;
|
||||
|
||||
if (shopSpecificFolder) {
|
||||
let shopSpecificTemplate = jsReportQueries.data.value.find(
|
||||
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
|
||||
);
|
||||
if (shopSpecificTemplate) {
|
||||
useShopSpecificTemplate = true;
|
||||
templateQueryToExecute = atob(shopSpecificTemplate.content);
|
||||
}
|
||||
}
|
||||
|
||||
if (!templateQueryToExecute) {
|
||||
const generalTemplate = jsReportQueries.data.value.find((f) => !f.folder);
|
||||
useShopSpecificTemplate = false;
|
||||
templateQueryToExecute = atob(generalTemplate.content);
|
||||
}
|
||||
|
||||
let contextData = {};
|
||||
if (templateQueryToExecute) {
|
||||
const { data } = await client.query({
|
||||
query: gql(templateQueryToExecute),
|
||||
variables: { ...templateObject.variables },
|
||||
const folders = await cleanAxios.get(`${server}/odata/folders`, {
|
||||
headers: {Authorization: jsrAuth},
|
||||
});
|
||||
contextData = data;
|
||||
}
|
||||
const shopSpecificFolder = folders.data.value.find(
|
||||
(f) => f.name === bodyshop.imexshopid
|
||||
);
|
||||
|
||||
return { contextData, useShopSpecificTemplate };
|
||||
const jsReportQueries = await cleanAxios.get(
|
||||
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
|
||||
{headers: {Authorization: jsrAuth}}
|
||||
);
|
||||
|
||||
let templateQueryToExecute;
|
||||
let useShopSpecificTemplate = false;
|
||||
// let shopSpecificTemplate;
|
||||
|
||||
if (shopSpecificFolder) {
|
||||
let shopSpecificTemplate = jsReportQueries.data.value.find(
|
||||
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
|
||||
);
|
||||
if (shopSpecificTemplate) {
|
||||
useShopSpecificTemplate = true;
|
||||
templateQueryToExecute = atob(shopSpecificTemplate.content);
|
||||
}
|
||||
}
|
||||
|
||||
if (!templateQueryToExecute) {
|
||||
const generalTemplate = jsReportQueries.data.value.find((f) => !f.folder);
|
||||
useShopSpecificTemplate = false;
|
||||
templateQueryToExecute = atob(generalTemplate.content);
|
||||
}
|
||||
|
||||
let contextData = {};
|
||||
if (templateQueryToExecute) {
|
||||
const {data} = await client.query({
|
||||
query: gql(templateQueryToExecute),
|
||||
variables: {...templateObject.variables},
|
||||
});
|
||||
contextData = data;
|
||||
}
|
||||
|
||||
return {contextData, useShopSpecificTemplate};
|
||||
};
|
||||
|
||||
//export const displayTemplateInWindow = (html) => {
|
||||
@@ -388,21 +389,21 @@ const fetchContextData = async (templateObject, jsrAuth) => {
|
||||
// };
|
||||
|
||||
function extend(o1, o2, o3) {
|
||||
var result = {},
|
||||
obj;
|
||||
var result = {},
|
||||
obj;
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
obj = arguments[i];
|
||||
for (var key in obj) {
|
||||
if (Object.prototype.toString.call(obj[key]) === "[object Object]") {
|
||||
if (typeof result[key] === "undefined") {
|
||||
result[key] = {};
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
obj = arguments[i];
|
||||
for (var key in obj) {
|
||||
if (Object.prototype.toString.call(obj[key]) === "[object Object]") {
|
||||
if (typeof result[key] === "undefined") {
|
||||
result[key] = {};
|
||||
}
|
||||
result[key] = extend(result[key], obj[key]);
|
||||
} else {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
result[key] = extend(result[key], obj[key]);
|
||||
} else {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
37
client/src/utils/betaHandler.js
Normal file
37
client/src/utils/betaHandler.js
Normal file
@@ -0,0 +1,37 @@
|
||||
export const BETA_KEY = 'betaSwitchImex';
|
||||
|
||||
export const checkBeta = () => {
|
||||
const cookie = document.cookie.split('; ').find(row => row.startsWith(BETA_KEY));
|
||||
return cookie ? cookie.split('=')[1] === 'true' : false;
|
||||
}
|
||||
|
||||
|
||||
export const setBeta = (value) => {
|
||||
const domain = window.location.hostname.split('.').slice(-2).join('.');
|
||||
document.cookie = `${BETA_KEY}=${value}; path=/; domain=.${domain}`;
|
||||
}
|
||||
|
||||
export const handleBeta = () => {
|
||||
// If the current host name does not start with beta or test, then we don't need to do anything.
|
||||
if (window.location.hostname.startsWith('localhost')) {
|
||||
console.log('Not on beta or test, so no need to handle beta.');
|
||||
return;
|
||||
}
|
||||
|
||||
const isBeta = checkBeta();
|
||||
|
||||
const currentHostName = window.location.hostname;
|
||||
|
||||
// Beta is enabled, but the current host name does start with beta.
|
||||
if (isBeta && !currentHostName.startsWith('beta')) {
|
||||
const href = `${window.location.protocol}//beta.${currentHostName}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
||||
window.location.replace(href);
|
||||
}
|
||||
|
||||
// Beta is not enabled, but the current host name does start with beta.
|
||||
else if (!isBeta && currentHostName.startsWith('beta')) {
|
||||
const href = `${window.location.protocol}//${currentHostName.replace('beta.', '')}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
||||
window.location.replace(href);
|
||||
}
|
||||
}
|
||||
export default handleBeta;
|
||||
73
client/src/utils/day.js
Normal file
73
client/src/utils/day.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import dayjsBusinessDays from "dayjs-business-days2";
|
||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
|
||||
import updateLocale from 'dayjs/plugin/updateLocale';
|
||||
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import minMax from 'dayjs/plugin/minMax';
|
||||
import isBetween from 'dayjs/plugin/isBetween';
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
||||
import pluralGetSet from 'dayjs/plugin/pluralGetSet';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import arraySupport from 'dayjs/plugin/arraySupport';
|
||||
import calendar from 'dayjs/plugin/calendar';
|
||||
import dayOfYear from 'dayjs/plugin/dayOfYear';
|
||||
import weekday from 'dayjs/plugin/weekday';
|
||||
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||
import weekYear from 'dayjs/plugin/weekYear';
|
||||
import isoWeek from 'dayjs/plugin/isoWeek';
|
||||
import isoWeeksInYear from 'dayjs/plugin/isoWeeksInYear';
|
||||
import isLeapYear from 'dayjs/plugin/isLeapYear';
|
||||
import localeData from 'dayjs/plugin/localeData';
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
import quarterOfYear from 'dayjs/plugin/quarterOfYear';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import isToday from 'dayjs/plugin/isToday';
|
||||
import isTomorrow from 'dayjs/plugin/isTomorrow';
|
||||
import isYesterday from 'dayjs/plugin/isYesterday';
|
||||
import objectSupport from 'dayjs/plugin/objectSupport';
|
||||
import toArray from 'dayjs/plugin/toArray';
|
||||
import toObject from 'dayjs/plugin/toObject';
|
||||
|
||||
// import badMutable from 'dayjs/plugin/badMutable';
|
||||
// import preParsePostFormat from 'dayjs/plugin/preParsePostFormat';
|
||||
|
||||
|
||||
// dayjs.extend(badMutable); // TODO: Client Update - This is not advised, scoreboard page
|
||||
dayjs.extend(toObject);
|
||||
dayjs.extend(toArray);
|
||||
dayjs.extend(objectSupport);
|
||||
dayjs.extend(isYesterday);
|
||||
dayjs.extend(isTomorrow);
|
||||
dayjs.extend(isToday);
|
||||
dayjs.extend(localeData);
|
||||
dayjs.extend(quarterOfYear);
|
||||
dayjs.extend(localizedFormat);
|
||||
// dayjs.extend(preParsePostFormat); // TODO: This should not be needed
|
||||
dayjs.extend(isLeapYear);
|
||||
dayjs.extend(isoWeeksInYear);
|
||||
dayjs.extend(isoWeek);
|
||||
dayjs.extend(weekYear);
|
||||
dayjs.extend(weekOfYear);
|
||||
dayjs.extend(weekday);
|
||||
dayjs.extend(dayOfYear);
|
||||
dayjs.extend(calendar);
|
||||
dayjs.extend(arraySupport);
|
||||
dayjs.extend(advancedFormat);
|
||||
dayjs.extend(duration);
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(pluralGetSet);
|
||||
dayjs.extend(customParseFormat);
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(updateLocale);
|
||||
dayjs.extend(isSameOrAfter);
|
||||
dayjs.extend(isSameOrBefore);
|
||||
dayjs.extend(minMax);
|
||||
dayjs.extend(isBetween);
|
||||
dayjs.extend(dayjsBusinessDays);
|
||||
|
||||
export default dayjs;
|
||||
37
client/src/utils/handleBeta.js
Normal file
37
client/src/utils/handleBeta.js
Normal file
@@ -0,0 +1,37 @@
|
||||
export const BETA_KEY = 'betaSwitchImex';
|
||||
|
||||
export const checkBeta = () => {
|
||||
const cookie = document.cookie.split('; ').find(row => row.startsWith(BETA_KEY));
|
||||
return cookie ? cookie.split('=')[1] === 'true' : false;
|
||||
}
|
||||
|
||||
|
||||
export const setBeta = (value) => {
|
||||
const domain = window.location.hostname.split('.').slice(-2).join('.');
|
||||
document.cookie = `${BETA_KEY}=${value}; path=/; domain=.${domain}`;
|
||||
}
|
||||
|
||||
export const handleBeta = () => {
|
||||
// If the current host name does not start with beta or test, then we don't need to do anything.
|
||||
if (window.location.hostname.startsWith('localhost')) {
|
||||
console.log('Not on beta or test, so no need to handle beta.');
|
||||
return;
|
||||
}
|
||||
|
||||
const isBeta = checkBeta();
|
||||
|
||||
const currentHostName = window.location.hostname;
|
||||
|
||||
// Beta is enabled, but the current host name does start with beta.
|
||||
if (isBeta && !currentHostName.startsWith('beta')) {
|
||||
const href= `${window.location.protocol}//beta.${currentHostName}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
||||
window.location.replace(href);
|
||||
}
|
||||
|
||||
// Beta is not enabled, but the current host name does start with beta.
|
||||
else if (!isBeta && currentHostName.startsWith('beta')) {
|
||||
const href = `${window.location.protocol}//${currentHostName.replace('beta.', '')}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
||||
window.location.replace(href);
|
||||
}
|
||||
}
|
||||
export default handleBeta;
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from "react";
|
||||
import { Redirect, Route, useLocation } from "react-router-dom";
|
||||
|
||||
function PrivateRoute({ component: Component, isAuthorized, ...rest }) {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={(props) =>
|
||||
isAuthorized === true ? (
|
||||
<Component {...props} />
|
||||
) : (
|
||||
<Redirect to={`/signin?redirect=${location.pathname}`} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default PrivateRoute;
|
||||
46
client/src/utils/prompt.js
Normal file
46
client/src/utils/prompt.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import {useBeforeUnload, useBlocker} from "react-router-dom";
|
||||
import {useCallback, useEffect, useRef} from "react";
|
||||
function usePrompt(message, {beforeUnload} = {}) {
|
||||
|
||||
let blocker = useBlocker(
|
||||
useCallback(
|
||||
(location) => {
|
||||
// This has been put in, so it does not affect transitions between the same page
|
||||
if (location.currentLocation.pathname === location.nextLocation.pathname) {
|
||||
return false;
|
||||
}
|
||||
return typeof message === "string" ? !window.confirm(message) : false;
|
||||
},
|
||||
[message]
|
||||
)
|
||||
);
|
||||
|
||||
let prevState = useRef(blocker.state);
|
||||
|
||||
useEffect(() => {
|
||||
if (blocker.state === "blocked") {
|
||||
blocker.reset();
|
||||
}
|
||||
prevState.current = blocker.state;
|
||||
}, [blocker]);
|
||||
|
||||
useBeforeUnload(
|
||||
useCallback(
|
||||
(event) => {
|
||||
if (beforeUnload && typeof message === "string") {
|
||||
event.preventDefault();
|
||||
event.returnValue = message;
|
||||
}
|
||||
},
|
||||
[message, beforeUnload]
|
||||
),
|
||||
{capture: true}
|
||||
);
|
||||
}
|
||||
|
||||
function Prompt({when, message, ...props}) {
|
||||
usePrompt(when ? message : false, props);
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Prompt;
|
||||
@@ -1,129 +1,128 @@
|
||||
import { useEffect, useCallback, useReducer } from "react";
|
||||
import {useCallback, useEffect, useReducer, useState} from "react";
|
||||
//Based on https://www.fullstacklabs.co/blog/keyboard-shortcuts-with-react-hooks
|
||||
const blacklistedTargets = []; // ["INPUT", "TEXTAREA"];
|
||||
export const useKeyboardSaveShortcut = (callback) =>
|
||||
useKeyboardShortcut(["Control", "S"], callback, { overrideSystem: true });
|
||||
useKeyboardShortcut(["Control", "S"], callback, {overrideSystem: true});
|
||||
|
||||
const keysReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "set-key-down":
|
||||
const keydownState = { ...state, [action.key]: true };
|
||||
return keydownState;
|
||||
case "set-key-up":
|
||||
const keyUpState = { ...state, [action.key]: false };
|
||||
return keyUpState;
|
||||
case "reset-keys":
|
||||
const resetState = { ...action.data };
|
||||
return resetState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
switch (action.type) {
|
||||
case "set-key-down":
|
||||
const keydownState = {...state, [action.key]: true};
|
||||
return keydownState;
|
||||
case "set-key-up":
|
||||
const keyUpState = {...state, [action.key]: false};
|
||||
return keyUpState;
|
||||
case "reset-keys":
|
||||
const resetState = {...action.data};
|
||||
return resetState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const useKeyboardShortcut = (shortcutKeys, callback, options) => {
|
||||
if (!Array.isArray(shortcutKeys))
|
||||
throw new Error(
|
||||
"The first parameter to `useKeyboardShortcut` must be an ordered array of `KeyboardEvent.key` strings."
|
||||
if (!Array.isArray(shortcutKeys))
|
||||
throw new Error(
|
||||
"The first parameter to `useKeyboardShortcut` must be an ordered array of `KeyboardEvent.key` strings."
|
||||
);
|
||||
|
||||
if (!shortcutKeys.length)
|
||||
throw new Error(
|
||||
"The first parameter to `useKeyboardShortcut` must contain atleast one `KeyboardEvent.key` string."
|
||||
);
|
||||
|
||||
if (!callback || typeof callback !== "function")
|
||||
throw new Error(
|
||||
"The second parameter to `useKeyboardShortcut` must be a function that will be envoked when the keys are pressed."
|
||||
);
|
||||
|
||||
const {overrideSystem} = options || {};
|
||||
const initalKeyMapping = shortcutKeys.reduce((currentKeys, key) => {
|
||||
currentKeys[key.toLowerCase()] = false;
|
||||
return currentKeys;
|
||||
}, {});
|
||||
|
||||
const [keys, setKeys] = useReducer(keysReducer, initalKeyMapping);
|
||||
const [listenersAdded, setListenersAdded] = useState(false);
|
||||
|
||||
const keydownListener = useCallback(
|
||||
(assignedKey) => (keydownEvent) => {
|
||||
const loweredKey = assignedKey.toLowerCase();
|
||||
|
||||
if (keydownEvent.repeat) return;
|
||||
if (blacklistedTargets.includes(keydownEvent.target.tagName)) return;
|
||||
if (loweredKey !== keydownEvent.key.toLowerCase()) return;
|
||||
if (keys[loweredKey] === undefined) return;
|
||||
|
||||
if (overrideSystem) {
|
||||
keydownEvent.preventDefault();
|
||||
disabledEventPropagation(keydownEvent);
|
||||
}
|
||||
|
||||
setKeys({type: "set-key-down", key: loweredKey});
|
||||
return false;
|
||||
},
|
||||
[keys, overrideSystem]
|
||||
);
|
||||
|
||||
if (!shortcutKeys.length)
|
||||
throw new Error(
|
||||
"The first parameter to `useKeyboardShortcut` must contain atleast one `KeyboardEvent.key` string."
|
||||
const keyupListener = useCallback(
|
||||
(assignedKey) => (keyupEvent) => {
|
||||
const raisedKey = assignedKey.toLowerCase();
|
||||
|
||||
if (blacklistedTargets.includes(keyupEvent.target.tagName)) return;
|
||||
if (keyupEvent.key.toLowerCase() !== raisedKey) return;
|
||||
if (keys[raisedKey] === undefined) return;
|
||||
|
||||
if (overrideSystem) {
|
||||
keyupEvent.preventDefault();
|
||||
disabledEventPropagation(keyupEvent);
|
||||
}
|
||||
|
||||
setKeys({type: "set-key-up", key: raisedKey});
|
||||
return false;
|
||||
},
|
||||
[keys, overrideSystem]
|
||||
);
|
||||
|
||||
if (!callback || typeof callback !== "function")
|
||||
throw new Error(
|
||||
"The second parameter to `useKeyboardShortcut` must be a function that will be envoked when the keys are pressed."
|
||||
);
|
||||
useEffect(() => {
|
||||
if (!Object.values(keys).filter((value) => !value).length) {
|
||||
callback(keys);
|
||||
setKeys({type: "reset-keys", data: initalKeyMapping});
|
||||
} else {
|
||||
setKeys({type: null});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [callback, keys]);
|
||||
|
||||
const { overrideSystem } = options || {};
|
||||
const initalKeyMapping = shortcutKeys.reduce((currentKeys, key) => {
|
||||
currentKeys[key.toLowerCase()] = false;
|
||||
return currentKeys;
|
||||
}, {});
|
||||
useEffect(() => {
|
||||
if (!listenersAdded) {
|
||||
console.log('Added events for keyup and keydown');
|
||||
shortcutKeys.forEach((k) => {
|
||||
window.addEventListener("keydown", keydownListener(k));
|
||||
window.addEventListener("keyup", keyupListener(k))
|
||||
});
|
||||
}
|
||||
|
||||
const [keys, setKeys] = useReducer(keysReducer, initalKeyMapping);
|
||||
setListenersAdded(true);
|
||||
|
||||
const keydownListener = useCallback(
|
||||
(assignedKey) => (keydownEvent) => {
|
||||
const loweredKey = assignedKey.toLowerCase();
|
||||
return () => {
|
||||
shortcutKeys.forEach((k) => {
|
||||
window.removeEventListener("keydown", keydownListener(k));
|
||||
window.removeEventListener("keyup", keyupListener(k));
|
||||
});
|
||||
}
|
||||
}, [listenersAdded]);
|
||||
|
||||
if (keydownEvent.repeat) return;
|
||||
if (blacklistedTargets.includes(keydownEvent.target.tagName)) return;
|
||||
if (loweredKey !== keydownEvent.key.toLowerCase()) return;
|
||||
if (keys[loweredKey] === undefined) return;
|
||||
|
||||
if (overrideSystem) {
|
||||
keydownEvent.preventDefault();
|
||||
disabledEventPropagation(keydownEvent);
|
||||
}
|
||||
|
||||
setKeys({ type: "set-key-down", key: loweredKey });
|
||||
return false;
|
||||
},
|
||||
[keys, overrideSystem]
|
||||
);
|
||||
|
||||
const keyupListener = useCallback(
|
||||
(assignedKey) => (keyupEvent) => {
|
||||
const raisedKey = assignedKey.toLowerCase();
|
||||
|
||||
if (blacklistedTargets.includes(keyupEvent.target.tagName)) return;
|
||||
if (keyupEvent.key.toLowerCase() !== raisedKey) return;
|
||||
if (keys[raisedKey] === undefined) return;
|
||||
|
||||
if (overrideSystem) {
|
||||
keyupEvent.preventDefault();
|
||||
disabledEventPropagation(keyupEvent);
|
||||
}
|
||||
|
||||
setKeys({ type: "set-key-up", key: raisedKey });
|
||||
return false;
|
||||
},
|
||||
[keys, overrideSystem]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!Object.values(keys).filter((value) => !value).length) {
|
||||
callback(keys);
|
||||
setKeys({ type: "reset-keys", data: initalKeyMapping });
|
||||
} else {
|
||||
setKeys({ type: null });
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [callback, keys]);
|
||||
|
||||
useEffect(() => {
|
||||
shortcutKeys.forEach((k) =>
|
||||
window.addEventListener("keydown", keydownListener(k))
|
||||
);
|
||||
return () =>
|
||||
shortcutKeys.forEach((k) =>
|
||||
window.removeEventListener("keydown", keydownListener(k))
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
shortcutKeys.forEach((k) =>
|
||||
window.addEventListener("keyup", keyupListener(k))
|
||||
);
|
||||
return () =>
|
||||
shortcutKeys.forEach((k) =>
|
||||
window.removeEventListener("keyup", keyupListener(k))
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default useKeyboardShortcut;
|
||||
|
||||
function disabledEventPropagation(e) {
|
||||
if (e) {
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
} else if (window.event) {
|
||||
window.event.cancelBubble = true;
|
||||
if (e) {
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
} else if (window.event) {
|
||||
window.event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user