- Progress commit

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-03-28 13:03:09 -04:00
876 changed files with 111723 additions and 125595 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import React from "react";
export function PartsLabelMulti() {
return <div></div>;
return <div></div>;
}

View File

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

View File

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

View File

@@ -1,3 +1,3 @@
export function onlyUnique(value, index, self, key) {
return self.indexOf(value) === index;
return self.indexOf(value) === index;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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