- Merge client update into test-beta

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-01-18 19:20:08 -05:00
696 changed files with 92291 additions and 107075 deletions

View File

@@ -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");
}

View File

@@ -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;

View File

@@ -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

View 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
View 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;

View 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;

View File

@@ -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;

View 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;

View File

@@ -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;
}
}
}
}