Merged in feature/2020-06-04 (pull request #98)

Feature/2020 06 04
This commit is contained in:
Patrick Fic
2021-06-04 20:21:40 +00:00
15 changed files with 207 additions and 73 deletions

View File

@@ -2527,6 +2527,27 @@
<folder_node>
<name>validation</name>
<children>
<concept_node>
<name>manualinhouse</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>unique_invoice_number</name>
<definition_loaded>false</definition_loaded>
@@ -12448,6 +12469,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>viewreleasenotes</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
@@ -31034,7 +31076,7 @@
<name>templates</name>
<children>
<concept_node>
<name>attendance</name>
<name>attendance_detail</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>

View File

@@ -9,12 +9,50 @@
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<link rel="apple-touch-icon" href="logo192.png" />
<!-- <script
data-jsd-embedded
data-key="51adb36e-ee16-46b1-a4c6-4b6d5fcd8530"
data-base-url="https://jsd-widget.atlassian.com"
src="https://jsd-widget.atlassian.com/assets/embed.js"
></script> -->
<script>
!(function () {
"use strict";
var e = [
"debug",
"destroy",
"do",
"help",
"identify",
"is",
"off",
"on",
"ready",
"render",
"reset",
"safe",
"set",
];
if (window.noticeable)
console.warn("Noticeable SDK code snippet loaded more than once");
else {
var n = (window.noticeable = window.noticeable || []);
function t(e) {
return function () {
var t = Array.prototype.slice.call(arguments);
return t.unshift(e), n.push(t), n;
};
}
!(function () {
for (var o = 0; o < e.length; o++) {
var r = e[o];
n[r] = t(r);
}
})(),
(function () {
var e = document.createElement("script");
(e.async = !0), (e.src = "https://sdk.noticeable.io/l.js");
var n = document.head;
n.insertBefore(e, n.firstChild);
})();
}
})();
</script>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

View File

@@ -20,14 +20,20 @@ export default function AppContainer() {
// Include the Crisp code here, without the <script></script> tags
window.$crisp = [];
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
var d = document;
var s = d.createElement("script");
s.src = "https://client.crisp.chat/l.js";
s.async = 1;
d.getElementsByTagName("head")[0].appendChild(s);
//Release Notes
// var rs = d.createElement("script");
// rs.src = "https://sdk.noticeable.io/s.js";
// //rs.async = 1;
// d.getElementsByTagName("head")[0].appendChild(rs);
// // window.noticeable.render("widget", "IABVNO4scRKY11XBQkNr");
return () => {
d.getElementsByTagName("head")[0].removeChild(s);
};

View File

@@ -118,3 +118,9 @@
.production-list-min-height {
min-height: 19px;
}
#noticeable-widget {
iframe {
z-index: 2 !important;
}
}

View File

@@ -106,6 +106,18 @@ export function BillFormComponent({
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
if (
value &&
!getFieldValue(["isinhouse"]) &&
value === bodyshop.inhousevendorid
) {
return Promise.reject(t("bills.validation.manualinhouse"));
}
return Promise.resolve();
},
}),
]}
>
<VendorSearchSelect

View File

@@ -14,13 +14,13 @@ import Icon, {
LineChartOutlined,
PaperClipOutlined,
PhoneOutlined,
QuestionCircleFilled,
ScheduleOutlined,
SettingOutlined,
TeamOutlined,
ToolFilled,
UnorderedListOutlined,
UserOutlined,
QuestionCircleFilled,
} from "@ant-design/icons";
import { Layout, Menu } from "antd";
import React from "react";
@@ -256,6 +256,13 @@ function Header({
{t("menus.header.temporarydocs")}
</Link>
</Menu.Item>
<Menu.Item
key="help"
onClick={() => {
window.open("https://help.imex.online/", "_blank");
}}
icon={<Icon component={QuestionCircleFilled} />}
/>
<Menu.SubMenu title={t("menus.header.shop")} icon={<SettingOutlined />}>
<Menu.Item key="shop" icon={<Icon component={GiSettingsKnobs} />}>
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
@@ -285,15 +292,6 @@ function Header({
<Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.Item
key="shop"
onClick={() => {
window.open("https://help.imex.online/", "_blank");
}}
icon={<Icon component={QuestionCircleFilled} />}
>
{t("menus.header.help")}
</Menu.Item>
<Menu.SubMenu
style={{ float: "right" }}
title={

View File

@@ -9,7 +9,6 @@ import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import StartChatButton from "../chat-open-button/chat-open-button.component";
const mapStateToProps = createStructuredSelector({
@@ -32,7 +31,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
dataIndex: "ro_number",
key: "ro_number",
width: "8%",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sorter: true, //(a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) => (
@@ -44,12 +43,12 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
dataIndex: "ownr_ln",
key: "ownr_ln",
ellipsis: true,
// sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
//sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
width: "25%",
// sortOrder: sortcolumn === "owner" && sortorder,
//sortOrder: sortcolumn === "ownr_ln" && sortorder,
render: (text, record) => {
return record.owner ? (
<Link to={"/manage/owners/" + record.owner.id}>
@@ -80,7 +79,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
key: "status",
width: "10%",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sorter: true, // (a, b) => alphaSort(a.status, b.status),
sortOrder: sortcolumn === "status" && sortorder,
render: (text, record) => {
return record.status || t("general.labels.na");
@@ -117,7 +116,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
key: "plate_no",
width: "8%",
ellipsis: true,
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder: sortcolumn === "plate_no" && sortorder,
render: (text, record) => {
return record.plate_no ? record.plate_no : "";
@@ -129,7 +128,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
key: "clm_no",
width: "12%",
ellipsis: true,
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder: sortcolumn === "clm_no" && sortorder,
render: (text, record) => {
return record.clm_no ? (
@@ -150,7 +149,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
dataIndex: "clm_total",
key: "clm_total",
width: "10%",
sorter: (a, b) => a.clm_total - b.clm_total,
sorter: true, //(a, b) => a.clm_total - b.clm_total,
sortOrder: sortcolumn === "clm_total" && sortorder,
render: (text, record) => {
return record.clm_total ? (
@@ -173,7 +172,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
const handleTableChange = (pagination, filters, sorter) => {
search.page = pagination.current;
search.sortcolumn = sorter.columnKey;
search.sortcolumn = sorter.column && sorter.column.key;
search.sortorder = sorter.order;
if (filters.status) {
search.statusFilters = JSON.stringify(_.flattenDeep(filters.status));

View File

@@ -1,6 +1,6 @@
import { AlertOutlined } from "@ant-design/icons";
import * as Sentry from "@sentry/react";
import { Button, notification } from "antd";
import { Button, notification, Space } from "antd";
//import "antd/dist/antd.css";
import "antd/dist/antd.less";
import Dinero from "dinero.js";
@@ -26,8 +26,7 @@ Dinero.globalRoundingMode = "HALF_EVEN";
if (process.env.NODE_ENV !== "development") {
Sentry.init({
dsn:
"https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
dsn: "https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
integrations: [
// new Integrations.BrowserTracing(),
// new Sentry.Integrations.Breadcrumbs({ console: true }),
@@ -57,21 +56,30 @@ const onServiceWorkerUpdate = (registration) => {
console.log("onServiceWorkerUpdate", registration);
const btn = (
<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 flex>
<Button
onClick={async () => {
window.open("https://imex-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>
);
notification.open({
icon: <AlertOutlined />,

View File

@@ -35,13 +35,14 @@ export function AllJobs({ setBreadcrumbs, setSelectedHeader }) {
offset: page ? (page - 1) * 25 : 0,
limit: 25,
...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}),
order: [
order: [
{
[sortcolumn || "created_at"]: sortorder
? sortorder === "descend"
? "desc"
: "asc"
: "desc",
[sortcolumn || "created_at"]:
sortorder && sortorder !== "false"
? sortorder === "descend"
? "desc"
: "asc"
: "desc",
},
],
},

View File

@@ -181,7 +181,10 @@ const mapStateToProps = createStructuredSelector({
export function Manage({ match, conflict, bodyshop }) {
const { t } = useTranslation();
useEffect(() => {
const widgetId = "IABVNO4scRKY11XBQkNr";
window.noticeable.render("widget", widgetId);
}, []);
useEffect(() => {
document.title = t("titles.app");
}, [t]);
@@ -383,11 +386,22 @@ export function Manage({ match, conflict, bodyshop }) {
<ChatAffixContainer />
<BackTop />
<Footer>
<div style={{ textAlign: "center", margin: "1rem 0rem" }}>
<div>
{`ImEX Online ${
process.env.REACT_APP_GIT_SHA
} - ${preval`module.exports = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});`}`}
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
margin: "1rem 0rem",
}}
>
<div style={{ display: "flex" }}>
<div>
{`ImEX Online ${
process.env.REACT_APP_GIT_SHA
} - ${preval`module.exports = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});`}`}
</div>
<div id="noticeable-widget" style={{ marginLeft: "1rem" }} />
</div>
<Link to="/about" target="_blank" style={{ color: "#ccc" }}>
Disclaimer

View File

@@ -165,7 +165,7 @@ export function* onSignInSuccess() {
export function* signInSuccessSaga({ payload }) {
LogRocket.identify(payload.email);
try {
window.$crisp.push(["set", "user:email", [payload.email]]);
// window.$crisp.push(["set", "user:email", [payload.email]]);
window.$crisp.push([
"set",
"user:nickname",
@@ -236,6 +236,10 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
(a) => a.useremail === userEmail
);
if (authRecord[0] && authRecord[0].user.validemail) {
window.$crisp.push(["set", "user:email", [authRecord[0].user.email]]);
}
yield put(setAuthlevel(authRecord[0] ? authRecord[0].authlevel : 0));
yield put(
updateUserDetailsSuccess(

View File

@@ -167,6 +167,7 @@
"exported": "Bill exported successfully."
},
"validation": {
"manualinhouse": "Manual posting to the in house vendor is restricted. ",
"unique_invoice_number": "This invoice number has already been entered for this vendor."
}
},
@@ -797,7 +798,8 @@
"selectall": "Select All",
"submit": "Submit",
"submitticket": "Submit a Support Ticket",
"view": "View"
"view": "View",
"viewreleasenotes": "See What's Changed"
},
"errors": {
"notfound": "No record was found."
@@ -1868,7 +1870,7 @@
"vendor": "Vendor"
},
"templates": {
"attendance": "Attendance (All Employees)",
"attendance_detail": "Attendance (All Employees)",
"attendance_employee": "Employee Attendance",
"attendance_summary": "Attendance Summary (All Employees)",
"credits_not_received_date": "Credits not Received by Date",
@@ -1902,7 +1904,7 @@
"open_orders_ins_co": "Open Orders by Insurance Company",
"parts_backorder": "Backordered Parts",
"payments_by_date": "Payments by Date",
"payments_by_date_type": "Payments by Date Range",
"payments_by_date_type": "Payments by Date and Type",
"purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)",
"purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)",
"purchases_by_date_range_detail": "Purchases by Date - Detail",

View File

@@ -167,6 +167,7 @@
"exported": ""
},
"validation": {
"manualinhouse": "",
"unique_invoice_number": ""
}
},
@@ -797,7 +798,8 @@
"selectall": "",
"submit": "",
"submitticket": "",
"view": ""
"view": "",
"viewreleasenotes": ""
},
"errors": {
"notfound": ""
@@ -1868,7 +1870,7 @@
"vendor": ""
},
"templates": {
"attendance": "",
"attendance_detail": "",
"attendance_employee": "",
"attendance_summary": "",
"credits_not_received_date": "",

View File

@@ -167,6 +167,7 @@
"exported": ""
},
"validation": {
"manualinhouse": "",
"unique_invoice_number": ""
}
},
@@ -797,7 +798,8 @@
"selectall": "",
"submit": "",
"submitticket": "",
"view": ""
"view": "",
"viewreleasenotes": ""
},
"errors": {
"notfound": ""
@@ -1868,7 +1870,7 @@
"vendor": ""
},
"templates": {
"attendance": "",
"attendance_detail": "",
"attendance_employee": "",
"attendance_summary": "",
"credits_not_received_date": "",

View File

@@ -555,10 +555,10 @@ export const TemplateList = (type, context) => {
idtype: "employee",
disabled: false,
},
attendance: {
title: i18n.t("reportcenter.templates.attendance"),
subject: i18n.t("reportcenter.templates.attendance"),
key: "attendance",
attendance_detail: {
title: i18n.t("reportcenter.templates.attendance_detail"),
subject: i18n.t("reportcenter.templates.attendance_detail"),
key: "attendance_detail",
disabled: false,
rangeFilter: {
@@ -710,7 +710,7 @@ export const TemplateList = (type, context) => {
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_summary_open_ins_cƒo"
"reportcenter.templates.hours_sold_summary_open_ins_co"
),
key: "hours_sold_summary_open_ins_co",
//idtype: "vendor",