@@ -1,67 +1,45 @@
|
||||
import i18n from "i18next";
|
||||
|
||||
const AuditTrailMapping = {
|
||||
admin_job_remove_from_ar: (status) =>
|
||||
i18n.t("audit_trail.messages.admin_job_remove_from_ar", {status}),
|
||||
admin_jobfieldchange: (field, value) =>
|
||||
"ADMIN: " +
|
||||
i18n.t("audit_trail.messages.jobfieldchanged", {field, value}),
|
||||
admin_jobstatuschange: (status) =>
|
||||
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", {status}),
|
||||
alertToggle: (status) =>
|
||||
i18n.t("audit_trail.messages.alerttoggle", {status}),
|
||||
appointmentcancel: (lost_sale_reason) =>
|
||||
i18n.t("audit_trail.messages.appointmentcancel", {lost_sale_reason}),
|
||||
appointmentinsert: (start) =>
|
||||
i18n.t("audit_trail.messages.appointmentinsert", {start}),
|
||||
billdeleted: (invoice_number) =>
|
||||
i18n.t("audit_trail.messages.billdeleted", { invoice_number }),
|
||||
billposted: (invoice_number) =>
|
||||
i18n.t("audit_trail.messages.billposted", {invoice_number}),
|
||||
billupdated: (invoice_number) =>
|
||||
i18n.t("audit_trail.messages.billupdated", {invoice_number}),
|
||||
jobassignmentchange: (operation, name) =>
|
||||
i18n.t("audit_trail.messages.jobassignmentchange", {operation, name}),
|
||||
jobassignmentremoved: (operation) =>
|
||||
i18n.t("audit_trail.messages.jobassignmentremoved", {operation}),
|
||||
jobchecklist: (type, inproduction, status) =>
|
||||
i18n.t("audit_trail.messages.jobchecklist", {type, inproduction, status}),
|
||||
jobconverted: (ro_number) =>
|
||||
i18n.t("audit_trail.messages.jobconverted", {ro_number}),
|
||||
jobintake: (status, email, scheduled_completion) =>
|
||||
i18n.t("audit_trail.messages.jobintake", {status, email,scheduled_completion}),
|
||||
jobdelivery: (status, email, actual_completion) =>
|
||||
i18n.t("audit_trail.messages.jobdelivery", {status, email,actual_completion}),
|
||||
jobexported: () => i18n.t("audit_trail.messages.jobexported"),
|
||||
jobfieldchange: (field, value) =>
|
||||
i18n.t("audit_trail.messages.jobfieldchanged", {field, value}),
|
||||
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
||||
jobinproductionchange: (inproduction) =>
|
||||
i18n.t("audit_trail.messages.jobinproductionchange", {inproduction}),
|
||||
jobinvoiced: () => i18n.t("audit_trail.messages.jobinvoiced"),
|
||||
jobmodifylbradj: ({mod_lbr_ty, hours}) =>
|
||||
i18n.t("audit_trail.messages.jobmodifylbradj", {mod_lbr_ty, hours}),
|
||||
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
|
||||
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
|
||||
jobnotedeleted: () => i18n.t("audit_trail.messages.jobnotedeleted"),
|
||||
admin_jobunvoid: () => i18n.t("audit_trail.messages.admin_jobunvoid"),
|
||||
admin_jobuninvoice: () => i18n.t("audit_trail.messages.admin_jobuninvoice"),
|
||||
admin_jobmarkforreexport: () =>
|
||||
i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
|
||||
admin_jobmarkexported: () =>
|
||||
i18n.t("audit_trail.messages.admin_jobmarkexported"),
|
||||
failedpayment: () => i18n.t("audit_trail.messages.failedpayment"),
|
||||
assignedlinehours: (team, hours) =>
|
||||
i18n.t("audit_trail.messages.assignedlinehours", {team, hours}),
|
||||
jobspartsorder: (order_number) =>
|
||||
i18n.t("audit_trail.messages.jobspartsorder", {order_number}),
|
||||
jobspartsreturn: (order_number) =>
|
||||
i18n.t("audit_trail.messages.jobspartsreturn", {order_number}),
|
||||
jobstatuschange: (status) =>
|
||||
i18n.t("audit_trail.messages.jobstatuschange", {status}),
|
||||
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
||||
jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }),
|
||||
jobvoid: () => i18n.t("audit_trail.messages.jobvoid"),
|
||||
admin_job_remove_from_ar: (status) => i18n.t("audit_trail.messages.admin_job_remove_from_ar", { status }),
|
||||
admin_jobfieldchange: (field, value) => "ADMIN: " + i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||
admin_jobstatuschange: (status) => "ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||
alertToggle: (status) => i18n.t("audit_trail.messages.alerttoggle", { status }),
|
||||
appointmentcancel: (lost_sale_reason) => i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
|
||||
appointmentinsert: (start) => i18n.t("audit_trail.messages.appointmentinsert", { start }),
|
||||
billdeleted: (invoice_number) => i18n.t("audit_trail.messages.billdeleted", { invoice_number }),
|
||||
billposted: (invoice_number) => i18n.t("audit_trail.messages.billposted", { invoice_number }),
|
||||
billupdated: (invoice_number) => i18n.t("audit_trail.messages.billupdated", { invoice_number }),
|
||||
jobassignmentchange: (operation, name) => i18n.t("audit_trail.messages.jobassignmentchange", { operation, name }),
|
||||
jobassignmentremoved: (operation) => i18n.t("audit_trail.messages.jobassignmentremoved", { operation }),
|
||||
jobchecklist: (type, inproduction, status) =>
|
||||
i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }),
|
||||
jobconverted: (ro_number) => i18n.t("audit_trail.messages.jobconverted", { ro_number }),
|
||||
jobintake: (status, email, scheduled_completion) =>
|
||||
i18n.t("audit_trail.messages.jobintake", { status, email, scheduled_completion }),
|
||||
jobdelivery: (status, email, actual_completion) =>
|
||||
i18n.t("audit_trail.messages.jobdelivery", { status, email, actual_completion }),
|
||||
jobexported: () => i18n.t("audit_trail.messages.jobexported"),
|
||||
jobfieldchange: (field, value) => i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
||||
jobinproductionchange: (inproduction) => i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
|
||||
jobinvoiced: () => i18n.t("audit_trail.messages.jobinvoiced"),
|
||||
jobmodifylbradj: ({ mod_lbr_ty, hours }) => i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
|
||||
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
|
||||
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
|
||||
jobnotedeleted: () => i18n.t("audit_trail.messages.jobnotedeleted"),
|
||||
admin_jobunvoid: () => i18n.t("audit_trail.messages.admin_jobunvoid"),
|
||||
admin_jobuninvoice: () => i18n.t("audit_trail.messages.admin_jobuninvoice"),
|
||||
admin_jobmarkforreexport: () => i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
|
||||
admin_jobmarkexported: () => i18n.t("audit_trail.messages.admin_jobmarkexported"),
|
||||
failedpayment: () => i18n.t("audit_trail.messages.failedpayment"),
|
||||
assignedlinehours: (team, hours) => i18n.t("audit_trail.messages.assignedlinehours", { team, hours }),
|
||||
jobspartsorder: (order_number) => i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
|
||||
jobspartsreturn: (order_number) => i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
|
||||
jobstatuschange: (status) => i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
||||
jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }),
|
||||
jobvoid: () => i18n.t("audit_trail.messages.jobvoid")
|
||||
};
|
||||
|
||||
export default AuditTrailMapping;
|
||||
|
||||
@@ -1,101 +1,53 @@
|
||||
import React from "react";
|
||||
import {Select} from "antd";
|
||||
import { Select } from "antd";
|
||||
import i18n from "../translations/i18n";
|
||||
|
||||
export default function CiecaSelect(parts = true, labor = true) {
|
||||
return (
|
||||
return (
|
||||
<>
|
||||
{labor && (
|
||||
<>
|
||||
{labor && (
|
||||
<>
|
||||
<Select.Option value="LAA">
|
||||
{i18n.t("joblines.fields.lbr_types.LAA")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAB">
|
||||
{i18n.t("joblines.fields.lbr_types.LAB")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAD">
|
||||
{i18n.t("joblines.fields.lbr_types.LAD")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAE">
|
||||
{i18n.t("joblines.fields.lbr_types.LAE")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAF">
|
||||
{i18n.t("joblines.fields.lbr_types.LAF")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAG">
|
||||
{i18n.t("joblines.fields.lbr_types.LAG")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAM">
|
||||
{i18n.t("joblines.fields.lbr_types.LAM")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAR">
|
||||
{i18n.t("joblines.fields.lbr_types.LAR")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAS">
|
||||
{i18n.t("joblines.fields.lbr_types.LAS")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAU">
|
||||
{i18n.t("joblines.fields.lbr_types.LAU")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LA1">
|
||||
{i18n.t("joblines.fields.lbr_types.LA1")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LA2">
|
||||
{i18n.t("joblines.fields.lbr_types.LA2")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LA3">
|
||||
{i18n.t("joblines.fields.lbr_types.LA3")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LA4">
|
||||
{i18n.t("joblines.fields.lbr_types.LA4")}
|
||||
</Select.Option>
|
||||
</>
|
||||
)}
|
||||
{parts && (
|
||||
<>
|
||||
<Select.Option value="PAA">
|
||||
{i18n.t("joblines.fields.part_types.PAA")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAC">
|
||||
{i18n.t("joblines.fields.part_types.PAC")}
|
||||
</Select.Option>
|
||||
|
||||
<Select.Option value="PAL">
|
||||
{i18n.t("joblines.fields.part_types.PAL")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAG">
|
||||
{i18n.t("joblines.fields.part_types.PAG")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAM">
|
||||
{i18n.t("joblines.fields.part_types.PAM")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAP">
|
||||
{i18n.t("joblines.fields.part_types.PAP")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAN">
|
||||
{i18n.t("joblines.fields.part_types.PAN")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAO">
|
||||
{i18n.t("joblines.fields.part_types.PAO")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAR">
|
||||
{i18n.t("joblines.fields.part_types.PAR")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAS">
|
||||
{i18n.t("joblines.fields.part_types.PAS")}
|
||||
</Select.Option>
|
||||
</>
|
||||
)}
|
||||
<Select.Option value="LAA">{i18n.t("joblines.fields.lbr_types.LAA")}</Select.Option>
|
||||
<Select.Option value="LAB">{i18n.t("joblines.fields.lbr_types.LAB")}</Select.Option>
|
||||
<Select.Option value="LAD">{i18n.t("joblines.fields.lbr_types.LAD")}</Select.Option>
|
||||
<Select.Option value="LAE">{i18n.t("joblines.fields.lbr_types.LAE")}</Select.Option>
|
||||
<Select.Option value="LAF">{i18n.t("joblines.fields.lbr_types.LAF")}</Select.Option>
|
||||
<Select.Option value="LAG">{i18n.t("joblines.fields.lbr_types.LAG")}</Select.Option>
|
||||
<Select.Option value="LAM">{i18n.t("joblines.fields.lbr_types.LAM")}</Select.Option>
|
||||
<Select.Option value="LAR">{i18n.t("joblines.fields.lbr_types.LAR")}</Select.Option>
|
||||
<Select.Option value="LAS">{i18n.t("joblines.fields.lbr_types.LAS")}</Select.Option>
|
||||
<Select.Option value="LAU">{i18n.t("joblines.fields.lbr_types.LAU")}</Select.Option>
|
||||
<Select.Option value="LA1">{i18n.t("joblines.fields.lbr_types.LA1")}</Select.Option>
|
||||
<Select.Option value="LA2">{i18n.t("joblines.fields.lbr_types.LA2")}</Select.Option>
|
||||
<Select.Option value="LA3">{i18n.t("joblines.fields.lbr_types.LA3")}</Select.Option>
|
||||
<Select.Option value="LA4">{i18n.t("joblines.fields.lbr_types.LA4")}</Select.Option>
|
||||
</>
|
||||
);
|
||||
)}
|
||||
{parts && (
|
||||
<>
|
||||
<Select.Option value="PAA">{i18n.t("joblines.fields.part_types.PAA")}</Select.Option>
|
||||
<Select.Option value="PAC">{i18n.t("joblines.fields.part_types.PAC")}</Select.Option>
|
||||
|
||||
<Select.Option value="PAL">{i18n.t("joblines.fields.part_types.PAL")}</Select.Option>
|
||||
<Select.Option value="PAG">{i18n.t("joblines.fields.part_types.PAG")}</Select.Option>
|
||||
<Select.Option value="PAM">{i18n.t("joblines.fields.part_types.PAM")}</Select.Option>
|
||||
<Select.Option value="PAP">{i18n.t("joblines.fields.part_types.PAP")}</Select.Option>
|
||||
<Select.Option value="PAN">{i18n.t("joblines.fields.part_types.PAN")}</Select.Option>
|
||||
<Select.Option value="PAO">{i18n.t("joblines.fields.part_types.PAO")}</Select.Option>
|
||||
<Select.Option value="PAR">{i18n.t("joblines.fields.part_types.PAR")}</Select.Option>
|
||||
<Select.Option value="PAS">{i18n.t("joblines.fields.part_types.PAS")}</Select.Option>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function GetPartTypeName(part_type) {
|
||||
if (!part_type) return null;
|
||||
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
||||
if (!part_type) return null;
|
||||
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
||||
}
|
||||
|
||||
export function Get(part_type) {
|
||||
if (!part_type) return null;
|
||||
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
||||
if (!part_type) return null;
|
||||
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
import axios from "axios";
|
||||
import {auth} from "../firebase/firebase.utils";
|
||||
import { auth } from "../firebase/firebase.utils";
|
||||
|
||||
axios.defaults.baseURL =
|
||||
import.meta.env.VITE_APP_AXIOS_BASE_API_URL ||
|
||||
(
|
||||
import.meta.env.MODE === "production" ?
|
||||
"https://api.imex.online/" :
|
||||
"http://localhost:4000/"
|
||||
);
|
||||
import.meta.env.VITE_APP_AXIOS_BASE_API_URL ||
|
||||
(import.meta.env.MODE === "production" ? "https://api.imex.online/" : "http://localhost:4000/");
|
||||
|
||||
export const axiosAuthInterceptorId = axios.interceptors.request.use(
|
||||
async (config) => {
|
||||
if (!config.headers.Authorization) {
|
||||
const token = auth.currentUser && (await auth.currentUser.getIdToken());
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
async (config) => {
|
||||
if (!config.headers.Authorization) {
|
||||
const token = auth.currentUser && (await auth.currentUser.getIdToken());
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
|
||||
const cleanAxios = axios.create();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import React from "react";
|
||||
import {NumericFormat} from "react-number-format";
|
||||
import { NumericFormat } from "react-number-format";
|
||||
|
||||
export default function CurrencyFormatter(props) {
|
||||
return (
|
||||
<NumericFormat
|
||||
thousandSeparator={true}
|
||||
decimalScale={2}
|
||||
fixedDecimalScale={true}
|
||||
prefix={"$"}
|
||||
value={props.children}
|
||||
displayType={"text"}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<NumericFormat
|
||||
thousandSeparator={true}
|
||||
decimalScale={2}
|
||||
fixedDecimalScale={true}
|
||||
prefix={"$"}
|
||||
value={props.children}
|
||||
displayType={"text"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,42 +1,32 @@
|
||||
import {Tooltip} from "antd";
|
||||
import { Tooltip } from "antd";
|
||||
import dayjs from "../utils/day";
|
||||
import React from "react";
|
||||
|
||||
export function DateFormatter(props) {
|
||||
return props.children
|
||||
? dayjs(props.children).format(
|
||||
props.includeDay ? "ddd MM/DD/YYYY" : "MM/DD/YYYY"
|
||||
)
|
||||
: null;
|
||||
return props.children ? dayjs(props.children).format(props.includeDay ? "ddd MM/DD/YYYY" : "MM/DD/YYYY") : null;
|
||||
}
|
||||
|
||||
export function DateTimeFormatter(props) {
|
||||
return props.children
|
||||
? dayjs(props.children).format(
|
||||
props.format ? props.format : "MM/DD/YYYY hh:mm a"
|
||||
)
|
||||
: null;
|
||||
return props.children ? dayjs(props.children).format(props.format ? props.format : "MM/DD/YYYY hh:mm a") : null;
|
||||
}
|
||||
|
||||
export function DateTimeFormatterFunction(date) {
|
||||
return dayjs(date).format("MM/DD/YYYY hh:mm a");
|
||||
return dayjs(date).format("MM/DD/YYYY hh:mm a");
|
||||
}
|
||||
|
||||
export function TimeFormatter(props) {
|
||||
return props.children
|
||||
? dayjs(props.children).format(props.format ? props.format : "hh:mm a")
|
||||
: null;
|
||||
return props.children ? dayjs(props.children).format(props.format ? props.format : "hh:mm a") : null;
|
||||
}
|
||||
|
||||
export function TimeAgoFormatter(props) {
|
||||
const m = dayjs(props.children);
|
||||
return props.children ? (
|
||||
<Tooltip placement="top" title={m.format("MM/DD/YYY hh:mm A")}>
|
||||
{m.fromNow()}
|
||||
</Tooltip>
|
||||
) : null;
|
||||
const m = dayjs(props.children);
|
||||
return props.children ? (
|
||||
<Tooltip placement="top" title={m.format("MM/DD/YYY hh:mm A")}>
|
||||
{m.fromNow()}
|
||||
</Tooltip>
|
||||
) : null;
|
||||
}
|
||||
|
||||
export function DateTimeFormat(value) {
|
||||
return dayjs(value).format("MM/DD/YYYY hh:mm A");
|
||||
return dayjs(value).format("MM/DD/YYYY hh:mm A");
|
||||
}
|
||||
|
||||
@@ -1,72 +1,57 @@
|
||||
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()],
|
||||
}
|
||||
{
|
||||
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()]
|
||||
}
|
||||
];
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
range.push({
|
||||
label: 'Last Year',
|
||||
value: [
|
||||
dayjs().subtract(1, "year"),
|
||||
dayjs(),
|
||||
]
|
||||
})
|
||||
range.push({
|
||||
label: "Last Year",
|
||||
value: [dayjs().subtract(1, "year"), dayjs()]
|
||||
});
|
||||
}
|
||||
|
||||
export default range;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {ApolloClient, ApolloLink, InMemoryCache, split} from "@apollo/client";
|
||||
import {setContext} from "@apollo/client/link/context";
|
||||
import {HttpLink} from "@apollo/client/link/http"; //"apollo-link-http";
|
||||
import {RetryLink} from "@apollo/client/link/retry";
|
||||
import {WebSocketLink} from "@apollo/client/link/ws";
|
||||
import {getMainDefinition, offsetLimitPagination,} from "@apollo/client/utilities";
|
||||
import { ApolloClient, ApolloLink, InMemoryCache, split } from "@apollo/client";
|
||||
import { setContext } from "@apollo/client/link/context";
|
||||
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
|
||||
import { RetryLink } from "@apollo/client/link/retry";
|
||||
import { WebSocketLink } from "@apollo/client/link/ws";
|
||||
import { getMainDefinition, offsetLimitPagination } from "@apollo/client/utilities";
|
||||
//import { split } from "apollo-link";
|
||||
import apolloLogger from "apollo-link-logger";
|
||||
//import axios from "axios";
|
||||
import {auth} from "../firebase/firebase.utils";
|
||||
import { auth } from "../firebase/firebase.utils";
|
||||
import errorLink from "../graphql/apollo-error-handling";
|
||||
import {SentryLink} from "apollo-link-sentry";
|
||||
import { SentryLink } from "apollo-link-sentry";
|
||||
|
||||
//import { store } from "../redux/store";
|
||||
const httpLink = new HttpLink({
|
||||
uri: import.meta.env.VITE_APP_GRAPHQL_ENDPOINT,
|
||||
uri: import.meta.env.VITE_APP_GRAPHQL_ENDPOINT
|
||||
});
|
||||
|
||||
const wsLink = new WebSocketLink({
|
||||
@@ -26,121 +26,112 @@ const wsLink = new WebSocketLink({
|
||||
if (token) {
|
||||
return {
|
||||
headers: {
|
||||
authorization: token ? `Bearer ${token}` : "",
|
||||
},
|
||||
authorization: token ? `Bearer ${token}` : ""
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const roundTripLink = new ApolloLink((operation, forward) => {
|
||||
// Called before operation is sent to server
|
||||
operation.setContext({start: new Date()});
|
||||
// Called before operation is sent to server
|
||||
operation.setContext({ start: new Date() });
|
||||
|
||||
return forward(operation).map((data) => {
|
||||
// Called after server responds
|
||||
const time = new Date() - operation.getContext().start;
|
||||
// console.log(
|
||||
// `Operation ${operation.operationName} took ${time} to complete`
|
||||
// );
|
||||
TrackExecutionTime(operation.operationName, time);
|
||||
return data;
|
||||
});
|
||||
return forward(operation).map((data) => {
|
||||
// Called after server responds
|
||||
const time = new Date() - operation.getContext().start;
|
||||
// console.log(
|
||||
// `Operation ${operation.operationName} took ${time} to complete`
|
||||
// );
|
||||
TrackExecutionTime(operation.operationName, time);
|
||||
return data;
|
||||
});
|
||||
});
|
||||
|
||||
const TrackExecutionTime = async (operationName, time) => {
|
||||
// if (process.env.NODE_ENV === "development") return;
|
||||
// const rdxStore = store.getState();
|
||||
// try {
|
||||
// axios.post("/ioevent", {
|
||||
// operationName,
|
||||
// time,
|
||||
// dbevent: true,
|
||||
// user:
|
||||
// rdxStore.user &&
|
||||
// rdxStore.user.currentUser &&
|
||||
// rdxStore.user.currentUser.email,
|
||||
// imexshopid:
|
||||
// rdxStore.user &&
|
||||
// rdxStore.user.bodyshop &&
|
||||
// rdxStore.user.bodyshop.imexshopid,
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.log("IOEvent Error", error);
|
||||
// }
|
||||
// if (process.env.NODE_ENV === "development") return;
|
||||
// const rdxStore = store.getState();
|
||||
// try {
|
||||
// axios.post("/ioevent", {
|
||||
// operationName,
|
||||
// time,
|
||||
// dbevent: true,
|
||||
// user:
|
||||
// rdxStore.user &&
|
||||
// rdxStore.user.currentUser &&
|
||||
// rdxStore.user.currentUser.email,
|
||||
// imexshopid:
|
||||
// rdxStore.user &&
|
||||
// rdxStore.user.bodyshop &&
|
||||
// rdxStore.user.bodyshop.imexshopid,
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.log("IOEvent Error", error);
|
||||
// }
|
||||
};
|
||||
|
||||
const subscriptionMiddleware = {
|
||||
applyMiddleware: async (options, next) => {
|
||||
options.authToken =
|
||||
auth.currentUser && (await auth.currentUser.getIdToken());
|
||||
next();
|
||||
},
|
||||
applyMiddleware: async (options, next) => {
|
||||
options.authToken = auth.currentUser && (await auth.currentUser.getIdToken());
|
||||
next();
|
||||
}
|
||||
};
|
||||
wsLink.subscriptionClient.use([subscriptionMiddleware]);
|
||||
|
||||
const link = split(
|
||||
// split based on operation type
|
||||
({query}) => {
|
||||
const definition = getMainDefinition(query);
|
||||
// console.log(
|
||||
// "##Intercepted GQL Transaction : " +
|
||||
// definition.operation +
|
||||
// "|" +
|
||||
// definition.name.value +
|
||||
// "##",
|
||||
// query
|
||||
// );
|
||||
return (
|
||||
definition.kind === "OperationDefinition" &&
|
||||
definition.operation === "subscription"
|
||||
);
|
||||
},
|
||||
wsLink,
|
||||
httpLink
|
||||
// split based on operation type
|
||||
({ query }) => {
|
||||
const definition = getMainDefinition(query);
|
||||
// console.log(
|
||||
// "##Intercepted GQL Transaction : " +
|
||||
// definition.operation +
|
||||
// "|" +
|
||||
// definition.name.value +
|
||||
// "##",
|
||||
// query
|
||||
// );
|
||||
return definition.kind === "OperationDefinition" && definition.operation === "subscription";
|
||||
},
|
||||
wsLink,
|
||||
httpLink
|
||||
);
|
||||
|
||||
const authLink = setContext((_, {headers}) => {
|
||||
return (
|
||||
auth.currentUser &&
|
||||
auth.currentUser
|
||||
.getIdToken()
|
||||
.then((token) => {
|
||||
if (token) {
|
||||
return {
|
||||
headers: {
|
||||
...headers,
|
||||
authorization: token ? `Bearer ${token}` : "",
|
||||
},
|
||||
};
|
||||
} else {
|
||||
console.error(
|
||||
"Authentication error. Unable to add authorization token because it was empty."
|
||||
);
|
||||
return {headers};
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
"Authentication error. Unable to add authorization token.",
|
||||
error.message
|
||||
);
|
||||
return {headers};
|
||||
})
|
||||
);
|
||||
const authLink = setContext((_, { headers }) => {
|
||||
return (
|
||||
auth.currentUser &&
|
||||
auth.currentUser
|
||||
.getIdToken()
|
||||
.then((token) => {
|
||||
if (token) {
|
||||
return {
|
||||
headers: {
|
||||
...headers,
|
||||
authorization: token ? `Bearer ${token}` : ""
|
||||
}
|
||||
};
|
||||
} else {
|
||||
console.error("Authentication error. Unable to add authorization token because it was empty.");
|
||||
return { headers };
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Authentication error. Unable to add authorization token.", error.message);
|
||||
return { headers };
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const retryLink = new RetryLink({
|
||||
delay: {
|
||||
initial: 500,
|
||||
max: 5,
|
||||
jitter: true,
|
||||
},
|
||||
attempts: {
|
||||
max: 5,
|
||||
retryIf: (error, _operation) => !!error,
|
||||
},
|
||||
delay: {
|
||||
initial: 500,
|
||||
max: 5,
|
||||
jitter: true
|
||||
},
|
||||
attempts: {
|
||||
max: 5,
|
||||
retryIf: (error, _operation) => !!error
|
||||
}
|
||||
});
|
||||
|
||||
const middlewares = [];
|
||||
@@ -149,21 +140,17 @@ if (import.meta.env.DEV) {
|
||||
}
|
||||
|
||||
middlewares.push(
|
||||
new SentryLink().concat(
|
||||
roundTripLink.concat(
|
||||
retryLink.concat(errorLink.concat(authLink.concat(link)))
|
||||
)
|
||||
)
|
||||
new SentryLink().concat(roundTripLink.concat(retryLink.concat(errorLink.concat(authLink.concat(link)))))
|
||||
);
|
||||
|
||||
const cache = new InMemoryCache({
|
||||
typePolicies: {
|
||||
Query: {
|
||||
fields: {
|
||||
conversations: offsetLimitPagination(),
|
||||
},
|
||||
},
|
||||
},
|
||||
typePolicies: {
|
||||
Query: {
|
||||
fields: {
|
||||
conversations: offsetLimitPagination()
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const client = new ApolloClient({
|
||||
@@ -174,15 +161,15 @@ const client = new ApolloClient({
|
||||
watchQuery: {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
errorPolicy: "ignore",
|
||||
errorPolicy: "ignore"
|
||||
},
|
||||
query: {
|
||||
fetchPolicy: "network-only",
|
||||
errorPolicy: "all",
|
||||
errorPolicy: "all"
|
||||
},
|
||||
mutate: {
|
||||
errorPolicy: "all",
|
||||
},
|
||||
},
|
||||
errorPolicy: "all"
|
||||
}
|
||||
}
|
||||
});
|
||||
export default client;
|
||||
|
||||
@@ -3,6 +3,6 @@ import parsePhoneNumber from "libphonenumber-js";
|
||||
import React from "react";
|
||||
|
||||
export default function PhoneNumberFormatter(props) {
|
||||
const p = parsePhoneNumber(props.children || "", "CA");
|
||||
return p ? <span>{p.formatNational()}</span> : null;
|
||||
const p = parsePhoneNumber(props.children || "", "CA");
|
||||
return p ? <span>{p.formatNational()}</span> : null;
|
||||
}
|
||||
|
||||
@@ -1,54 +1,69 @@
|
||||
import {AlertOutlined} from "@ant-design/icons";
|
||||
import {Button, notification, Space} from "antd";
|
||||
import { AlertOutlined } from "@ant-design/icons";
|
||||
import { Button, notification, Space } from "antd";
|
||||
import i18n from "i18next";
|
||||
import React from "react";
|
||||
import * as serviceWorkerRegistration from "../serviceWorkerRegistration";
|
||||
import {store} from "../redux/store";
|
||||
import { store } from "../redux/store";
|
||||
import InstanceRenderManager from "./instanceRenderMgr";
|
||||
|
||||
const onServiceWorkerUpdate = (registration) => {
|
||||
console.log("onServiceWorkerUpdate", registration);
|
||||
console.log("onServiceWorkerUpdate", registration);
|
||||
|
||||
const btn = (
|
||||
<Space flex>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
window.open(
|
||||
InstanceRenderManager({imex:"https://imex-online.noticeable.news/",
|
||||
rome: "https://rome-online.noticeable.news/"})
|
||||
, "_blank");
|
||||
}}
|
||||
>
|
||||
{i18n.t("general.actions.viewreleasenotes")}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (registration && registration.waiting) {
|
||||
await registration.unregister();
|
||||
// Makes Workbox call skipWaiting()
|
||||
registration.waiting.postMessage({type: "SKIP_WAITING"});
|
||||
// Once the service worker is unregistered, we can reload the page to let
|
||||
// the browser download a fresh copy of our app (invalidating the cache)
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{i18n.t("general.actions.refresh")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
const btn = (
|
||||
<Space flex>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
window.open(
|
||||
InstanceRenderManager({
|
||||
imex: "https://imex-online.noticeable.news/",
|
||||
rome: "https://rome-online.noticeable.news/"
|
||||
}),
|
||||
"_blank"
|
||||
);
|
||||
}}
|
||||
>
|
||||
{i18n.t("general.actions.viewreleasenotes")}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (registration && registration.waiting) {
|
||||
await registration.unregister();
|
||||
// Makes Workbox call skipWaiting()
|
||||
registration.waiting.postMessage({ type: "SKIP_WAITING" });
|
||||
// Once the service worker is unregistered, we can reload the page to let
|
||||
// the browser download a fresh copy of our app (invalidating the cache)
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{i18n.t("general.actions.refresh")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
store.dispatch()
|
||||
store.dispatch();
|
||||
|
||||
notification.open({
|
||||
icon: <AlertOutlined/>,
|
||||
message: i18n.t("general.messages.newversiontitle",{app: InstanceRenderManager({imex:'$t(titles.imexonline)', rome: '$t(titles.romeonline)', promanager: '$t(titles.promanager)'})}),
|
||||
description: i18n.t("general.messages.newversionmessage", {app: InstanceRenderManager({imex:'$t(titles.imexonline)', rome: '$t(titles.romeonline)', promanager: '$t(titles.promanager)'})}),
|
||||
duration: 0,
|
||||
btn,
|
||||
key: "updateavailable",
|
||||
});
|
||||
notification.open({
|
||||
icon: <AlertOutlined />,
|
||||
message: i18n.t("general.messages.newversiontitle", {
|
||||
app: InstanceRenderManager({
|
||||
imex: "$t(titles.imexonline)",
|
||||
rome: "$t(titles.romeonline)",
|
||||
promanager: "$t(titles.promanager)"
|
||||
})
|
||||
}),
|
||||
description: i18n.t("general.messages.newversionmessage", {
|
||||
app: InstanceRenderManager({
|
||||
imex: "$t(titles.imexonline)",
|
||||
rome: "$t(titles.romeonline)",
|
||||
promanager: "$t(titles.promanager)"
|
||||
})
|
||||
}),
|
||||
duration: 0,
|
||||
btn,
|
||||
key: "updateavailable"
|
||||
});
|
||||
};
|
||||
|
||||
serviceWorkerRegistration.register({onUpdate: onServiceWorkerUpdate});
|
||||
serviceWorkerRegistration.register({ onUpdate: onServiceWorkerUpdate });
|
||||
|
||||
@@ -1,437 +1,397 @@
|
||||
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 {generateTemplate} from "./graphQLmodifier";
|
||||
import { generateTemplate } from "./graphQLmodifier";
|
||||
const server = import.meta.env.VITE_APP_REPORTS_SERVER_URL;
|
||||
|
||||
jsreport.serverUrl = server;
|
||||
|
||||
let Templates;
|
||||
export function GenerateTemplates(){
|
||||
export function GenerateTemplates() {
|
||||
//Required as a part of the transition to Vite.
|
||||
//Previous method had the template hash generating before translations loaded, resulting in empty files.
|
||||
Templates = TemplateList()
|
||||
//Previous method had the template hash generating before translations loaded, resulting in empty files.
|
||||
Templates = TemplateList();
|
||||
}
|
||||
|
||||
export default async function RenderTemplate(
|
||||
templateObject,
|
||||
bodyshop,
|
||||
renderAsHtml = false,
|
||||
renderAsExcel = false,
|
||||
renderAsText = false
|
||||
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;
|
||||
if (window.jsr3) {
|
||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||
}
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
|
||||
jsreport.headers["Authorization"] = jsrAuth;
|
||||
jsreport.headers["Authorization"] = jsrAuth;
|
||||
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let {contextData, useShopSpecificTemplate} = await fetchContextData(
|
||||
templateObject,
|
||||
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];
|
||||
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,
|
||||
filters: templateObject?.filters,
|
||||
sorters: templateObject?.sorters,
|
||||
offset: bodyshop.timezone, //dayjs().utcOffset(),
|
||||
defaultSorters: templateObject?.defaultSorters,
|
||||
},
|
||||
};
|
||||
|
||||
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)});
|
||||
}
|
||||
}
|
||||
|
||||
export async function RenderTemplates(
|
||||
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;
|
||||
|
||||
templateObjects.forEach((template) => {
|
||||
proms.push(
|
||||
(async () => {
|
||||
let {contextData, useShopSpecificTemplate} = await fetchContextData(
|
||||
template,
|
||||
jsrAuth
|
||||
);
|
||||
unsortedTemplatesAndData.push({
|
||||
templateObject: template,
|
||||
contextData,
|
||||
useShopSpecificTemplate,
|
||||
});
|
||||
})()
|
||||
);
|
||||
});
|
||||
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();
|
||||
}
|
||||
} catch (error) {
|
||||
notification["error"]({message: JSON.stringify(error)});
|
||||
}
|
||||
}
|
||||
|
||||
export const GenerateDocument = async (
|
||||
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,
|
||||
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"
|
||||
}
|
||||
})
|
||||
);
|
||||
} else if (sendType === "x") {
|
||||
|
||||
await RenderTemplate(template, bodyshop, false, true);
|
||||
} else if (sendType === "text") {
|
||||
await RenderTemplate(template, bodyshop, false, false, true);
|
||||
} else {
|
||||
await RenderTemplate(template, bodyshop);
|
||||
}),
|
||||
...(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,
|
||||
filters: templateObject?.filters,
|
||||
sorters: templateObject?.sorters,
|
||||
offset: bodyshop.timezone, //dayjs().utcOffset(),
|
||||
defaultSorters: templateObject?.defaultSorters
|
||||
}
|
||||
};
|
||||
|
||||
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) });
|
||||
}
|
||||
}
|
||||
|
||||
export async function RenderTemplates(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;
|
||||
|
||||
templateObjects.forEach((template) => {
|
||||
proms.push(
|
||||
(async () => {
|
||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(template, jsrAuth);
|
||||
unsortedTemplatesAndData.push({
|
||||
templateObject: template,
|
||||
contextData,
|
||||
useShopSpecificTemplate
|
||||
});
|
||||
})()
|
||||
);
|
||||
});
|
||||
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();
|
||||
}
|
||||
} catch (error) {
|
||||
notification["error"]({ message: JSON.stringify(error) });
|
||||
}
|
||||
}
|
||||
|
||||
export const GenerateDocument = async (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") {
|
||||
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);
|
||||
};
|
||||
|
||||
export const fetchFilterData = async ({name}) => {
|
||||
try {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
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 jsReportFilters = await cleanAxios.get(
|
||||
`${server}/odata/assets?$filter=name eq '${name}.filters'`,
|
||||
{headers: {Authorization: jsrAuth}}
|
||||
);
|
||||
console.log("🚀 ~ fetchFilterData ~ jsReportFilters:", jsReportFilters);
|
||||
|
||||
let parsedFilterData;
|
||||
let useShopSpecificTemplate = false;
|
||||
// let shopSpecificTemplate;
|
||||
|
||||
if (shopSpecificFolder) {
|
||||
let shopSpecificTemplate = jsReportFilters.data.value.find(
|
||||
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
|
||||
);
|
||||
if (shopSpecificTemplate) {
|
||||
useShopSpecificTemplate = true;
|
||||
parsedFilterData = atob(shopSpecificTemplate.content);
|
||||
}
|
||||
}
|
||||
|
||||
if (!parsedFilterData) {
|
||||
const generalTemplate = jsReportFilters.data.value.find((f) => !f.folder);
|
||||
useShopSpecificTemplate = false;
|
||||
if (generalTemplate) parsedFilterData = atob(generalTemplate.content);
|
||||
}
|
||||
const data = JSON.parse(parsedFilterData);
|
||||
return {
|
||||
data,
|
||||
useShopSpecificTemplate,
|
||||
success: true,
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const fetchContextData = async (templateObject, jsrAuth) => {
|
||||
export const fetchFilterData = async ({ name }) => {
|
||||
try {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
|
||||
jsreport.headers["FirebaseAuthorization"] =
|
||||
"Bearer " + (await auth.currentUser.getIdToken());
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
jsreport.headers["FirebaseAuthorization"] = "Bearer " + (await auth.currentUser.getIdToken());
|
||||
|
||||
const folders = await cleanAxios.get(`${server}/odata/folders`, {
|
||||
headers: {Authorization: jsrAuth},
|
||||
headers: { Authorization: jsrAuth }
|
||||
});
|
||||
const shopSpecificFolder = folders.data.value.find(
|
||||
(f) => f.name === bodyshop.imexshopid
|
||||
);
|
||||
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}}
|
||||
);
|
||||
const jsReportFilters = await cleanAxios.get(`${server}/odata/assets?$filter=name eq '${name}.filters'`, {
|
||||
headers: { Authorization: jsrAuth }
|
||||
});
|
||||
console.log("🚀 ~ fetchFilterData ~ jsReportFilters:", jsReportFilters);
|
||||
|
||||
let templateQueryToExecute;
|
||||
let parsedFilterData;
|
||||
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);
|
||||
}
|
||||
let shopSpecificTemplate = jsReportFilters.data.value.find(
|
||||
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
|
||||
);
|
||||
if (shopSpecificTemplate) {
|
||||
useShopSpecificTemplate = true;
|
||||
parsedFilterData = atob(shopSpecificTemplate.content);
|
||||
}
|
||||
}
|
||||
|
||||
if (!templateQueryToExecute) {
|
||||
const generalTemplate = jsReportQueries.data.value.find((f) => !f.folder);
|
||||
useShopSpecificTemplate = false;
|
||||
templateQueryToExecute = atob(generalTemplate.content);
|
||||
if (!parsedFilterData) {
|
||||
const generalTemplate = jsReportFilters.data.value.find((f) => !f.folder);
|
||||
useShopSpecificTemplate = false;
|
||||
if (generalTemplate) parsedFilterData = atob(generalTemplate.content);
|
||||
}
|
||||
const data = JSON.parse(parsedFilterData);
|
||||
return {
|
||||
data,
|
||||
useShopSpecificTemplate,
|
||||
success: true
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
success: false
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Commented out for future revision debugging
|
||||
// console.log('Template Object');
|
||||
// console.dir(templateObject);
|
||||
// console.log('Unmodified Query');
|
||||
// console.dir(templateQueryToExecute);
|
||||
const fetchContextData = async (templateObject, jsrAuth) => {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
|
||||
const hasFilters = templateObject?.filters?.length > 0;
|
||||
const hasSorters = templateObject?.sorters?.length > 0;
|
||||
const hasDefaultSorters = templateObject?.defaultSorters?.length > 0;
|
||||
jsreport.headers["FirebaseAuthorization"] = "Bearer " + (await auth.currentUser.getIdToken());
|
||||
|
||||
// We have no template filters or sorters, so we can just execute the query and return the data
|
||||
if (!hasFilters && !hasSorters && !hasDefaultSorters) {
|
||||
let contextData = {};
|
||||
if (templateQueryToExecute) {
|
||||
const {data} = await client.query({
|
||||
query: gql(templateQueryToExecute),
|
||||
variables: {...templateObject.variables},
|
||||
});
|
||||
contextData = data;
|
||||
}
|
||||
return {contextData, useShopSpecificTemplate};
|
||||
}
|
||||
const folders = await cleanAxios.get(`${server}/odata/folders`, {
|
||||
headers: { Authorization: jsrAuth }
|
||||
});
|
||||
const shopSpecificFolder = folders.data.value.find((f) => f.name === bodyshop.imexshopid);
|
||||
|
||||
return await generateTemplate(
|
||||
templateQueryToExecute,
|
||||
templateObject,
|
||||
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);
|
||||
}
|
||||
|
||||
// Commented out for future revision debugging
|
||||
// console.log('Template Object');
|
||||
// console.dir(templateObject);
|
||||
// console.log('Unmodified Query');
|
||||
// console.dir(templateQueryToExecute);
|
||||
|
||||
const hasFilters = templateObject?.filters?.length > 0;
|
||||
const hasSorters = templateObject?.sorters?.length > 0;
|
||||
const hasDefaultSorters = templateObject?.defaultSorters?.length > 0;
|
||||
|
||||
// We have no template filters or sorters, so we can just execute the query and return the data
|
||||
if (!hasFilters && !hasSorters && !hasDefaultSorters) {
|
||||
let contextData = {};
|
||||
if (templateQueryToExecute) {
|
||||
const { data } = await client.query({
|
||||
query: gql(templateQueryToExecute),
|
||||
variables: { ...templateObject.variables }
|
||||
});
|
||||
contextData = data;
|
||||
}
|
||||
return { contextData, useShopSpecificTemplate };
|
||||
}
|
||||
|
||||
return await generateTemplate(templateQueryToExecute, templateObject, useShopSpecificTemplate);
|
||||
};
|
||||
|
||||
//export const displayTemplateInWindow = (html) => {
|
||||
@@ -467,21 +427,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] = {};
|
||||
}
|
||||
result[key] = extend(result[key], obj[key]);
|
||||
} else {
|
||||
result[key] = obj[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];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,45 +1,36 @@
|
||||
import _ from "lodash";
|
||||
|
||||
export const CheckJobBucket = (buckets, job) => {
|
||||
const jobHours =
|
||||
job.labhrs.aggregate.sum.mod_lb_hrs + job.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
const jobHours = job.labhrs.aggregate.sum.mod_lb_hrs + job.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
|
||||
const matchingBucket = buckets.filter((b) =>
|
||||
b.gte <= jobHours && b.lt ? b.lt > jobHours : true
|
||||
);
|
||||
const matchingBucket = buckets.filter((b) => (b.gte <= jobHours && b.lt ? b.lt > jobHours : true));
|
||||
|
||||
return matchingBucket[0] && matchingBucket[0].id;
|
||||
return matchingBucket[0] && matchingBucket[0].id;
|
||||
};
|
||||
|
||||
export const CalculateLoad = (currentLoad, buckets, jobsIn, jobsOut) => {
|
||||
//Add the jobs coming
|
||||
const newLoad = _.cloneDeep(currentLoad);
|
||||
jobsIn.forEach((job) => {
|
||||
const bucketId = CheckJobBucket(buckets, job);
|
||||
if (bucketId) {
|
||||
newLoad[bucketId].count = newLoad[bucketId].count + 1;
|
||||
} else {
|
||||
console.log(
|
||||
"[Util Arr Job]Uh oh, this job doesn't fit in a bucket!",
|
||||
job
|
||||
);
|
||||
}
|
||||
});
|
||||
//Add the jobs coming
|
||||
const newLoad = _.cloneDeep(currentLoad);
|
||||
jobsIn.forEach((job) => {
|
||||
const bucketId = CheckJobBucket(buckets, job);
|
||||
if (bucketId) {
|
||||
newLoad[bucketId].count = newLoad[bucketId].count + 1;
|
||||
} else {
|
||||
console.log("[Util Arr Job]Uh oh, this job doesn't fit in a bucket!", job);
|
||||
}
|
||||
});
|
||||
|
||||
jobsOut.forEach((job) => {
|
||||
const bucketId = CheckJobBucket(buckets, job);
|
||||
if (bucketId) {
|
||||
newLoad[bucketId].count = newLoad[bucketId].count - 1;
|
||||
if (newLoad[bucketId].count < 0) {
|
||||
console.log("***ERROR: NEGATIVE LOAD", bucketId, job);
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
"[Util Out Job]Uh oh, this job doesn't fit in a bucket!",
|
||||
job
|
||||
);
|
||||
}
|
||||
});
|
||||
jobsOut.forEach((job) => {
|
||||
const bucketId = CheckJobBucket(buckets, job);
|
||||
if (bucketId) {
|
||||
newLoad[bucketId].count = newLoad[bucketId].count - 1;
|
||||
if (newLoad[bucketId].count < 0) {
|
||||
console.log("***ERROR: NEGATIVE LOAD", bucketId, job);
|
||||
}
|
||||
} else {
|
||||
console.log("[Util Out Job]Uh oh, this job doesn't fit in a bucket!", job);
|
||||
}
|
||||
});
|
||||
|
||||
return newLoad;
|
||||
return newLoad;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
|
||||
export function PartsLabelMulti() {
|
||||
return <div></div>;
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
@@ -1,148 +1,139 @@
|
||||
export const MockBodyshop = {
|
||||
address1: "123 Fake St",
|
||||
address2: "Unit #100",
|
||||
city: "Vancouver",
|
||||
country: "Canada",
|
||||
created_at: "2019-12-10T20:03:06.420853+00:00",
|
||||
email: "snaptsoft@gmail.com",
|
||||
federal_tax_id: "GST10150492",
|
||||
id: "52b7357c-0edd-4c95-85c3-dfdbcdfad9ac",
|
||||
insurance_vendor_id: "F123456",
|
||||
logo_img_path: "https://www.snapt.ca/assets/logo-placeholder.png",
|
||||
md_ro_statuses: {
|
||||
statuses: [
|
||||
"Open",
|
||||
"Scheduled",
|
||||
"Arrived",
|
||||
"Repair Plan",
|
||||
"Parts",
|
||||
"Body",
|
||||
"Prep",
|
||||
"Paint",
|
||||
"Reassembly",
|
||||
"Sublet",
|
||||
"Detail",
|
||||
"Completed",
|
||||
"Delivered",
|
||||
"Invoiced",
|
||||
"Exported",
|
||||
],
|
||||
open_statuses: [
|
||||
"Open",
|
||||
"Scheduled",
|
||||
"Arrived",
|
||||
"Repair Plan",
|
||||
"Parts",
|
||||
"Body",
|
||||
"Prep",
|
||||
"Paint",
|
||||
],
|
||||
default_arrived: "Arrived",
|
||||
default_exported: "Exported",
|
||||
default_imported: "Open",
|
||||
default_invoiced: "Invoiced",
|
||||
default_completed: "Completed",
|
||||
default_delivered: "Delivered",
|
||||
default_scheduled: "Scheduled",
|
||||
},
|
||||
md_order_statuses: {
|
||||
statuses: ["Ordered", "Received", "Canceled", "Backordered"],
|
||||
default_bo: "Backordered",
|
||||
default_ordered: "Ordered",
|
||||
default_canceled: "Canceled",
|
||||
default_received: "Received",
|
||||
},
|
||||
shopname: "Testing Collision",
|
||||
state: "BC",
|
||||
state_tax_id: "PST1000-2991",
|
||||
updated_at: "2020-03-23T22:06:03.509544+00:00",
|
||||
zip_post: "V6B 1M9",
|
||||
region_config: "CA_BC",
|
||||
md_responsibility_centers: {
|
||||
costs: [
|
||||
"Aftermarket",
|
||||
"ATS",
|
||||
"Body",
|
||||
"Detail",
|
||||
"Daignostic",
|
||||
"Electrical",
|
||||
"Chrome",
|
||||
"Frame",
|
||||
"Mechanical",
|
||||
"Refinish",
|
||||
"Structural",
|
||||
"Existing",
|
||||
"Glass",
|
||||
"LKQ",
|
||||
"OEM",
|
||||
"OEM Partial",
|
||||
"Re-cored",
|
||||
"Remanufactured",
|
||||
"Other",
|
||||
"Sublet",
|
||||
"Towing",
|
||||
],
|
||||
profits: [
|
||||
"Aftermarket",
|
||||
"ATS",
|
||||
"Body",
|
||||
"Detail",
|
||||
"Daignostic",
|
||||
"Electrical",
|
||||
"Chrome",
|
||||
"Frame",
|
||||
"Mechanical",
|
||||
"Refinish",
|
||||
"Structural",
|
||||
"Existing",
|
||||
"Glass",
|
||||
"LKQ",
|
||||
"OEM",
|
||||
"OEM Partial",
|
||||
"Re-cored",
|
||||
"Remanufactured",
|
||||
"Other",
|
||||
"Sublet",
|
||||
"Towing",
|
||||
],
|
||||
defaults: {
|
||||
ATS: "ATS",
|
||||
LAB: "Body",
|
||||
LAD: "Diagnostic",
|
||||
LAE: "Electrical",
|
||||
LAF: "Frame",
|
||||
LAG: "Glass",
|
||||
LAM: "Mechanical",
|
||||
LAR: "Refinish",
|
||||
LAS: "Structural",
|
||||
LAU: "Detail",
|
||||
PAA: "Aftermarket",
|
||||
PAC: "Chrome",
|
||||
PAL: "LKQ",
|
||||
PAM: "Remanufactured",
|
||||
PAN: "OEM",
|
||||
PAO: "Other",
|
||||
PAP: "OEM Partial",
|
||||
PAR: "16",
|
||||
TOW: "Towing",
|
||||
},
|
||||
},
|
||||
employees: [
|
||||
{
|
||||
id: "075b744c-8919-49ca-abb2-ccd51040326d",
|
||||
first_name: "Patrick",
|
||||
last_name: "BODY123",
|
||||
employee_number: "101",
|
||||
cost_center: "Body",
|
||||
__typename: "employees",
|
||||
},
|
||||
{
|
||||
id: "8cc787d3-1cfe-49d3-8a15-8469cd5c2e41",
|
||||
first_name: "Patrick",
|
||||
last_name: "Painter",
|
||||
employee_number: "10211",
|
||||
cost_center: "REFINISH",
|
||||
__typename: "employees",
|
||||
},
|
||||
address1: "123 Fake St",
|
||||
address2: "Unit #100",
|
||||
city: "Vancouver",
|
||||
country: "Canada",
|
||||
created_at: "2019-12-10T20:03:06.420853+00:00",
|
||||
email: "snaptsoft@gmail.com",
|
||||
federal_tax_id: "GST10150492",
|
||||
id: "52b7357c-0edd-4c95-85c3-dfdbcdfad9ac",
|
||||
insurance_vendor_id: "F123456",
|
||||
logo_img_path: "https://www.snapt.ca/assets/logo-placeholder.png",
|
||||
md_ro_statuses: {
|
||||
statuses: [
|
||||
"Open",
|
||||
"Scheduled",
|
||||
"Arrived",
|
||||
"Repair Plan",
|
||||
"Parts",
|
||||
"Body",
|
||||
"Prep",
|
||||
"Paint",
|
||||
"Reassembly",
|
||||
"Sublet",
|
||||
"Detail",
|
||||
"Completed",
|
||||
"Delivered",
|
||||
"Invoiced",
|
||||
"Exported"
|
||||
],
|
||||
open_statuses: ["Open", "Scheduled", "Arrived", "Repair Plan", "Parts", "Body", "Prep", "Paint"],
|
||||
default_arrived: "Arrived",
|
||||
default_exported: "Exported",
|
||||
default_imported: "Open",
|
||||
default_invoiced: "Invoiced",
|
||||
default_completed: "Completed",
|
||||
default_delivered: "Delivered",
|
||||
default_scheduled: "Scheduled"
|
||||
},
|
||||
md_order_statuses: {
|
||||
statuses: ["Ordered", "Received", "Canceled", "Backordered"],
|
||||
default_bo: "Backordered",
|
||||
default_ordered: "Ordered",
|
||||
default_canceled: "Canceled",
|
||||
default_received: "Received"
|
||||
},
|
||||
shopname: "Testing Collision",
|
||||
state: "BC",
|
||||
state_tax_id: "PST1000-2991",
|
||||
updated_at: "2020-03-23T22:06:03.509544+00:00",
|
||||
zip_post: "V6B 1M9",
|
||||
region_config: "CA_BC",
|
||||
md_responsibility_centers: {
|
||||
costs: [
|
||||
"Aftermarket",
|
||||
"ATS",
|
||||
"Body",
|
||||
"Detail",
|
||||
"Daignostic",
|
||||
"Electrical",
|
||||
"Chrome",
|
||||
"Frame",
|
||||
"Mechanical",
|
||||
"Refinish",
|
||||
"Structural",
|
||||
"Existing",
|
||||
"Glass",
|
||||
"LKQ",
|
||||
"OEM",
|
||||
"OEM Partial",
|
||||
"Re-cored",
|
||||
"Remanufactured",
|
||||
"Other",
|
||||
"Sublet",
|
||||
"Towing"
|
||||
],
|
||||
profits: [
|
||||
"Aftermarket",
|
||||
"ATS",
|
||||
"Body",
|
||||
"Detail",
|
||||
"Daignostic",
|
||||
"Electrical",
|
||||
"Chrome",
|
||||
"Frame",
|
||||
"Mechanical",
|
||||
"Refinish",
|
||||
"Structural",
|
||||
"Existing",
|
||||
"Glass",
|
||||
"LKQ",
|
||||
"OEM",
|
||||
"OEM Partial",
|
||||
"Re-cored",
|
||||
"Remanufactured",
|
||||
"Other",
|
||||
"Sublet",
|
||||
"Towing"
|
||||
],
|
||||
defaults: {
|
||||
ATS: "ATS",
|
||||
LAB: "Body",
|
||||
LAD: "Diagnostic",
|
||||
LAE: "Electrical",
|
||||
LAF: "Frame",
|
||||
LAG: "Glass",
|
||||
LAM: "Mechanical",
|
||||
LAR: "Refinish",
|
||||
LAS: "Structural",
|
||||
LAU: "Detail",
|
||||
PAA: "Aftermarket",
|
||||
PAC: "Chrome",
|
||||
PAL: "LKQ",
|
||||
PAM: "Remanufactured",
|
||||
PAN: "OEM",
|
||||
PAO: "Other",
|
||||
PAP: "OEM Partial",
|
||||
PAR: "16",
|
||||
TOW: "Towing"
|
||||
}
|
||||
},
|
||||
employees: [
|
||||
{
|
||||
id: "075b744c-8919-49ca-abb2-ccd51040326d",
|
||||
first_name: "Patrick",
|
||||
last_name: "BODY123",
|
||||
employee_number: "101",
|
||||
cost_center: "Body",
|
||||
__typename: "employees"
|
||||
},
|
||||
{
|
||||
id: "8cc787d3-1cfe-49d3-8a15-8469cd5c2e41",
|
||||
first_name: "Patrick",
|
||||
last_name: "Painter",
|
||||
employee_number: "10211",
|
||||
cost_center: "REFINISH",
|
||||
__typename: "employees"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,154 +1,141 @@
|
||||
(function (global) {
|
||||
var parse = function (data) {
|
||||
data = data.replace(/\n/, "");
|
||||
// replace spaces with regular space
|
||||
data = data.replace(/\s/g, " ");
|
||||
var parse = function (data) {
|
||||
data = data.replace(/\n/, "");
|
||||
// replace spaces with regular space
|
||||
data = data.replace(/\s/g, " ");
|
||||
|
||||
if (/^@/.test(data) === true) {
|
||||
return pdf417(data);
|
||||
} else if (/^%/.test(data) === true) {
|
||||
return stripe(data);
|
||||
} else {
|
||||
console.log("couldnt identify format");
|
||||
}
|
||||
};
|
||||
if (/^@/.test(data) === true) {
|
||||
return pdf417(data);
|
||||
} else if (/^%/.test(data) === true) {
|
||||
return stripe(data);
|
||||
} else {
|
||||
console.log("couldnt identify format");
|
||||
}
|
||||
};
|
||||
|
||||
var parseDate = function (date) {
|
||||
var start = parseInt(date[0] + date[1]);
|
||||
if (start < 13) {
|
||||
return (
|
||||
date[4] +
|
||||
date[5] +
|
||||
date[6] +
|
||||
date[7] +
|
||||
date[0] +
|
||||
date[1] +
|
||||
date[2] +
|
||||
date[3]
|
||||
);
|
||||
}
|
||||
return date;
|
||||
};
|
||||
var parseDate = function (date) {
|
||||
var start = parseInt(date[0] + date[1]);
|
||||
if (start < 13) {
|
||||
return date[4] + date[5] + date[6] + date[7] + date[0] + date[1] + date[2] + date[3];
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
var stripe = function (data) {
|
||||
data = data.replace(/\n/, "");
|
||||
// replace spaces with regular space
|
||||
data = data.replace(/\s/g, " ");
|
||||
var track = data.match(/(.*?\?)(.*?\?)(.*?\?)/);
|
||||
var res1 = track[1].match(
|
||||
/(%)([A-Z]{2})([^^]{0,13})\^?([^^]{0,35})\^?([^^]{0,60})\^?\s*?\?/
|
||||
);
|
||||
var res2 = track[2].match(
|
||||
/(;)(\d{6})(\d{0,13})(=)(\d{4})(\d{8})(\d{0,5})=?\?/
|
||||
);
|
||||
var res3 = track[3].match(
|
||||
/(#|%|\+)(\d|!|")(\d|\s|.)([0-9A-Z ]{11})([0-9A-Z ]{2})([0-9A-Z ]{10})([0-9A-Z ]{4})([12MF ]{1})([0-9A-Z ]{3})([0-9A-Z ]{3})([0-9A-Z ]{3})([0-9A-Z ]{3})(.*?)\?/
|
||||
);
|
||||
var state = res1[2];
|
||||
var stripe = function (data) {
|
||||
data = data.replace(/\n/, "");
|
||||
// replace spaces with regular space
|
||||
data = data.replace(/\s/g, " ");
|
||||
var track = data.match(/(.*?\?)(.*?\?)(.*?\?)/);
|
||||
var res1 = track[1].match(/(%)([A-Z]{2})([^^]{0,13})\^?([^^]{0,35})\^?([^^]{0,60})\^?\s*?\?/);
|
||||
var res2 = track[2].match(/(;)(\d{6})(\d{0,13})(=)(\d{4})(\d{8})(\d{0,5})=?\?/);
|
||||
var res3 = track[3].match(
|
||||
/(#|%|\+)(\d|!|")(\d|\s|.)([0-9A-Z ]{11})([0-9A-Z ]{2})([0-9A-Z ]{10})([0-9A-Z ]{4})([12MF ]{1})([0-9A-Z ]{3})([0-9A-Z ]{3})([0-9A-Z ]{3})([0-9A-Z ]{3})(.*?)\?/
|
||||
);
|
||||
var state = res1[2];
|
||||
return {
|
||||
state: state,
|
||||
city: res1[3],
|
||||
name: (function () {
|
||||
var res = res1[4].match(/([^$]{0,35})\$?([^$]{0,35})?\$?([^$]{0,35})?/);
|
||||
if (!res) return;
|
||||
return {
|
||||
state: state,
|
||||
city: res1[3],
|
||||
name: (function () {
|
||||
var res = res1[4].match(/([^$]{0,35})\$?([^$]{0,35})?\$?([^$]{0,35})?/);
|
||||
if (!res) return;
|
||||
return {
|
||||
last: res[1],
|
||||
first: res[2],
|
||||
middle: res[3],
|
||||
};
|
||||
})(),
|
||||
address: res1[5],
|
||||
iso_iin: res2[2],
|
||||
dl: res2[3],
|
||||
expiration_date: parseDate(res2[5]),
|
||||
birthday: (function () {
|
||||
var dob = res2[6].match(/(\d{4})(\d{2})(\d{2})/);
|
||||
if (!dob) return;
|
||||
last: res[1],
|
||||
first: res[2],
|
||||
middle: res[3]
|
||||
};
|
||||
})(),
|
||||
address: res1[5],
|
||||
iso_iin: res2[2],
|
||||
dl: res2[3],
|
||||
expiration_date: parseDate(res2[5]),
|
||||
birthday: (function () {
|
||||
var dob = res2[6].match(/(\d{4})(\d{2})(\d{2})/);
|
||||
if (!dob) return;
|
||||
|
||||
if (dob[2] === "99") {
|
||||
/* FL decided to reverse 2012 aamva spec, 99 means here
|
||||
if (dob[2] === "99") {
|
||||
/* FL decided to reverse 2012 aamva spec, 99 means here
|
||||
that dob month === to expiration month, it should be
|
||||
opposite
|
||||
*/
|
||||
var exp_dt = res2[5].match(/(\d{2})(\d{2})/);
|
||||
dob[2] = exp_dt[2];
|
||||
}
|
||||
//dob[2]--; what was this for?
|
||||
return dob[1] + dob[2] + dob[3];
|
||||
})(),
|
||||
dl_overflow: res2[7],
|
||||
cds_version: res3[1],
|
||||
jurisdiction_version: res3[2],
|
||||
postal_code: res3[4],
|
||||
klass: res3[5],
|
||||
class: res3[5],
|
||||
restrictions: res3[6],
|
||||
endorsments: res3[7],
|
||||
sex: (function () {
|
||||
switch (res3[8]) {
|
||||
case "1":
|
||||
case "M":
|
||||
return "MALE";
|
||||
var exp_dt = res2[5].match(/(\d{2})(\d{2})/);
|
||||
dob[2] = exp_dt[2];
|
||||
}
|
||||
//dob[2]--; what was this for?
|
||||
return dob[1] + dob[2] + dob[3];
|
||||
})(),
|
||||
dl_overflow: res2[7],
|
||||
cds_version: res3[1],
|
||||
jurisdiction_version: res3[2],
|
||||
postal_code: res3[4],
|
||||
klass: res3[5],
|
||||
class: res3[5],
|
||||
restrictions: res3[6],
|
||||
endorsments: res3[7],
|
||||
sex: (function () {
|
||||
switch (res3[8]) {
|
||||
case "1":
|
||||
case "M":
|
||||
return "MALE";
|
||||
|
||||
case "2":
|
||||
case "F":
|
||||
return "FEMALE";
|
||||
case "2":
|
||||
case "F":
|
||||
return "FEMALE";
|
||||
|
||||
default:
|
||||
return res3[8] + ": MISSING/INVALID";
|
||||
}
|
||||
})(),
|
||||
height: res3[9],
|
||||
weight: res3[10],
|
||||
hair_color: res3[11],
|
||||
eye_color: res3[12],
|
||||
misc: res3[13],
|
||||
id: (function () {
|
||||
var id;
|
||||
switch (state) {
|
||||
case "FL":
|
||||
var res = res2[3].match(/(\d{2})(.*)/);
|
||||
if (!res) return;
|
||||
id = String.fromCharCode(Number(res[1]) + 64) + res[2] + res2[7];
|
||||
break;
|
||||
default:
|
||||
id = res2[3];
|
||||
break;
|
||||
}
|
||||
return id;
|
||||
})(),
|
||||
};
|
||||
default:
|
||||
return res3[8] + ": MISSING/INVALID";
|
||||
}
|
||||
})(),
|
||||
height: res3[9],
|
||||
weight: res3[10],
|
||||
hair_color: res3[11],
|
||||
eye_color: res3[12],
|
||||
misc: res3[13],
|
||||
id: (function () {
|
||||
var id;
|
||||
switch (state) {
|
||||
case "FL":
|
||||
var res = res2[3].match(/(\d{2})(.*)/);
|
||||
if (!res) return;
|
||||
id = String.fromCharCode(Number(res[1]) + 64) + res[2] + res2[7];
|
||||
break;
|
||||
default:
|
||||
id = res2[3];
|
||||
break;
|
||||
}
|
||||
return id;
|
||||
})()
|
||||
};
|
||||
};
|
||||
|
||||
var pdf417 = function (data) {
|
||||
data = data.replace(/\n/, "");
|
||||
// replace spaces with regular space
|
||||
data = data.replace(/\s/g, " ");
|
||||
var pdf417 = function (data) {
|
||||
data = data.replace(/\n/, "");
|
||||
// replace spaces with regular space
|
||||
data = data.replace(/\s/g, " ");
|
||||
|
||||
// get version of aamva (before 2000 or after)
|
||||
var version = data.match(/[A-Z ]{5}\d{6}(\d{2})/);
|
||||
// get version of aamva (before 2000 or after)
|
||||
var version = data.match(/[A-Z ]{5}\d{6}(\d{2})/);
|
||||
|
||||
var parseRegex;
|
||||
var parseRegex;
|
||||
|
||||
/* version 01 year 2000 */
|
||||
switch (Number(version[1])) {
|
||||
case 1: {
|
||||
parseRegex = new RegExp(
|
||||
"(DAQ.*?)?" + // Drivers license number
|
||||
"(DAA.*?)?" + // Driver License Name
|
||||
"(DAG.*?)?" + // Driver Mailing Street Address
|
||||
"(DAI.*?)?" + // Driver Mailing City
|
||||
"(DAJ.*?)?" + // Driver Mailing Jurisdiction Code
|
||||
"(DAK.*?)?" + // Driver Mailing Postal Code
|
||||
"(DAQ.*?)?" + // Driver License/ID Number
|
||||
"(DAR.*?)?" + // Driver License Classification Code
|
||||
"(DAS.*?)?" + // Driver License Restriction Code
|
||||
"(DAT.*?)?" + // Driver License Endorsements Code
|
||||
"(DBA.*?)?" + // Driver License Expiration Date
|
||||
"(DBB.*?)?" + // Date of Birth
|
||||
"(DBC.*?)?" + // Driver Sex
|
||||
"(DBD.*?)?" + // Driver License or ID Document Issue Date
|
||||
/* optional
|
||||
/* version 01 year 2000 */
|
||||
switch (Number(version[1])) {
|
||||
case 1: {
|
||||
parseRegex = new RegExp(
|
||||
"(DAQ.*?)?" + // Drivers license number
|
||||
"(DAA.*?)?" + // Driver License Name
|
||||
"(DAG.*?)?" + // Driver Mailing Street Address
|
||||
"(DAI.*?)?" + // Driver Mailing City
|
||||
"(DAJ.*?)?" + // Driver Mailing Jurisdiction Code
|
||||
"(DAK.*?)?" + // Driver Mailing Postal Code
|
||||
"(DAQ.*?)?" + // Driver License/ID Number
|
||||
"(DAR.*?)?" + // Driver License Classification Code
|
||||
"(DAS.*?)?" + // Driver License Restriction Code
|
||||
"(DAT.*?)?" + // Driver License Endorsements Code
|
||||
"(DBA.*?)?" + // Driver License Expiration Date
|
||||
"(DBB.*?)?" + // Date of Birth
|
||||
"(DBC.*?)?" + // Driver Sex
|
||||
"(DBD.*?)?" + // Driver License or ID Document Issue Date
|
||||
/* optional
|
||||
'(DAU.*?)?' + // Height (FT/IN)
|
||||
'(DAW.*?)?' + // Weight (LBS)
|
||||
'(DAY.*?)?' + // Eye Color
|
||||
@@ -188,35 +175,35 @@
|
||||
'(DBR.*?)?' + // Driver "AKA" Suffix
|
||||
'(DBS.*?)?' // Driver "AKA" Prefix
|
||||
*/
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
/* version 02 year 2003 */
|
||||
case 2: {
|
||||
parseRegex = new RegExp(
|
||||
"(DCA.*?)?" + // Jurisdiction-specific vehicle class
|
||||
"(DCB.*?)?" + // Jurisdiction-specific restriction codes
|
||||
"(DCD.*?)?" + // Jurisdiction-specific endorsement codes
|
||||
"(DBA.*?)?" + // Document Expiration Date
|
||||
"(DCS.*?)?" + // Customer Family Name
|
||||
"(DCT.*?)?" + // Customer Given Names
|
||||
"(DCU.*?)?" + // Name Suffix
|
||||
"(DBD.*?)?" + // Document Issue Date
|
||||
"(DBB.*?)?" + // Date of Birth
|
||||
"(DBC.*?)?" + // Physical Description – Sex
|
||||
"(DAY.*?)?" + // Physical Description – Eye Color
|
||||
"(DAU.*?)?" + // Physical Description – Height
|
||||
"(DCE.*?)?" + // Physical Description – Weight Range
|
||||
"(DAG.*?)?" + // Address – Street 1
|
||||
"(DAI.*?)?" + // Address – City
|
||||
"(DAJ.*?)?" + // Address – Jurisdiction Code
|
||||
"(DAK.*?)?" + // Address – Postal Code
|
||||
"(DAQ.*?)?" + // Customer ID Number
|
||||
"(DCF.*?)?" + // Document Discriminator
|
||||
"(DCG.*?)?" + // Country Identification
|
||||
"(DCH.*?)?" + // Federal Commercial Vehicle Codes
|
||||
/* optional elements
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
/* version 02 year 2003 */
|
||||
case 2: {
|
||||
parseRegex = new RegExp(
|
||||
"(DCA.*?)?" + // Jurisdiction-specific vehicle class
|
||||
"(DCB.*?)?" + // Jurisdiction-specific restriction codes
|
||||
"(DCD.*?)?" + // Jurisdiction-specific endorsement codes
|
||||
"(DBA.*?)?" + // Document Expiration Date
|
||||
"(DCS.*?)?" + // Customer Family Name
|
||||
"(DCT.*?)?" + // Customer Given Names
|
||||
"(DCU.*?)?" + // Name Suffix
|
||||
"(DBD.*?)?" + // Document Issue Date
|
||||
"(DBB.*?)?" + // Date of Birth
|
||||
"(DBC.*?)?" + // Physical Description – Sex
|
||||
"(DAY.*?)?" + // Physical Description – Eye Color
|
||||
"(DAU.*?)?" + // Physical Description – Height
|
||||
"(DCE.*?)?" + // Physical Description – Weight Range
|
||||
"(DAG.*?)?" + // Address – Street 1
|
||||
"(DAI.*?)?" + // Address – City
|
||||
"(DAJ.*?)?" + // Address – Jurisdiction Code
|
||||
"(DAK.*?)?" + // Address – Postal Code
|
||||
"(DAQ.*?)?" + // Customer ID Number
|
||||
"(DCF.*?)?" + // Document Discriminator
|
||||
"(DCG.*?)?" + // Country Identification
|
||||
"(DCH.*?)?" + // Federal Commercial Vehicle Codes
|
||||
/* optional elements
|
||||
'(DAH.*?)?' + // Address – Street 2
|
||||
'(DAZ.*?)?' + // Hair color
|
||||
'(DCI.*?)?' + // Place of birth
|
||||
@@ -232,33 +219,33 @@
|
||||
'(DCQ.*?)?' + // Jurisdiction- specific endorsement code description
|
||||
'(DCR.*?)?' // Jurisdiction- specific restriction code description
|
||||
*/
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
/* version 03 year 2005 */
|
||||
case 3: {
|
||||
parseRegex = new RegExp(
|
||||
"(DCA.*?)?" + // Jurisdiction-specific vehicle class
|
||||
"(DCB.*?)?" + // Jurisdiction-specific restriction codes
|
||||
"(DCD.*?)?" + // Jurisdiction-specific endorsement codes
|
||||
"(DBA.*?)?" + // Document Expiration Date
|
||||
"(DCS.*?)?" + // Customer Family Name
|
||||
"(DCT.*?)?" + // Customer Given Names
|
||||
"(DBD.*?)?" + // Document Issue Date
|
||||
"(DBB.*?)?" + // Date of Birth
|
||||
"(DBC.*?)?" + // Physical Description – Sex
|
||||
"(DAY.*?)?" + // Physical Description – Eye Color
|
||||
"(DAU.*?)?" + // Physical Description – Height
|
||||
"(DAG.*?)?" + // Address – Street 1
|
||||
"(DAI.*?)?" + // Address – City
|
||||
"(DAJ.*?)?" + // Address – Jurisdiction Code
|
||||
"(DAK.*?)?" + // Address – Postal Code
|
||||
"(DAQ.*?)?" + // Customer ID Number
|
||||
"(DCF.*?)?" + // Document Discriminator
|
||||
"(DCG.*?)?" + // Country Identification
|
||||
"(DCH.*?)?" + // Federal Commercial Vehicle Codes
|
||||
/* optional elements
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
/* version 03 year 2005 */
|
||||
case 3: {
|
||||
parseRegex = new RegExp(
|
||||
"(DCA.*?)?" + // Jurisdiction-specific vehicle class
|
||||
"(DCB.*?)?" + // Jurisdiction-specific restriction codes
|
||||
"(DCD.*?)?" + // Jurisdiction-specific endorsement codes
|
||||
"(DBA.*?)?" + // Document Expiration Date
|
||||
"(DCS.*?)?" + // Customer Family Name
|
||||
"(DCT.*?)?" + // Customer Given Names
|
||||
"(DBD.*?)?" + // Document Issue Date
|
||||
"(DBB.*?)?" + // Date of Birth
|
||||
"(DBC.*?)?" + // Physical Description – Sex
|
||||
"(DAY.*?)?" + // Physical Description – Eye Color
|
||||
"(DAU.*?)?" + // Physical Description – Height
|
||||
"(DAG.*?)?" + // Address – Street 1
|
||||
"(DAI.*?)?" + // Address – City
|
||||
"(DAJ.*?)?" + // Address – Jurisdiction Code
|
||||
"(DAK.*?)?" + // Address – Postal Code
|
||||
"(DAQ.*?)?" + // Customer ID Number
|
||||
"(DCF.*?)?" + // Document Discriminator
|
||||
"(DCG.*?)?" + // Country Identification
|
||||
"(DCH.*?)?" + // Federal Commercial Vehicle Codes
|
||||
/* optional elements
|
||||
+ '(DAH.*?)?' + // Address – Street 2
|
||||
'(DAZ.*?)?' + // Hair color
|
||||
'(DCI.*?)?' + // Place of birth
|
||||
@@ -277,64 +264,64 @@
|
||||
'(DCQ.*?)?' + // Jurisdiction- specific endorsement code description
|
||||
'(DCR.*?)?' // Jurisdiction- specific restriction code description
|
||||
*/
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
parseRegex = new RegExp(
|
||||
"(DAQ.*?)?" +
|
||||
"(DCS.*?)?" +
|
||||
"(DDE.*?)?" +
|
||||
"(DAC.*?)?" +
|
||||
"(DDF.*?)?" +
|
||||
"(DAD.*?)?" +
|
||||
"(DDG.*?)?" +
|
||||
"(DCA.*?)?" +
|
||||
"(DCB.*?)?" +
|
||||
"(DCD.*?)?" +
|
||||
"(DBD.*?)?" +
|
||||
"(DBB.*?)?" +
|
||||
"(DBA.*?)?" +
|
||||
"(DBC.*?)?" +
|
||||
"(DAU.*?)?" +
|
||||
"(DAY.*?)?" +
|
||||
"(DAG.*?)?" +
|
||||
"(DAI.*?)?" +
|
||||
"(DAJ.*?)?" +
|
||||
"(DAK.*?)?" +
|
||||
"(DCF.*?)?" +
|
||||
/* optional */
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
/* version 07 year 2012 */
|
||||
case 7: {
|
||||
parseRegex = new RegExp(
|
||||
"(DCA.*?)?" + // Jurisdiction-specific vehicle class
|
||||
"(DCB.*?)?" + // Jurisdiction-specific restriction codes
|
||||
"(DCD.*?)?" + // Jurisdiction-specific endorsement codes
|
||||
"(DBA.*?)?" + // Document Expiration Date
|
||||
"(DCS.*?)?" + // Customer Family Name
|
||||
"(DAC.*?)?" + // Customer First Name
|
||||
"(DAD.*?)?" + // Customer Middle Name(s)
|
||||
"(DBD.*?)?" + // Document Issue Date
|
||||
"(DBB.*?)?" + // Date of Birth
|
||||
"(DBC.*?)?" + // Physical Description – Sex
|
||||
"(DAY.*?)?" + // Physical Description – Eye Color
|
||||
"(DAU.*?)?" + // Physical Description – Height
|
||||
"(DAG.*?)?" + // Address – Street 1
|
||||
"(DAI.*?)?" + // Address – City
|
||||
"(DAJ.*?)?" + // Address – Jurisdiction Code
|
||||
"(DAK.*?)?" + // Address – Postal Code
|
||||
"(DAQ.*?)?" + // Customer ID Number
|
||||
"(DCF.*?)?" + // Document Discriminator
|
||||
"(DCG.*?)?" + // Country Identification
|
||||
"(DDE.*?)?" + // Family name truncation
|
||||
"(DDF.*?)?" + // First name truncation
|
||||
"(DDG.*?)?" + // Middle name truncation
|
||||
/* optional elements
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
parseRegex = new RegExp(
|
||||
"(DAQ.*?)?" +
|
||||
"(DCS.*?)?" +
|
||||
"(DDE.*?)?" +
|
||||
"(DAC.*?)?" +
|
||||
"(DDF.*?)?" +
|
||||
"(DAD.*?)?" +
|
||||
"(DDG.*?)?" +
|
||||
"(DCA.*?)?" +
|
||||
"(DCB.*?)?" +
|
||||
"(DCD.*?)?" +
|
||||
"(DBD.*?)?" +
|
||||
"(DBB.*?)?" +
|
||||
"(DBA.*?)?" +
|
||||
"(DBC.*?)?" +
|
||||
"(DAU.*?)?" +
|
||||
"(DAY.*?)?" +
|
||||
"(DAG.*?)?" +
|
||||
"(DAI.*?)?" +
|
||||
"(DAJ.*?)?" +
|
||||
"(DAK.*?)?" +
|
||||
"(DCF.*?)?" +
|
||||
/* optional */
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
/* version 07 year 2012 */
|
||||
case 7: {
|
||||
parseRegex = new RegExp(
|
||||
"(DCA.*?)?" + // Jurisdiction-specific vehicle class
|
||||
"(DCB.*?)?" + // Jurisdiction-specific restriction codes
|
||||
"(DCD.*?)?" + // Jurisdiction-specific endorsement codes
|
||||
"(DBA.*?)?" + // Document Expiration Date
|
||||
"(DCS.*?)?" + // Customer Family Name
|
||||
"(DAC.*?)?" + // Customer First Name
|
||||
"(DAD.*?)?" + // Customer Middle Name(s)
|
||||
"(DBD.*?)?" + // Document Issue Date
|
||||
"(DBB.*?)?" + // Date of Birth
|
||||
"(DBC.*?)?" + // Physical Description – Sex
|
||||
"(DAY.*?)?" + // Physical Description – Eye Color
|
||||
"(DAU.*?)?" + // Physical Description – Height
|
||||
"(DAG.*?)?" + // Address – Street 1
|
||||
"(DAI.*?)?" + // Address – City
|
||||
"(DAJ.*?)?" + // Address – Jurisdiction Code
|
||||
"(DAK.*?)?" + // Address – Postal Code
|
||||
"(DAQ.*?)?" + // Customer ID Number
|
||||
"(DCF.*?)?" + // Document Discriminator
|
||||
"(DCG.*?)?" + // Country Identification
|
||||
"(DDE.*?)?" + // Family name truncation
|
||||
"(DDF.*?)?" + // First name truncation
|
||||
"(DDG.*?)?" + // Middle name truncation
|
||||
/* optional elements
|
||||
'(DAH.*?)?' + // Address – Street 2
|
||||
'(DAZ.*?)?' + // Hair color
|
||||
'(DCI.*?)?' + // Place of birth
|
||||
@@ -364,195 +351,195 @@
|
||||
'(DDK.*?)?' + // Organ Donor Indicator
|
||||
'(DDL.*?)?' // Veteran Indicator
|
||||
*/
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
case 9: {
|
||||
var prefixes = [
|
||||
"DCA", // jurisdiction vehicle class
|
||||
"DCB", // jurisdiction restriction codes
|
||||
"DCD", // jurisdiction endorsement codes
|
||||
"DBA", // doc. expiration date
|
||||
"DCS", // customer family name
|
||||
"DAC", // first name
|
||||
"DAD", // middle names (comma seperated)
|
||||
"DBD", // doc. issue date
|
||||
"DBB", // date of birth (MMDDCCYY for U.S., CCYYMMDD for Canada)
|
||||
"DBC", // gender (1-name, 2-female, 9-not specified)
|
||||
"DAY", // eye color (ansi d-20 codes)
|
||||
"DAU", // height
|
||||
"DAG", // street 1
|
||||
"DAI", // city
|
||||
"DAJ", // state
|
||||
"DAK", // zip
|
||||
"DAQ", // customer id number
|
||||
"DCF", // doc. distriminator
|
||||
"DCG", // country identification (USA/CAN)
|
||||
"DDE", // last name truncated (T-trucated, N-not, U-unknown)
|
||||
"DDF", // first name truncated (T-trucated, N-not, U-unknown)
|
||||
"DDG", // middle name truncated (T-trucated, N-not, U-unknown)
|
||||
// optionals
|
||||
"DAH", // street address line 2
|
||||
"DAZ", // hair color
|
||||
"DCI", // place of birth
|
||||
"DCJ", // audit info
|
||||
"DCK", // inventory control number
|
||||
"DBN", // alias last name
|
||||
"DBG", // alias first name
|
||||
"DBS", // aliast suffix name
|
||||
"DCU", // name suffix . (JR, SR, 1ST, 2ND...)
|
||||
"DCE", // weight range
|
||||
"DCL", // race / ethnicity (AAMVA D20 code)
|
||||
"DCM", // vehicle classification
|
||||
"DCN", // standard endorsement code
|
||||
"DCO", // standard restriction code
|
||||
"DCP", // vehicle classification description
|
||||
"DCQ", // endorsement code description
|
||||
"DCR", // restriction code description
|
||||
"DDA", // compliance type
|
||||
"DDB", // card revision date
|
||||
"DDC", // hazmat endorsement exp. date
|
||||
"DDD", // limited duration doc. indicator
|
||||
"DAW", // weight lbs
|
||||
"DAX", // weight kg
|
||||
"DDH", // under 18 until, date turns 18 (MMDDCCYY for U.S., CCYYMMDD for Canada)
|
||||
"DDI", // under 19 until, date turns 19 (MMDDCCYY for U.S., CCYYMMDD for Canada)
|
||||
"DDJ", // under 21 until, date turns 21 (MMDDCCYY for U.S., CCYYMMDD for Canada)
|
||||
"DDK", // organ donor (1-yes)
|
||||
"DDL", // veteran indicator (1-yes)
|
||||
];
|
||||
var regExStr = "";
|
||||
var prefixIdxs = [];
|
||||
for (var i = 0; i < prefixes.length; i++) {
|
||||
var idx = data.indexOf(prefixes[i]);
|
||||
if (idx !== -1) {
|
||||
prefixIdxs.push({
|
||||
prefix: prefixes[i],
|
||||
index: idx,
|
||||
});
|
||||
}
|
||||
}
|
||||
// if prefixes are not in order as found in the string, the regex will not perform as expected
|
||||
prefixIdxs.sort((a, b) => (a.index > b.index ? 1 : -1));
|
||||
prefixIdxs.forEach((obj) => (regExStr += `(${obj.prefix}.*?)?`));
|
||||
regExStr += "$";
|
||||
|
||||
parseRegex = new RegExp(regExStr);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.log("unable to get version", version);
|
||||
// probably not a right parse...
|
||||
}
|
||||
"$"
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
case 9: {
|
||||
var prefixes = [
|
||||
"DCA", // jurisdiction vehicle class
|
||||
"DCB", // jurisdiction restriction codes
|
||||
"DCD", // jurisdiction endorsement codes
|
||||
"DBA", // doc. expiration date
|
||||
"DCS", // customer family name
|
||||
"DAC", // first name
|
||||
"DAD", // middle names (comma seperated)
|
||||
"DBD", // doc. issue date
|
||||
"DBB", // date of birth (MMDDCCYY for U.S., CCYYMMDD for Canada)
|
||||
"DBC", // gender (1-name, 2-female, 9-not specified)
|
||||
"DAY", // eye color (ansi d-20 codes)
|
||||
"DAU", // height
|
||||
"DAG", // street 1
|
||||
"DAI", // city
|
||||
"DAJ", // state
|
||||
"DAK", // zip
|
||||
"DAQ", // customer id number
|
||||
"DCF", // doc. distriminator
|
||||
"DCG", // country identification (USA/CAN)
|
||||
"DDE", // last name truncated (T-trucated, N-not, U-unknown)
|
||||
"DDF", // first name truncated (T-trucated, N-not, U-unknown)
|
||||
"DDG", // middle name truncated (T-trucated, N-not, U-unknown)
|
||||
// optionals
|
||||
"DAH", // street address line 2
|
||||
"DAZ", // hair color
|
||||
"DCI", // place of birth
|
||||
"DCJ", // audit info
|
||||
"DCK", // inventory control number
|
||||
"DBN", // alias last name
|
||||
"DBG", // alias first name
|
||||
"DBS", // aliast suffix name
|
||||
"DCU", // name suffix . (JR, SR, 1ST, 2ND...)
|
||||
"DCE", // weight range
|
||||
"DCL", // race / ethnicity (AAMVA D20 code)
|
||||
"DCM", // vehicle classification
|
||||
"DCN", // standard endorsement code
|
||||
"DCO", // standard restriction code
|
||||
"DCP", // vehicle classification description
|
||||
"DCQ", // endorsement code description
|
||||
"DCR", // restriction code description
|
||||
"DDA", // compliance type
|
||||
"DDB", // card revision date
|
||||
"DDC", // hazmat endorsement exp. date
|
||||
"DDD", // limited duration doc. indicator
|
||||
"DAW", // weight lbs
|
||||
"DAX", // weight kg
|
||||
"DDH", // under 18 until, date turns 18 (MMDDCCYY for U.S., CCYYMMDD for Canada)
|
||||
"DDI", // under 19 until, date turns 19 (MMDDCCYY for U.S., CCYYMMDD for Canada)
|
||||
"DDJ", // under 21 until, date turns 21 (MMDDCCYY for U.S., CCYYMMDD for Canada)
|
||||
"DDK", // organ donor (1-yes)
|
||||
"DDL" // veteran indicator (1-yes)
|
||||
];
|
||||
var regExStr = "";
|
||||
var prefixIdxs = [];
|
||||
for (var i = 0; i < prefixes.length; i++) {
|
||||
var idx = data.indexOf(prefixes[i]);
|
||||
if (idx !== -1) {
|
||||
prefixIdxs.push({
|
||||
prefix: prefixes[i],
|
||||
index: idx
|
||||
});
|
||||
}
|
||||
}
|
||||
// if prefixes are not in order as found in the string, the regex will not perform as expected
|
||||
prefixIdxs.sort((a, b) => (a.index > b.index ? 1 : -1));
|
||||
prefixIdxs.forEach((obj) => (regExStr += `(${obj.prefix}.*?)?`));
|
||||
regExStr += "$";
|
||||
|
||||
var parsedData = {};
|
||||
var res = data.match(parseRegex);
|
||||
parseRegex = new RegExp(regExStr);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.log("unable to get version", version);
|
||||
// probably not a right parse...
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 1; i < res.length; i++) {
|
||||
if (res[i] !== undefined) {
|
||||
parsedData[String(res[i]).substring(0, 3)] = res[i].substring(3).trim();
|
||||
}
|
||||
}
|
||||
var parsedData = {};
|
||||
var res = data.match(parseRegex);
|
||||
|
||||
var name;
|
||||
switch (Number(version[1])) {
|
||||
case 1: {
|
||||
// version one joining all of the names in one string
|
||||
name = parsedData.DAA.split(",");
|
||||
parsedData.DCS = name[0];
|
||||
parsedData.DAC = name[1];
|
||||
parsedData.DAD = name[2];
|
||||
for (i = 1; i < res.length; i++) {
|
||||
if (res[i] !== undefined) {
|
||||
parsedData[String(res[i]).substring(0, 3)] = res[i].substring(3).trim();
|
||||
}
|
||||
}
|
||||
|
||||
// drivers license class
|
||||
parsedData.DCA = parsedData.DAR;
|
||||
var name;
|
||||
switch (Number(version[1])) {
|
||||
case 1: {
|
||||
// version one joining all of the names in one string
|
||||
name = parsedData.DAA.split(",");
|
||||
parsedData.DCS = name[0];
|
||||
parsedData.DAC = name[1];
|
||||
parsedData.DAD = name[2];
|
||||
|
||||
// date on 01 is CCYYMMDD while on 07 MMDDCCYY
|
||||
parsedData.DBB =
|
||||
parsedData.DBB.substring(4, 6) + // month
|
||||
parsedData.DBB.substring(6, 8) + // day
|
||||
parsedData.DBB.substring(0, 4); // year
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// version 3 putting middle and first names in the same field
|
||||
name = parsedData.DCT.split(",");
|
||||
parsedData.DAC = name[0]; // first name
|
||||
parsedData.DAD = name[1]; // middle name
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.log("no version matched");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// drivers license class
|
||||
parsedData.DCA = parsedData.DAR;
|
||||
|
||||
var rawData = {
|
||||
state: parsedData.DAJ,
|
||||
city: parsedData.DAI,
|
||||
name: (function () {
|
||||
return {
|
||||
last: parsedData.DCS,
|
||||
first: parsedData.DAC,
|
||||
middle: parsedData.DAD,
|
||||
};
|
||||
})(),
|
||||
address: parsedData.DAG,
|
||||
iso_iin: undefined,
|
||||
dl: parsedData.DAQ,
|
||||
expiration_date: parseDate(parsedData.DBA),
|
||||
birthday: (function () {
|
||||
if (!parsedData.DBB) return;
|
||||
var match = parsedData.DBB.match(/(\d{2})(\d{2})(\d{4})/);
|
||||
if (!match) return;
|
||||
return match[3] + match[1] + match[2];
|
||||
})(),
|
||||
dl_overflow: undefined,
|
||||
cds_version: undefined,
|
||||
jurisdiction_version: undefined,
|
||||
postal_code: parsedData.DAK
|
||||
? parsedData.DAK.match(/\d{-}\d+/)
|
||||
? parsedData.DAK
|
||||
: parsedData.DAK.substring(0, 5)
|
||||
: undefined,
|
||||
klass: parsedData.DCA,
|
||||
class: parsedData.DCA,
|
||||
restrictions: undefined,
|
||||
endorsments: undefined,
|
||||
sex: (function () {
|
||||
switch (Number(parsedData.DBC)) {
|
||||
case 1:
|
||||
return "MALE";
|
||||
// date on 01 is CCYYMMDD while on 07 MMDDCCYY
|
||||
parsedData.DBB =
|
||||
parsedData.DBB.substring(4, 6) + // month
|
||||
parsedData.DBB.substring(6, 8) + // day
|
||||
parsedData.DBB.substring(0, 4); // year
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// version 3 putting middle and first names in the same field
|
||||
name = parsedData.DCT.split(",");
|
||||
parsedData.DAC = name[0]; // first name
|
||||
parsedData.DAD = name[1]; // middle name
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.log("no version matched");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case 2:
|
||||
return "FEMALE";
|
||||
|
||||
default:
|
||||
if (parsedData.DBC[0] === "M") {
|
||||
return "MALE";
|
||||
} else if (parsedData.DBC[0] === "F") {
|
||||
return "FEMALE";
|
||||
}
|
||||
return "MISSING/INVALID";
|
||||
}
|
||||
})(),
|
||||
height: undefined,
|
||||
weight: undefined,
|
||||
hair_color: undefined,
|
||||
eye_color: undefined,
|
||||
misc: undefined,
|
||||
id: (function () {
|
||||
if (!parsedData.DAQ) return;
|
||||
return parsedData.DAQ.replace(/[^A-ZA-Z0-9]/g, "");
|
||||
})(),
|
||||
var rawData = {
|
||||
state: parsedData.DAJ,
|
||||
city: parsedData.DAI,
|
||||
name: (function () {
|
||||
return {
|
||||
last: parsedData.DCS,
|
||||
first: parsedData.DAC,
|
||||
middle: parsedData.DAD
|
||||
};
|
||||
})(),
|
||||
address: parsedData.DAG,
|
||||
iso_iin: undefined,
|
||||
dl: parsedData.DAQ,
|
||||
expiration_date: parseDate(parsedData.DBA),
|
||||
birthday: (function () {
|
||||
if (!parsedData.DBB) return;
|
||||
var match = parsedData.DBB.match(/(\d{2})(\d{2})(\d{4})/);
|
||||
if (!match) return;
|
||||
return match[3] + match[1] + match[2];
|
||||
})(),
|
||||
dl_overflow: undefined,
|
||||
cds_version: undefined,
|
||||
jurisdiction_version: undefined,
|
||||
postal_code: parsedData.DAK
|
||||
? parsedData.DAK.match(/\d{-}\d+/)
|
||||
? parsedData.DAK
|
||||
: parsedData.DAK.substring(0, 5)
|
||||
: undefined,
|
||||
klass: parsedData.DCA,
|
||||
class: parsedData.DCA,
|
||||
restrictions: undefined,
|
||||
endorsments: undefined,
|
||||
sex: (function () {
|
||||
switch (Number(parsedData.DBC)) {
|
||||
case 1:
|
||||
return "MALE";
|
||||
|
||||
return rawData;
|
||||
case 2:
|
||||
return "FEMALE";
|
||||
|
||||
default:
|
||||
if (parsedData.DBC[0] === "M") {
|
||||
return "MALE";
|
||||
} else if (parsedData.DBC[0] === "F") {
|
||||
return "FEMALE";
|
||||
}
|
||||
return "MISSING/INVALID";
|
||||
}
|
||||
})(),
|
||||
height: undefined,
|
||||
weight: undefined,
|
||||
hair_color: undefined,
|
||||
eye_color: undefined,
|
||||
misc: undefined,
|
||||
id: (function () {
|
||||
if (!parsedData.DAQ) return;
|
||||
return parsedData.DAQ.replace(/[^A-ZA-Z0-9]/g, "");
|
||||
})()
|
||||
};
|
||||
|
||||
global.parse = parse;
|
||||
global.stripe = stripe;
|
||||
global.pdf417 = pdf417;
|
||||
return rawData;
|
||||
};
|
||||
|
||||
global.parse = parse;
|
||||
global.stripe = stripe;
|
||||
global.pdf417 = pdf417;
|
||||
})(this);
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export function onlyUnique(value, index, self, key) {
|
||||
return self.indexOf(value) === index;
|
||||
return self.indexOf(value) === index;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
function confirmDialog(msg) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let confirmed = window.confirm(msg);
|
||||
return new Promise(function (resolve, reject) {
|
||||
let confirmed = window.confirm(msg);
|
||||
|
||||
return confirmed ? resolve(true) : resolve(false);
|
||||
});
|
||||
return confirmed ? resolve(true) : resolve(false);
|
||||
});
|
||||
}
|
||||
|
||||
export default confirmDialog;
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
export const BETA_KEY = 'betaSwitchImex';
|
||||
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;
|
||||
}
|
||||
|
||||
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}`;
|
||||
}
|
||||
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;
|
||||
}
|
||||
// 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 isBeta = checkBeta();
|
||||
|
||||
const currentHostName = window.location.hostname;
|
||||
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 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;
|
||||
// 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,9 +1,9 @@
|
||||
import i18n from "i18next";
|
||||
|
||||
export function CreateRecentItem(id, type, labelid, url) {
|
||||
return {
|
||||
id,
|
||||
label: `${i18n.t(`general.itemtypes.${type}`)}: ${labelid}`,
|
||||
url,
|
||||
};
|
||||
return {
|
||||
id,
|
||||
label: `${i18n.t(`general.itemtypes.${type}`)}: ${labelid}`,
|
||||
url
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import axios from "axios";
|
||||
import {notification} from "antd";
|
||||
import { notification } from "antd";
|
||||
|
||||
async function CriticalPartsScan(jobid) {
|
||||
try {
|
||||
await axios.post("/job/partsscan", {jobid});
|
||||
} catch (error) {
|
||||
notification.open({type: "error", message: JSON.stringify(error)});
|
||||
}
|
||||
try {
|
||||
await axios.post("/job/partsscan", { jobid });
|
||||
} catch (error) {
|
||||
notification.open({ type: "error", message: JSON.stringify(error) });
|
||||
}
|
||||
}
|
||||
|
||||
export default CriticalPartsScan;
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
import dayjs from 'dayjs';
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import dayjsBusinessDays from "dayjs-business-days2";
|
||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
|
||||
import updateLocale from 'dayjs/plugin/updateLocale';
|
||||
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 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";
|
||||
|
||||
dayjs.extend(toObject);
|
||||
dayjs.extend(toArray);
|
||||
@@ -64,4 +64,4 @@ dayjs.extend(minMax);
|
||||
dayjs.extend(isBetween);
|
||||
dayjs.extend(dayjsBusinessDays);
|
||||
|
||||
export default dayjs;
|
||||
export default dayjs;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const filename = process.argv[2];
|
||||
|
||||
fs.readFile(filename, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error(`Error reading file ${filename}:`, err);
|
||||
return;
|
||||
}
|
||||
const filteredData = JSON.stringify(data);
|
||||
console.log('Select the content between the quotes below and paste it into the EULA Content field in the EULA Content table in the database.')
|
||||
console.log('--------------------------------------------------')
|
||||
console.log(filteredData);
|
||||
console.log('--------------------------------------------------')
|
||||
});
|
||||
fs.readFile(filename, "utf8", (err, data) => {
|
||||
if (err) {
|
||||
console.error(`Error reading file ${filename}:`, err);
|
||||
return;
|
||||
}
|
||||
const filteredData = JSON.stringify(data);
|
||||
console.log(
|
||||
"Select the content between the quotes below and paste it into the EULA Content field in the EULA Content table in the database."
|
||||
);
|
||||
console.log("--------------------------------------------------");
|
||||
console.log(filteredData);
|
||||
console.log("--------------------------------------------------");
|
||||
});
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
export default async function FcmHandler({client, payload}) {
|
||||
switch (payload.type) {
|
||||
case "messaging-inbound":
|
||||
client.cache.modify({
|
||||
id: client.cache.identify({
|
||||
__typename: "conversations",
|
||||
id: payload.conversationid,
|
||||
}),
|
||||
fields: {
|
||||
messages_aggregate(cached) {
|
||||
return {aggregate: {count: cached.aggregate.count + 1}};
|
||||
},
|
||||
},
|
||||
});
|
||||
client.cache.modify({
|
||||
fields: {
|
||||
messages_aggregate(cached) {
|
||||
return {aggregate: {count: cached.aggregate.count + 1}};
|
||||
},
|
||||
},
|
||||
});
|
||||
break;
|
||||
case "messaging-outbound":
|
||||
client.cache.modify({
|
||||
id: client.cache.identify({
|
||||
__typename: "conversations",
|
||||
id: payload.conversationid,
|
||||
}),
|
||||
fields: {
|
||||
updated_at(oldupdated0) {
|
||||
return new Date();
|
||||
},
|
||||
// messages_aggregate(cached) {
|
||||
// return { aggregate: { count: cached.aggregate.count + 1 } };
|
||||
// },
|
||||
},
|
||||
});
|
||||
break;
|
||||
case "messaging-mark-conversation-read":
|
||||
let previousUnreadCount = 0;
|
||||
client.cache.modify({
|
||||
id: client.cache.identify({
|
||||
__typename: "conversations",
|
||||
id: payload.conversationid,
|
||||
}),
|
||||
fields: {
|
||||
messages_aggregate(cached) {
|
||||
previousUnreadCount = cached.aggregate.count;
|
||||
return {aggregate: {count: 0}};
|
||||
},
|
||||
},
|
||||
});
|
||||
client.cache.modify({
|
||||
fields: {
|
||||
messages_aggregate(cached) {
|
||||
return {
|
||||
aggregate: {
|
||||
count: cached.aggregate.count - previousUnreadCount,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log("No payload type set.");
|
||||
break;
|
||||
}
|
||||
export default async function FcmHandler({ client, payload }) {
|
||||
switch (payload.type) {
|
||||
case "messaging-inbound":
|
||||
client.cache.modify({
|
||||
id: client.cache.identify({
|
||||
__typename: "conversations",
|
||||
id: payload.conversationid
|
||||
}),
|
||||
fields: {
|
||||
messages_aggregate(cached) {
|
||||
return { aggregate: { count: cached.aggregate.count + 1 } };
|
||||
}
|
||||
}
|
||||
});
|
||||
client.cache.modify({
|
||||
fields: {
|
||||
messages_aggregate(cached) {
|
||||
return { aggregate: { count: cached.aggregate.count + 1 } };
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "messaging-outbound":
|
||||
client.cache.modify({
|
||||
id: client.cache.identify({
|
||||
__typename: "conversations",
|
||||
id: payload.conversationid
|
||||
}),
|
||||
fields: {
|
||||
updated_at(oldupdated0) {
|
||||
return new Date();
|
||||
}
|
||||
// messages_aggregate(cached) {
|
||||
// return { aggregate: { count: cached.aggregate.count + 1 } };
|
||||
// },
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "messaging-mark-conversation-read":
|
||||
let previousUnreadCount = 0;
|
||||
client.cache.modify({
|
||||
id: client.cache.identify({
|
||||
__typename: "conversations",
|
||||
id: payload.conversationid
|
||||
}),
|
||||
fields: {
|
||||
messages_aggregate(cached) {
|
||||
previousUnreadCount = cached.aggregate.count;
|
||||
return { aggregate: { count: 0 } };
|
||||
}
|
||||
}
|
||||
});
|
||||
client.cache.modify({
|
||||
fields: {
|
||||
messages_aggregate(cached) {
|
||||
return {
|
||||
aggregate: {
|
||||
count: cached.aggregate.count - previousUnreadCount
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log("No payload type set.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
export default function formatBytes(a, b = 2) {
|
||||
if (0 === a || !a) return "0 Bytes";
|
||||
const c = 0 > b ? 0 : b,
|
||||
d = Math.floor(Math.log(a) / Math.log(1024));
|
||||
return (
|
||||
parseFloat((a / Math.pow(1024, d)).toFixed(c)) +
|
||||
" " +
|
||||
["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][d]
|
||||
);
|
||||
if (0 === a || !a) return "0 Bytes";
|
||||
const c = 0 > b ? 0 : b,
|
||||
d = Math.floor(Math.log(a) / Math.log(1024));
|
||||
return (
|
||||
parseFloat((a / Math.pow(1024, d)).toFixed(c)) + " " + ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][d]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Kind, parse, print, visit} from "graphql";
|
||||
import { Kind, parse, print, visit } from "graphql";
|
||||
import client from "./GraphQLClient";
|
||||
import {gql} from "@apollo/client";
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
/* eslint-disable no-loop-func */
|
||||
|
||||
@@ -9,14 +9,14 @@ import {gql} from "@apollo/client";
|
||||
* @type {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null,null,null]}
|
||||
*/
|
||||
const STRING_OPERATORS = [
|
||||
{value: "_eq", label: "equals"},
|
||||
{value: "_neq", label: "does not equal"},
|
||||
{value: "_like", label: "contains"},
|
||||
{value: "_nlike", label: "does not contain"},
|
||||
{value: "_ilike", label: "contains case-insensitive"},
|
||||
{value: "_nilike", label: "does not contain case-insensitive"},
|
||||
{value: "_in", label: "in", type: "array"},
|
||||
{value: "_nin", label: "not in", type: "array"}
|
||||
{ value: "_eq", label: "equals" },
|
||||
{ value: "_neq", label: "does not equal" },
|
||||
{ value: "_like", label: "contains" },
|
||||
{ value: "_nlike", label: "does not contain" },
|
||||
{ value: "_ilike", label: "contains case-insensitive" },
|
||||
{ value: "_nilike", label: "does not contain case-insensitive" },
|
||||
{ value: "_in", label: "in", type: "array" },
|
||||
{ value: "_nin", label: "not in", type: "array" }
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -24,14 +24,14 @@ const STRING_OPERATORS = [
|
||||
* @type {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null,null,null]}
|
||||
*/
|
||||
const DATE_OPERATORS = [
|
||||
{value: "_eq", label: "equals"},
|
||||
{value: "_neq", label: "does not equal"},
|
||||
{value: "_gt", label: "greater than"},
|
||||
{value: "_lt", label: "less than"},
|
||||
{value: "_gte", label: "greater than or equal"},
|
||||
{value: "_lte", label: "less than or equal"},
|
||||
{value: "_in", label: "in", type: "array"},
|
||||
{value: "_nin", label: "not in", type: "array"}
|
||||
{ value: "_eq", label: "equals" },
|
||||
{ value: "_neq", label: "does not equal" },
|
||||
{ value: "_gt", label: "greater than" },
|
||||
{ value: "_lt", label: "less than" },
|
||||
{ value: "_gte", label: "greater than or equal" },
|
||||
{ value: "_lte", label: "less than or equal" },
|
||||
{ value: "_in", label: "in", type: "array" },
|
||||
{ value: "_nin", label: "not in", type: "array" }
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -39,8 +39,8 @@ const DATE_OPERATORS = [
|
||||
* @type {[{label: string, value: string},{label: string, value: string}]}
|
||||
*/
|
||||
const BOOLEAN_OPERATORS = [
|
||||
{value: "_eq", label: "equals"},
|
||||
{value: "_neq", label: "does not equal"},
|
||||
{ value: "_eq", label: "equals" },
|
||||
{ value: "_neq", label: "does not equal" }
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -48,14 +48,14 @@ const BOOLEAN_OPERATORS = [
|
||||
* @type {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null,null,null]}
|
||||
*/
|
||||
const NUMBER_OPERATORS = [
|
||||
{value: "_eq", label: "equals"},
|
||||
{value: "_neq", label: "does not equal"},
|
||||
{value: "_gt", label: "greater than"},
|
||||
{value: "_lt", label: "less than"},
|
||||
{value: "_gte", label: "greater than or equal"},
|
||||
{value: "_lte", label: "less than or equal"},
|
||||
{value: "_in", label: "in", type: "array"},
|
||||
{value: "_nin", label: "not in", type: "array"}
|
||||
{ value: "_eq", label: "equals" },
|
||||
{ value: "_neq", label: "does not equal" },
|
||||
{ value: "_gt", label: "greater than" },
|
||||
{ value: "_lt", label: "less than" },
|
||||
{ value: "_gte", label: "greater than or equal" },
|
||||
{ value: "_lte", label: "less than or equal" },
|
||||
{ value: "_in", label: "in", type: "array" },
|
||||
{ value: "_nin", label: "not in", type: "array" }
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -63,8 +63,8 @@ const NUMBER_OPERATORS = [
|
||||
* @type {[{label: string, value: string},{label: string, value: string}]}
|
||||
*/
|
||||
const ORDER_BY_OPERATORS = [
|
||||
{value: "asc", label: "ascending"},
|
||||
{value: "desc", label: "descending"}
|
||||
{ value: "asc", label: "ascending" },
|
||||
{ value: "desc", label: "descending" }
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -72,7 +72,7 @@ const ORDER_BY_OPERATORS = [
|
||||
* @returns {[{label: string, value: string},{label: string, value: string}]}
|
||||
*/
|
||||
export function getOrderOperatorsByType() {
|
||||
return ORDER_BY_OPERATORS;
|
||||
return ORDER_BY_OPERATORS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,15 +80,15 @@ export function getOrderOperatorsByType() {
|
||||
* @param type
|
||||
* @returns {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null]}
|
||||
*/
|
||||
export function getWhereOperatorsByType(type = 'string') {
|
||||
const operators = {
|
||||
string: STRING_OPERATORS,
|
||||
number: NUMBER_OPERATORS,
|
||||
boolean: BOOLEAN_OPERATORS,
|
||||
bool: BOOLEAN_OPERATORS,
|
||||
date: DATE_OPERATORS
|
||||
};
|
||||
return operators[type];
|
||||
export function getWhereOperatorsByType(type = "string") {
|
||||
const operators = {
|
||||
string: STRING_OPERATORS,
|
||||
number: NUMBER_OPERATORS,
|
||||
boolean: BOOLEAN_OPERATORS,
|
||||
bool: BOOLEAN_OPERATORS,
|
||||
date: DATE_OPERATORS
|
||||
};
|
||||
return operators[type];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +97,7 @@ export function getWhereOperatorsByType(type = 'string') {
|
||||
* @returns {DocumentNode}
|
||||
*/
|
||||
export function parseQuery(query) {
|
||||
return parse(query);
|
||||
return parse(query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +106,7 @@ export function parseQuery(query) {
|
||||
* @returns {string}
|
||||
*/
|
||||
export function printQuery(query) {
|
||||
return print(query);
|
||||
return print(query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,38 +117,37 @@ export function printQuery(query) {
|
||||
* @returns {Promise<{contextData: {}, useShopSpecificTemplate}>}
|
||||
*/
|
||||
export async function generateTemplate(templateQueryToExecute, templateObject, useShopSpecificTemplate) {
|
||||
// Advanced Filtering and Sorting modifications start here
|
||||
// Advanced Filtering and Sorting modifications start here
|
||||
|
||||
// Parse the query and apply the filters and sorters
|
||||
const ast = parseQuery(templateQueryToExecute);
|
||||
// Parse the query and apply the filters and sorters
|
||||
const ast = parseQuery(templateQueryToExecute);
|
||||
|
||||
if (templateObject?.filters && templateObject?.filters?.length) {
|
||||
applyFilters(ast, templateObject.filters);
|
||||
}
|
||||
|
||||
if (templateObject?.filters && templateObject?.filters?.length) {
|
||||
applyFilters(ast, templateObject.filters);
|
||||
}
|
||||
if (templateObject?.sorters && templateObject?.sorters?.length) {
|
||||
applySorters(ast, templateObject.sorters);
|
||||
} else if (templateObject?.defaultSorters && templateObject?.defaultSorters?.length) {
|
||||
applySorters(ast, templateObject.defaultSorters);
|
||||
}
|
||||
|
||||
if (templateObject?.sorters && templateObject?.sorters?.length) {
|
||||
applySorters(ast, templateObject.sorters);
|
||||
} else if (templateObject?.defaultSorters && templateObject?.defaultSorters?.length) {
|
||||
applySorters(ast, templateObject.defaultSorters);
|
||||
}
|
||||
const finalQuery = printQuery(ast);
|
||||
|
||||
const finalQuery = printQuery(ast);
|
||||
// commented out for future revision debugging
|
||||
// console.log('Modified Query');
|
||||
// console.log(finalQuery);
|
||||
|
||||
// commented out for future revision debugging
|
||||
// console.log('Modified Query');
|
||||
// console.log(finalQuery);
|
||||
let contextData = {};
|
||||
if (templateQueryToExecute) {
|
||||
const { data } = await client.query({
|
||||
query: gql(finalQuery),
|
||||
variables: { ...templateObject.variables }
|
||||
});
|
||||
contextData = data;
|
||||
}
|
||||
|
||||
let contextData = {};
|
||||
if (templateQueryToExecute) {
|
||||
const {data} = await client.query({
|
||||
query: gql(finalQuery),
|
||||
variables: {...templateObject.variables},
|
||||
});
|
||||
contextData = data;
|
||||
}
|
||||
|
||||
return {contextData, useShopSpecificTemplate};
|
||||
return { contextData, useShopSpecificTemplate };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,58 +156,58 @@ export async function generateTemplate(templateQueryToExecute, templateObject, u
|
||||
* @param sorters
|
||||
*/
|
||||
export function applySorters(ast, sorters) {
|
||||
sorters.forEach((sorter) => {
|
||||
const fieldPath = sorter.field.split('.');
|
||||
visit(ast, {
|
||||
OperationDefinition: {
|
||||
sorters.forEach((sorter) => {
|
||||
const fieldPath = sorter.field.split(".");
|
||||
visit(ast, {
|
||||
OperationDefinition: {
|
||||
enter(node) {
|
||||
// Loop through each sorter to apply it
|
||||
// noinspection DuplicatedCode
|
||||
|
||||
let currentSelection = node; // Start with the root operation
|
||||
|
||||
// Navigate down the field path to the correct location
|
||||
for (let i = 0; i < fieldPath.length - 1; i++) {
|
||||
let found = false;
|
||||
visit(currentSelection, {
|
||||
Field: {
|
||||
enter(node) {
|
||||
// Loop through each sorter to apply it
|
||||
// noinspection DuplicatedCode
|
||||
|
||||
let currentSelection = node; // Start with the root operation
|
||||
|
||||
// Navigate down the field path to the correct location
|
||||
for (let i = 0; i < fieldPath.length - 1; i++) {
|
||||
let found = false;
|
||||
visit(currentSelection, {
|
||||
Field: {
|
||||
enter(node) {
|
||||
if (node.name.value === fieldPath[i]) {
|
||||
currentSelection = node; // Move down to the next level
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!found) break; // Stop if we can't find the next field in the path
|
||||
}
|
||||
|
||||
// Apply the sorter at the correct level
|
||||
if (currentSelection) {
|
||||
const targetFieldName = fieldPath[fieldPath.length - 1];
|
||||
let orderByArg = currentSelection.arguments.find(arg => arg.name.value === 'order_by');
|
||||
if (!orderByArg) {
|
||||
orderByArg = {
|
||||
kind: Kind.ARGUMENT,
|
||||
name: {kind: Kind.NAME, value: 'order_by'},
|
||||
value: {kind: Kind.OBJECT, fields: []},
|
||||
};
|
||||
currentSelection.arguments.push(orderByArg);
|
||||
}
|
||||
|
||||
const sorterField = {
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: {kind: Kind.NAME, value: targetFieldName},
|
||||
value: {kind: Kind.ENUM, value: sorter.direction}, // Adjust if your schema uses a different type for sorting directions
|
||||
};
|
||||
|
||||
// Add the new sorter condition
|
||||
orderByArg.value.fields.push(sorterField);
|
||||
}
|
||||
if (node.name.value === fieldPath[i]) {
|
||||
currentSelection = node; // Move down to the next level
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!found) break; // Stop if we can't find the next field in the path
|
||||
}
|
||||
|
||||
// Apply the sorter at the correct level
|
||||
if (currentSelection) {
|
||||
const targetFieldName = fieldPath[fieldPath.length - 1];
|
||||
let orderByArg = currentSelection.arguments.find((arg) => arg.name.value === "order_by");
|
||||
if (!orderByArg) {
|
||||
orderByArg = {
|
||||
kind: Kind.ARGUMENT,
|
||||
name: { kind: Kind.NAME, value: "order_by" },
|
||||
value: { kind: Kind.OBJECT, fields: [] }
|
||||
};
|
||||
currentSelection.arguments.push(orderByArg);
|
||||
}
|
||||
});
|
||||
|
||||
const sorterField = {
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: { kind: Kind.NAME, value: targetFieldName },
|
||||
value: { kind: Kind.ENUM, value: sorter.direction } // Adjust if your schema uses a different type for sorting directions
|
||||
};
|
||||
|
||||
// Add the new sorter condition
|
||||
orderByArg.value.fields.push(sorterField);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,45 +217,47 @@ export function applySorters(ast, sorters) {
|
||||
* @param filterField
|
||||
*/
|
||||
function applyTopLevelSub(node, fieldPath, filterField) {
|
||||
// Find or create the where argument for the top-level subfield
|
||||
let whereArg = node.selectionSet.selections
|
||||
.find(selection => selection.name.value === fieldPath[0])
|
||||
?.arguments.find(arg => arg.name.value === 'where');
|
||||
// Find or create the where argument for the top-level subfield
|
||||
let whereArg = node.selectionSet.selections
|
||||
.find((selection) => selection.name.value === fieldPath[0])
|
||||
?.arguments.find((arg) => arg.name.value === "where");
|
||||
|
||||
if (!whereArg) {
|
||||
whereArg = {
|
||||
kind: Kind.ARGUMENT,
|
||||
name: {kind: Kind.NAME, value: 'where'},
|
||||
value: {kind: Kind.OBJECT, fields: []},
|
||||
if (!whereArg) {
|
||||
whereArg = {
|
||||
kind: Kind.ARGUMENT,
|
||||
name: { kind: Kind.NAME, value: "where" },
|
||||
value: { kind: Kind.OBJECT, fields: [] }
|
||||
};
|
||||
const topLevelSubSelection = node.selectionSet.selections.find(
|
||||
(selection) => selection.name.value === fieldPath[0]
|
||||
);
|
||||
if (topLevelSubSelection) {
|
||||
topLevelSubSelection.arguments = topLevelSubSelection.arguments || [];
|
||||
topLevelSubSelection.arguments.push(whereArg);
|
||||
}
|
||||
}
|
||||
|
||||
// Correctly position the nested filter without an extra 'where'
|
||||
if (fieldPath.length > 2) {
|
||||
// More than one level deep
|
||||
let currentField = whereArg.value;
|
||||
fieldPath.slice(1, -1).forEach((path, index) => {
|
||||
let existingField = currentField.fields.find((f) => f.name.value === path);
|
||||
if (!existingField) {
|
||||
existingField = {
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: { kind: Kind.NAME, value: path },
|
||||
value: { kind: Kind.OBJECT, fields: [] }
|
||||
};
|
||||
const topLevelSubSelection = node.selectionSet.selections.find(selection =>
|
||||
selection.name.value === fieldPath[0]
|
||||
);
|
||||
if (topLevelSubSelection) {
|
||||
topLevelSubSelection.arguments = topLevelSubSelection.arguments || [];
|
||||
topLevelSubSelection.arguments.push(whereArg);
|
||||
}
|
||||
}
|
||||
|
||||
// Correctly position the nested filter without an extra 'where'
|
||||
if (fieldPath.length > 2) { // More than one level deep
|
||||
let currentField = whereArg.value;
|
||||
fieldPath.slice(1, -1).forEach((path, index) => {
|
||||
let existingField = currentField.fields.find(f => f.name.value === path);
|
||||
if (!existingField) {
|
||||
existingField = {
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: {kind: Kind.NAME, value: path},
|
||||
value: {kind: Kind.OBJECT, fields: []}
|
||||
};
|
||||
currentField.fields.push(existingField);
|
||||
}
|
||||
currentField = existingField.value;
|
||||
});
|
||||
currentField.fields.push(filterField);
|
||||
} else { // Directly under the top level
|
||||
whereArg.value.fields.push(filterField);
|
||||
}
|
||||
currentField.fields.push(existingField);
|
||||
}
|
||||
currentField = existingField.value;
|
||||
});
|
||||
currentField.fields.push(filterField);
|
||||
} else {
|
||||
// Directly under the top level
|
||||
whereArg.value.fields.push(filterField);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,41 +267,41 @@ function applyTopLevelSub(node, fieldPath, filterField) {
|
||||
* @returns {ASTNode}
|
||||
*/
|
||||
export function applyFilters(ast, filters) {
|
||||
return visit(ast, {
|
||||
OperationDefinition: {
|
||||
enter(node) {
|
||||
filters.forEach(filter => {
|
||||
const fieldPath = filter.field.split('.');
|
||||
let topLevel = false;
|
||||
let topLevelSub = false;
|
||||
return visit(ast, {
|
||||
OperationDefinition: {
|
||||
enter(node) {
|
||||
filters.forEach((filter) => {
|
||||
const fieldPath = filter.field.split(".");
|
||||
let topLevel = false;
|
||||
let topLevelSub = false;
|
||||
|
||||
// Determine if the filter should be applied at the top level
|
||||
if (fieldPath.length === 2) {
|
||||
topLevel = true;
|
||||
}
|
||||
// Determine if the filter should be applied at the top level
|
||||
if (fieldPath.length === 2) {
|
||||
topLevel = true;
|
||||
}
|
||||
|
||||
if (fieldPath.length > 2 && fieldPath[0].startsWith('[') && fieldPath[0].endsWith(']')) {
|
||||
fieldPath[0] = fieldPath[0].substring(1, fieldPath[0].length - 1); // Strip the brackets
|
||||
topLevelSub = true;
|
||||
}
|
||||
if (fieldPath.length > 2 && fieldPath[0].startsWith("[") && fieldPath[0].endsWith("]")) {
|
||||
fieldPath[0] = fieldPath[0].substring(1, fieldPath[0].length - 1); // Strip the brackets
|
||||
topLevelSub = true;
|
||||
}
|
||||
|
||||
// Construct the filter for a top-level application
|
||||
const targetFieldName = fieldPath[fieldPath.length - 1];
|
||||
// Construct the filter for a top-level application
|
||||
const targetFieldName = fieldPath[fieldPath.length - 1];
|
||||
|
||||
let filterValue = createFilterValue(filter);
|
||||
let filterField = createFilterField(targetFieldName, filter, filterValue);
|
||||
let filterValue = createFilterValue(filter);
|
||||
let filterField = createFilterField(targetFieldName, filter, filterValue);
|
||||
|
||||
if (topLevel) {
|
||||
applyTopLevelFilter(node, fieldPath, filterField);
|
||||
} else if (topLevelSub) {
|
||||
applyTopLevelSub(node, fieldPath, filterField);
|
||||
} else {
|
||||
applyNestedFilter(node, fieldPath, filterField);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (topLevel) {
|
||||
applyTopLevelFilter(node, fieldPath, filterField);
|
||||
} else if (topLevelSub) {
|
||||
applyTopLevelSub(node, fieldPath, filterField);
|
||||
} else {
|
||||
applyNestedFilter(node, fieldPath, filterField);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,22 +310,22 @@ export function applyFilters(ast, filters) {
|
||||
* @returns {{kind: (Kind|Kind.INT), value}|{kind: Kind.LIST, values: *}}
|
||||
*/
|
||||
function createFilterValue(filter) {
|
||||
if (Array.isArray(filter.value)) {
|
||||
// If it's an array, create a list value with the array items
|
||||
return {
|
||||
kind: Kind.LIST,
|
||||
values: filter.value.map(item => ({
|
||||
kind: getGraphQLKind(item),
|
||||
value: item,
|
||||
})),
|
||||
};
|
||||
} else {
|
||||
// If it's not an array, use the existing logic
|
||||
return {
|
||||
kind: getGraphQLKind(filter.value),
|
||||
value: filter.value,
|
||||
};
|
||||
}
|
||||
if (Array.isArray(filter.value)) {
|
||||
// If it's an array, create a list value with the array items
|
||||
return {
|
||||
kind: Kind.LIST,
|
||||
values: filter.value.map((item) => ({
|
||||
kind: getGraphQLKind(item),
|
||||
value: item
|
||||
}))
|
||||
};
|
||||
} else {
|
||||
// If it's not an array, use the existing logic
|
||||
return {
|
||||
kind: getGraphQLKind(filter.value),
|
||||
value: filter.value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,18 +336,20 @@ function createFilterValue(filter) {
|
||||
* @returns {{kind: Kind.OBJECT_FIELD, name: {kind: Kind.NAME, value}, value: {kind: Kind.OBJECT, fields: [{kind: Kind.OBJECT_FIELD, name: {kind: Kind.NAME, value}, value}]}}}
|
||||
*/
|
||||
function createFilterField(targetFieldName, filter, filterValue) {
|
||||
return {
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: {kind: Kind.NAME, value: targetFieldName},
|
||||
value: {
|
||||
kind: Kind.OBJECT,
|
||||
fields: [{
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: {kind: Kind.NAME, value: filter.operator},
|
||||
value: filterValue,
|
||||
}],
|
||||
},
|
||||
};
|
||||
return {
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: { kind: Kind.NAME, value: targetFieldName },
|
||||
value: {
|
||||
kind: Kind.OBJECT,
|
||||
fields: [
|
||||
{
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: { kind: Kind.NAME, value: filter.operator },
|
||||
value: filterValue
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,45 +359,45 @@ function createFilterField(targetFieldName, filter, filterValue) {
|
||||
* @param filterField
|
||||
*/
|
||||
function applyTopLevelFilter(node, fieldPath, filterField) {
|
||||
// Find or create the where argument for the top-level field
|
||||
let whereArg = node.selectionSet.selections
|
||||
.find(selection => selection.name.value === fieldPath[0])
|
||||
?.arguments.find(arg => arg.name.value === 'where');
|
||||
// Find or create the where argument for the top-level field
|
||||
let whereArg = node.selectionSet.selections
|
||||
.find((selection) => selection.name.value === fieldPath[0])
|
||||
?.arguments.find((arg) => arg.name.value === "where");
|
||||
|
||||
if (!whereArg) {
|
||||
whereArg = {
|
||||
kind: Kind.ARGUMENT,
|
||||
name: {kind: Kind.NAME, value: 'where'},
|
||||
value: {kind: Kind.OBJECT, fields: []},
|
||||
if (!whereArg) {
|
||||
whereArg = {
|
||||
kind: Kind.ARGUMENT,
|
||||
name: { kind: Kind.NAME, value: "where" },
|
||||
value: { kind: Kind.OBJECT, fields: [] }
|
||||
};
|
||||
const topLevelSelection = node.selectionSet.selections.find((selection) => selection.name.value === fieldPath[0]);
|
||||
if (topLevelSelection) {
|
||||
topLevelSelection.arguments = topLevelSelection.arguments || [];
|
||||
topLevelSelection.arguments.push(whereArg);
|
||||
}
|
||||
}
|
||||
|
||||
// Correctly position the nested filter without an extra 'where'
|
||||
if (fieldPath.length > 2) {
|
||||
// More than one level deep
|
||||
let currentField = whereArg.value;
|
||||
fieldPath.slice(1, -1).forEach((path, index) => {
|
||||
let existingField = currentField.fields.find((f) => f.name.value === path);
|
||||
if (!existingField) {
|
||||
existingField = {
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: { kind: Kind.NAME, value: path },
|
||||
value: { kind: Kind.OBJECT, fields: [] }
|
||||
};
|
||||
const topLevelSelection = node.selectionSet.selections.find(selection =>
|
||||
selection.name.value === fieldPath[0]
|
||||
);
|
||||
if (topLevelSelection) {
|
||||
topLevelSelection.arguments = topLevelSelection.arguments || [];
|
||||
topLevelSelection.arguments.push(whereArg);
|
||||
}
|
||||
}
|
||||
|
||||
// Correctly position the nested filter without an extra 'where'
|
||||
if (fieldPath.length > 2) { // More than one level deep
|
||||
let currentField = whereArg.value;
|
||||
fieldPath.slice(1, -1).forEach((path, index) => {
|
||||
let existingField = currentField.fields.find(f => f.name.value === path);
|
||||
if (!existingField) {
|
||||
existingField = {
|
||||
kind: Kind.OBJECT_FIELD,
|
||||
name: {kind: Kind.NAME, value: path},
|
||||
value: {kind: Kind.OBJECT, fields: []}
|
||||
};
|
||||
currentField.fields.push(existingField);
|
||||
}
|
||||
currentField = existingField.value;
|
||||
});
|
||||
currentField.fields.push(filterField);
|
||||
} else { // Directly under the top level
|
||||
whereArg.value.fields.push(filterField);
|
||||
}
|
||||
currentField.fields.push(existingField);
|
||||
}
|
||||
currentField = existingField.value;
|
||||
});
|
||||
currentField.fields.push(filterField);
|
||||
} else {
|
||||
// Directly under the top level
|
||||
whereArg.value.fields.push(filterField);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,46 +407,46 @@ function applyTopLevelFilter(node, fieldPath, filterField) {
|
||||
* @param filterField
|
||||
*/
|
||||
function applyNestedFilter(node, fieldPath, filterField) {
|
||||
// Initialize a reference to the current selection to traverse down the AST
|
||||
let currentSelection = node;
|
||||
// Initialize a reference to the current selection to traverse down the AST
|
||||
let currentSelection = node;
|
||||
|
||||
// Iterate over the fieldPath, except for the last entry, to navigate the structure
|
||||
for (let i = 0; i < fieldPath.length - 1; i++) {
|
||||
const fieldName = fieldPath[i];
|
||||
let fieldFound = false;
|
||||
// Iterate over the fieldPath, except for the last entry, to navigate the structure
|
||||
for (let i = 0; i < fieldPath.length - 1; i++) {
|
||||
const fieldName = fieldPath[i];
|
||||
let fieldFound = false;
|
||||
|
||||
// Check if the current selection has a selectionSet and selections
|
||||
if (currentSelection.selectionSet && currentSelection.selectionSet.selections) {
|
||||
// Look for the field in the current selection's selections
|
||||
const selection = currentSelection.selectionSet.selections.find(sel => sel.name.value === fieldName);
|
||||
if (selection) {
|
||||
// Move down the AST to the found selection
|
||||
currentSelection = selection;
|
||||
fieldFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the field was not found in the current path, it's an issue
|
||||
if (!fieldFound) {
|
||||
console.error(`Field ${fieldName} not found in the current selection.`);
|
||||
return; // Exit the loop and function due to error
|
||||
}
|
||||
// Check if the current selection has a selectionSet and selections
|
||||
if (currentSelection.selectionSet && currentSelection.selectionSet.selections) {
|
||||
// Look for the field in the current selection's selections
|
||||
const selection = currentSelection.selectionSet.selections.find((sel) => sel.name.value === fieldName);
|
||||
if (selection) {
|
||||
// Move down the AST to the found selection
|
||||
currentSelection = selection;
|
||||
fieldFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, currentSelection should be the parent field where the filter needs to be applied
|
||||
// Check if the 'where' argument already exists in the current selection
|
||||
const whereArg = currentSelection.arguments.find(arg => arg.name.value === 'where');
|
||||
if (!whereArg) {
|
||||
// If not found, create a new 'where' argument for the current selection
|
||||
currentSelection.arguments.push({
|
||||
kind: Kind.ARGUMENT,
|
||||
name: {kind: Kind.NAME, value: 'where'},
|
||||
value: {kind: Kind.OBJECT, fields: []} // Empty fields array to be populated with the filter
|
||||
});
|
||||
// If the field was not found in the current path, it's an issue
|
||||
if (!fieldFound) {
|
||||
console.error(`Field ${fieldName} not found in the current selection.`);
|
||||
return; // Exit the loop and function due to error
|
||||
}
|
||||
}
|
||||
|
||||
// Add the filter field to the 'where' clause of the current selection
|
||||
currentSelection.arguments.find(arg => arg.name.value === 'where').value.fields.push(filterField);
|
||||
// At this point, currentSelection should be the parent field where the filter needs to be applied
|
||||
// Check if the 'where' argument already exists in the current selection
|
||||
const whereArg = currentSelection.arguments.find((arg) => arg.name.value === "where");
|
||||
if (!whereArg) {
|
||||
// If not found, create a new 'where' argument for the current selection
|
||||
currentSelection.arguments.push({
|
||||
kind: Kind.ARGUMENT,
|
||||
name: { kind: Kind.NAME, value: "where" },
|
||||
value: { kind: Kind.OBJECT, fields: [] } // Empty fields array to be populated with the filter
|
||||
});
|
||||
}
|
||||
|
||||
// Add the filter field to the 'where' clause of the current selection
|
||||
currentSelection.arguments.find((arg) => arg.name.value === "where").value.fields.push(filterField);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,17 +455,17 @@ function applyNestedFilter(node, fieldPath, filterField) {
|
||||
* @returns {Kind|Kind.INT}
|
||||
*/
|
||||
function getGraphQLKind(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return Kind.LIST;
|
||||
} else if (typeof value === 'number') {
|
||||
return value % 1 === 0 ? Kind.INT : Kind.FLOAT;
|
||||
} else if (typeof value === 'boolean') {
|
||||
return Kind.BOOLEAN;
|
||||
} else if (typeof value === 'string') {
|
||||
return Kind.STRING;
|
||||
} else if (value instanceof Date) {
|
||||
return Kind.STRING; // GraphQL does not have a Date type, so we return it as a string
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return Kind.LIST;
|
||||
} else if (typeof value === "number") {
|
||||
return value % 1 === 0 ? Kind.INT : Kind.FLOAT;
|
||||
} else if (typeof value === "boolean") {
|
||||
return Kind.BOOLEAN;
|
||||
} else if (typeof value === "string") {
|
||||
return Kind.STRING;
|
||||
} else if (value instanceof Date) {
|
||||
return Kind.STRING; // GraphQL does not have a Date type, so we return it as a string
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-enable no-loop-func */
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
export const BETA_KEY = 'betaSwitchImex';
|
||||
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;
|
||||
}
|
||||
|
||||
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}`;
|
||||
}
|
||||
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;
|
||||
}
|
||||
// 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 isBeta = checkBeta();
|
||||
|
||||
const currentHostName = window.location.hostname;
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
|
||||
@@ -9,30 +9,25 @@
|
||||
* @property { string | object | function } imex Return this prop if Rome.
|
||||
*/
|
||||
|
||||
export default function InstanceRenderManager({
|
||||
executeFunction,
|
||||
rome,
|
||||
promanager,
|
||||
imex,
|
||||
debug,
|
||||
instance,
|
||||
args,
|
||||
}) {
|
||||
export default function InstanceRenderManager({ executeFunction, rome, promanager, imex, debug, instance, args }) {
|
||||
let propToReturn = null;
|
||||
|
||||
switch (instance || import.meta.env.VITE_APP_INSTANCE) {
|
||||
case 'IMEX':
|
||||
case "IMEX":
|
||||
propToReturn = imex;
|
||||
break;
|
||||
case 'ROME':
|
||||
propToReturn = rome; //TODO:AIO Implement USE_IMEX
|
||||
case "ROME":
|
||||
if (rome === "USE_IMEX") {
|
||||
propToReturn = imex;
|
||||
} else {
|
||||
propToReturn = rome;
|
||||
}
|
||||
break;
|
||||
case 'PROMANAGER':
|
||||
case "PROMANAGER":
|
||||
//Return the rome prop if USE_ROME.
|
||||
//If not USE_ROME, we want to default back to the rome prop if it's undefined.
|
||||
//If null, we might want to show nothing, so make sure we return null.
|
||||
propToReturn =
|
||||
promanager === 'USE_ROME' ? rome : promanager !== undefined ? promanager : rome;
|
||||
propToReturn = promanager === "USE_ROME" ? rome : promanager !== undefined ? promanager : rome;
|
||||
break;
|
||||
default:
|
||||
propToReturn = imex;
|
||||
@@ -40,16 +35,16 @@ export default function InstanceRenderManager({
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
console.log('InstanceRenderManager Debugger');
|
||||
console.log('=========================');
|
||||
console.log("InstanceRenderManager Debugger");
|
||||
console.log("=========================");
|
||||
console.log({ executeFunction, rome, promanager, imex, debug, propToReturn });
|
||||
console.log('=========================');
|
||||
console.log("=========================");
|
||||
}
|
||||
|
||||
//Checking to see if we need to default to another one.
|
||||
if (propToReturn === 'imex') {
|
||||
if (propToReturn === "imex") {
|
||||
propToReturn = imex;
|
||||
}
|
||||
if (executeFunction && typeof propToReturn === 'function') return propToReturn(...args);
|
||||
if (executeFunction && typeof propToReturn === "function") return propToReturn(...args);
|
||||
return propToReturn === undefined ? null : propToReturn;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function IsJobReadOnly(job) {
|
||||
if (!job) return false;
|
||||
return job.date_exported || job.date_invoiced || job.voided;
|
||||
if (!job) return false;
|
||||
return job.date_exported || job.date_invoiced || job.voided;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {store} from "../redux/store";
|
||||
import { store } from "../redux/store";
|
||||
|
||||
export function CreateExplorerLinkForJob({jobid}) {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
return `imexmedia://${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`;
|
||||
export function CreateExplorerLinkForJob({ jobid }) {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
return `imexmedia://${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`;
|
||||
}
|
||||
|
||||
@@ -1,47 +1,46 @@
|
||||
import {useBeforeUnload, useBlocker} from "react-router-dom";
|
||||
import {useCallback, useEffect, useRef} from "react";
|
||||
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();
|
||||
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;
|
||||
}
|
||||
prevState.current = blocker.state;
|
||||
}, [blocker]);
|
||||
return typeof message === "string" ? !window.confirm(message) : false;
|
||||
},
|
||||
[message]
|
||||
)
|
||||
);
|
||||
|
||||
useBeforeUnload(
|
||||
useCallback(
|
||||
(event) => {
|
||||
if (beforeUnload && typeof message === "string") {
|
||||
event.preventDefault();
|
||||
event.returnValue = message;
|
||||
}
|
||||
},
|
||||
[message, beforeUnload]
|
||||
),
|
||||
{capture: true}
|
||||
);
|
||||
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;
|
||||
function Prompt({ when, message, ...props }) {
|
||||
usePrompt(when ? message : false, props);
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Prompt;
|
||||
export default Prompt;
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
export function alphaSort(a, b) {
|
||||
return (a ? a.toLowerCase() : "").localeCompare(b ? b.toLowerCase() : "");
|
||||
return (a ? a.toLowerCase() : "").localeCompare(b ? b.toLowerCase() : "");
|
||||
|
||||
// if (A < B)
|
||||
// //sort string ascending
|
||||
// return -1;
|
||||
// if (A > B) return 1;
|
||||
// return 0; //default return value (no sorting)
|
||||
// if (A < B)
|
||||
// //sort string ascending
|
||||
// return -1;
|
||||
// if (A > B) return 1;
|
||||
// return 0; //default return value (no sorting)
|
||||
}
|
||||
|
||||
export function dateSort(a, b) {
|
||||
return new Date(a) - new Date(b);
|
||||
return new Date(a) - new Date(b);
|
||||
}
|
||||
|
||||
export function statusSort(a, b, statusList) {
|
||||
return (
|
||||
statusList.findIndex((x) => x === a) - statusList.findIndex((x) => x === b)
|
||||
);
|
||||
return statusList.findIndex((x) => x === a) - statusList.findIndex((x) => x === b);
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
* @constructor
|
||||
*/
|
||||
export default function UndefinedToNull(obj, keys) {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (keys && keys.indexOf(key) >= 0) {
|
||||
if (obj[key] === undefined) obj[key] = null;
|
||||
} else {
|
||||
if (obj[key] === undefined) obj[key] = null;
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (keys && keys.indexOf(key) >= 0) {
|
||||
if (obj[key] === undefined) obj[key] = null;
|
||||
} else {
|
||||
if (obj[key] === undefined) obj[key] = null;
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
const usePrevious = (value, initialValue) => {
|
||||
const ref = useRef(initialValue);
|
||||
@@ -18,8 +18,8 @@ const useEffectDebugger = (effectHook, dependencies, dependencyNames = []) => {
|
||||
...accum,
|
||||
[keyName]: {
|
||||
before: previousDeps[index],
|
||||
after: dependency,
|
||||
},
|
||||
after: dependency
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ const useEffectDebugger = (effectHook, dependencies, dependencyNames = []) => {
|
||||
}, {});
|
||||
|
||||
if (Object.keys(changedDeps).length) {
|
||||
console.log('[use-effect-debugger] ', changedDeps);
|
||||
console.log("[use-effect-debugger] ", changedDeps);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
@@ -1,128 +1,127 @@
|
||||
import {useCallback, useEffect, useReducer, useState} 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 (!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 (!Array.isArray(shortcutKeys))
|
||||
throw new Error(
|
||||
"The first parameter to `useKeyboardShortcut` must be an ordered array of `KeyboardEvent.key` strings."
|
||||
);
|
||||
|
||||
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 (!shortcutKeys.length)
|
||||
throw new Error(
|
||||
"The first parameter to `useKeyboardShortcut` must contain atleast one `KeyboardEvent.key` string."
|
||||
);
|
||||
|
||||
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]);
|
||||
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 (!listenersAdded) {
|
||||
console.log('Added events for keyup and keydown');
|
||||
shortcutKeys.forEach((k) => {
|
||||
window.addEventListener("keydown", keydownListener(k));
|
||||
window.addEventListener("keyup", keyupListener(k))
|
||||
});
|
||||
}
|
||||
const { overrideSystem } = options || {};
|
||||
const initalKeyMapping = shortcutKeys.reduce((currentKeys, key) => {
|
||||
currentKeys[key.toLowerCase()] = false;
|
||||
return currentKeys;
|
||||
}, {});
|
||||
|
||||
setListenersAdded(true);
|
||||
const [keys, setKeys] = useReducer(keysReducer, initalKeyMapping);
|
||||
const [listenersAdded, setListenersAdded] = useState(false);
|
||||
|
||||
return () => {
|
||||
shortcutKeys.forEach((k) => {
|
||||
window.removeEventListener("keydown", keydownListener(k));
|
||||
window.removeEventListener("keyup", keyupListener(k));
|
||||
});
|
||||
}
|
||||
}, [listenersAdded]);
|
||||
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]
|
||||
);
|
||||
|
||||
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(() => {
|
||||
if (!listenersAdded) {
|
||||
console.log("Added events for keyup and keydown");
|
||||
shortcutKeys.forEach((k) => {
|
||||
window.addEventListener("keydown", keydownListener(k));
|
||||
window.addEventListener("keyup", keyupListener(k));
|
||||
});
|
||||
}
|
||||
|
||||
setListenersAdded(true);
|
||||
|
||||
return () => {
|
||||
shortcutKeys.forEach((k) => {
|
||||
window.removeEventListener("keydown", keydownListener(k));
|
||||
window.removeEventListener("keyup", keyupListener(k));
|
||||
});
|
||||
};
|
||||
}, [listenersAdded]);
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
import {useState} from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function useLocalStorage(key, initialValue) {
|
||||
// State to store our value
|
||||
// Pass initial state function to useState so logic is only executed once
|
||||
const [storedValue, setStoredValue] = useState(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return initialValue;
|
||||
}
|
||||
try {
|
||||
// Get from local storage by key
|
||||
const item = window.localStorage.getItem(key);
|
||||
// Parse stored json or if none return initialValue
|
||||
return item ? JSON.parse(item) : initialValue;
|
||||
} catch (error) {
|
||||
// If error also return initialValue
|
||||
console.log(error);
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
// Return a wrapped version of useState's setter function that ...
|
||||
// ... persists the new value to localStorage.
|
||||
const setValue = (value) => {
|
||||
try {
|
||||
// Allow value to be a function so we have same API as useState
|
||||
const valueToStore =
|
||||
value instanceof Function ? value(storedValue) : value;
|
||||
// Save state
|
||||
setStoredValue(valueToStore);
|
||||
// Save to local storage
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
||||
}
|
||||
} catch (error) {
|
||||
// A more advanced implementation would handle the error case
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
return [storedValue, setValue];
|
||||
// State to store our value
|
||||
// Pass initial state function to useState so logic is only executed once
|
||||
const [storedValue, setStoredValue] = useState(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return initialValue;
|
||||
}
|
||||
try {
|
||||
// Get from local storage by key
|
||||
const item = window.localStorage.getItem(key);
|
||||
// Parse stored json or if none return initialValue
|
||||
return item ? JSON.parse(item) : initialValue;
|
||||
} catch (error) {
|
||||
// If error also return initialValue
|
||||
console.log(error);
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
// Return a wrapped version of useState's setter function that ...
|
||||
// ... persists the new value to localStorage.
|
||||
const setValue = (value) => {
|
||||
try {
|
||||
// Allow value to be a function so we have same API as useState
|
||||
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
||||
// Save state
|
||||
setStoredValue(valueToStore);
|
||||
// Save to local storage
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
||||
}
|
||||
} catch (error) {
|
||||
// A more advanced implementation would handle the error case
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
return [storedValue, setValue];
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {useEffect, useRef} from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
function useTraceUpdate(props) {
|
||||
const prev = useRef(props);
|
||||
useEffect(() => {
|
||||
const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
|
||||
if (prev.current[k] !== v) {
|
||||
ps[k] = [prev.current[k], v];
|
||||
}
|
||||
return ps;
|
||||
}, {});
|
||||
if (Object.keys(changedProps).length > 0) {
|
||||
console.log("Changed props:", changedProps);
|
||||
}
|
||||
prev.current = props;
|
||||
});
|
||||
const prev = useRef(props);
|
||||
useEffect(() => {
|
||||
const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
|
||||
if (prev.current[k] !== v) {
|
||||
ps[k] = [prev.current[k], v];
|
||||
}
|
||||
return ps;
|
||||
}, {});
|
||||
if (Object.keys(changedProps).length > 0) {
|
||||
console.log("Changed props:", changedProps);
|
||||
}
|
||||
prev.current = props;
|
||||
});
|
||||
}
|
||||
|
||||
export default useTraceUpdate;
|
||||
|
||||
Reference in New Issue
Block a user