Compare commits

..

14 Commits

Author SHA1 Message Date
Dave Richer
f50b198c21 feature/IO-3223-canny - Small syntactic update 2025-04-25 17:12:18 -04:00
Dave Richer
3495326de3 feature/IO-3223-canny - Merge release / fix conflicts 2025-04-25 17:06:44 -04:00
Patrick Fic
b5973085e7 IO-3223 Add Canny for feature request and change log. 2025-04-25 14:02:40 -07:00
Dave Richer
8687214420 release/2025-04-25 - update handleInvoiceBasedPayment.test.js 2025-04-25 11:58:47 -04:00
Dave Richer
d61b89a1e5 release/2025-04-25 - Add logging around handleInvoiceBasePayment paymentResponse, toned logs down. fixed issue in paymentResponseResults 2025-04-25 11:54:36 -04:00
Allan Carr
468b42abd2 Merged in feature/IO-3220-VPB-Popup (pull request #2284)
IO-3220 VPB Board Settings Popup

Approved-by: Dave Richer
2025-04-25 14:59:13 +00:00
Allan Carr
fc03e5f983 IO-3220 VPB Board Settings Popup
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-04-24 13:32:27 -07:00
Allan Carr
c4742e38ea Merged in feature/IO-3213-Hit-and-Run-Toggle (pull request #2280)
IO-3213 Hit and Run Toggle

Approved-by: Dave Richer
2025-04-24 15:41:41 +00:00
Allan Carr
99e1adbe13 Merge branch 'release/2025-04-25' into feature/IO-3213-Hit-and-Run-Toggle
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>

# Conflicts:
#	client/src/components/jobs-detail-general/jobs-detail-general.component.jsx
2025-04-24 08:42:32 -07:00
Allan Carr
eb5c797a43 Merged in feature/IO-3215-Employee-Assignment-Timeticket-Modal (pull request #2279)
IO-3215 Employee Assignment Timeticket Modal

Approved-by: Dave Richer
2025-04-24 15:34:42 +00:00
Allan Carr
0595c5545e Merged in feature/IO-3212-ACV-Amount (pull request #2281)
IO-3212 ACV Amount

Approved-by: Dave Richer
2025-04-24 15:33:41 +00:00
Allan Carr
55944257aa IO-3212 ACV Amount
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-04-23 13:16:49 -07:00
Allan Carr
03241778fa IO-3212 ACV Amount
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-04-23 13:10:42 -07:00
Allan Carr
555b81fb14 IO-3213 Hit and Run Toggle
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-04-23 12:04:29 -07:00
32 changed files with 5362 additions and 2463 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -74,50 +74,8 @@
})();
</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 || []);
<script>!function(w,d,i,s){function l(){if(!d.getElementById(i)){var f=d.getElementsByTagName(s)[0],e=d.createElement(s);e.type="text/javascript",e.async=!0,e.src="https://canny.io/sdk.js",f.parentNode.insertBefore(e,f)}}if("function"!=typeof w.Canny){var c=function(){c.q.push(arguments)};c.q=[],w.Canny=c,"complete"===d.readyState?l():w.attachEvent?w.attachEvent("onload",l):w.addEventListener("load",l,!1)}}(window,document,"canny-jssdk","script");</script>
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>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

2893
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
import { Col, Form, Input, InputNumber, Row, Select, Space, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -188,6 +187,12 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
<Form.Item label={t("jobs.fields.tlos_ind")} name="tlos_ind" valuePropName="checked">
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.hit_and_run")} name="hit_and_run" valuePropName="checked">
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.acv_amount")} name="acv_amount">
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
</FormRow>
</Col>
<Col {...lossColDamage}>

View File

@@ -10,6 +10,7 @@ import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import dayjs from "../../utils/day";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import DataLabel from "../data-label/data-label.component";
@@ -21,7 +22,6 @@ import ProductionListColumnComment from "../production-list-columns/production-l
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
import "./jobs-detail-header.styles.scss";
import dayjs from "../../utils/day";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -149,6 +149,14 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
</Space>
</Tag>
)}
{job.hit_and_run && (
<Tag color="green">
<Space>
<WarningFilled />
<span>{t("jobs.fields.hit_and_run")}</span>
</Space>
</Tag>
)}
</Space>
</div>
</Card>

View File

@@ -1,7 +1,6 @@
import React from "react";
import { Card, Form, Select } from "antd";
import { useTranslation } from "react-i18next";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
const FilterSettings = ({
selectedMdInsCos,

View File

@@ -1,10 +1,9 @@
import { Card, Checkbox, Col, Form, Row } from "antd";
import React from "react";
import PropTypes from "prop-types";
const InformationSettings = ({ t }) => (
<Card title={t("production.settings.information")}>
<Row gutter={[16, 16]}>
<Card title={t("production.settings.information")} style={{ maxWidth: "100%", overflowX: "auto" }}>
<Row gutter={[16, 16]} wrap>
{[
"model_info",
"ownr_nm",
@@ -21,7 +20,7 @@ const InformationSettings = ({ t }) => (
"subtotal",
"tasks"
].map((item) => (
<Col span={4} key={item}>
<Col xs={24} sm={12} md={8} lg={6} key={item}>
<Form.Item name={item} valuePropName="checked">
<Checkbox>{t(`production.labels.${item}`)}</Checkbox>
</Form.Item>

View File

@@ -1,9 +1,8 @@
import { Card, Col, Form, Radio, Row } from "antd";
import React from "react";
import PropTypes from "prop-types";
const LayoutSettings = ({ t }) => (
<Card title={t("production.settings.layout")}>
<Card title={t("production.settings.layout")} style={{ maxWidth: "100%", overflowX: "auto" }}>
<Row gutter={[16, 16]}>
{[
{
@@ -48,9 +47,9 @@ const LayoutSettings = ({ t }) => (
]
}
].map(({ name, label, options }) => (
<Col span={4} key={name}>
<Col xs={24} sm={16} md={10} lg={8} key={name}>
<Form.Item name={name} label={label}>
<Radio.Group>
<Radio.Group style={{ display: "flex", flexWrap: "nowrap" }}>
{options.map((option) => (
<Radio.Button key={option.value.toString()} value={option.value}>
{option.label}

View File

@@ -1,8 +1,7 @@
import { Card, Checkbox, Form } from "antd";
import PropTypes from "prop-types";
import { DragDropContext, Draggable, Droppable } from "../trello-board/dnd/lib/index.js";
import { statisticsItems } from "./defaultKanbanSettings.js";
import { Card, Checkbox, Form } from "antd";
import React from "react";
import PropTypes from "prop-types";
const StatisticsSettings = ({ t, statisticsOrder, setStatisticsOrder, setHasChanges }) => {
const onDragEnd = (result) => {

View File

@@ -1,17 +1,17 @@
import { SettingOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Card, Col, Form, Popover, Row, Tabs } from "antd";
import { isFunction } from "lodash";
import PropTypes from "prop-types";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useNotification } from "../../../contexts/Notifications/notificationContext.jsx";
import { UPDATE_KANBAN_SETTINGS } from "../../../graphql/user.queries.js";
import { defaultKanbanSettings, mergeWithDefaults } from "./defaultKanbanSettings.js";
import LayoutSettings from "./LayoutSettings.jsx";
import InformationSettings from "./InformationSettings.jsx";
import StatisticsSettings from "./StatisticsSettings.jsx";
import FilterSettings from "./FilterSettings.jsx";
import PropTypes from "prop-types";
import { isFunction } from "lodash";
import { useNotification } from "../../../contexts/Notifications/notificationContext.jsx";
import { SettingOutlined } from "@ant-design/icons";
import InformationSettings from "./InformationSettings.jsx";
import LayoutSettings from "./LayoutSettings.jsx";
import StatisticsSettings from "./StatisticsSettings.jsx";
function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bodyshop, data, onSettingsChange }) {
const [form] = Form.useForm();
@@ -87,7 +87,7 @@ function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bod
};
const overlay = (
<Card style={{ minWidth: "80vw" }}>
<Card style={{ maxWidth: "80vw", width: "100%"}}>
<Form form={form} onFinish={handleFinish} layout="vertical" onValuesChange={handleValuesChange}>
<Tabs
defaultActiveKey="1"

View File

@@ -113,7 +113,7 @@ export function UpdateAlert({ updateAvailable }) {
</Col>
<Col sm={24} md={8} lg={6}>
<Space wrap>
<Button onClick={() => window.open("https://imex-online.noticeable.news/", "_blank")}>
<Button onClick={() => window.open("https://shopmanagement.canny.io/changelog", "_blank")}>
{i18n.t("general.actions.viewreleasenotes")}
</Button>
<Button loading={loading} type="primary" onClick={() => ReloadNewVersion()}>

View File

@@ -423,6 +423,7 @@ export const GET_JOB_BY_PK = gql`
actual_completion
actual_delivery
actual_in
acv_amount
adjustment_bottom_line
alt_transport
area_of_damage
@@ -511,6 +512,7 @@ export const GET_JOB_BY_PK = gql`
est_ph1
flat_rate_ats
federal_tax_rate
hit_and_run
id
inproduction
ins_addr1

View File

@@ -0,0 +1,42 @@
import axios from "axios";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key))
});
export function FeedbackPage({ setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.feature-request", {
app: InstanceRenderManager({
imex: "$t(titles.imexonline)",
rome: "$t(titles.romeonline)"
})
});
setBreadcrumbs([{ link: "/manage/feature-request", label: t("titles.bc.feature-request") }]);
}, [t, setBreadcrumbs, setSelectedHeader]);
useEffect(() => {
async function RenderCanny() {
const ssoToken = await axios.post("/sso/canny");
window.Canny("render", {
boardToken: "bba97b06-70db-0334-dee7-8108d73ef614",
basePath: `/manage/feature-request`, // See step 2
ssoToken: ssoToken.data, // See step 3,
theme: "light" // options: light [default], dark, auto
});
}
RenderCanny();
}, []);
return <div data-canny />;
}
export default connect(null, mapDispatchToProps)(FeedbackPage);

View File

@@ -1,4 +1,5 @@
import { FloatButton, Layout, Spin } from "antd";
import { Button, FloatButton, Layout, Space, Spin } from "antd";
import { AlertOutlined, BulbOutlined } from "@ant-design/icons";
// import preval from "preval.macro";
import React, { lazy, Suspense, useEffect, useState } from "react";
@@ -19,7 +20,6 @@ import LoadingSpinner from "../../components/loading-spinner/loading-spinner.com
import PartnerPingComponent from "../../components/partner-ping/partner-ping.component";
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
import { requestForToken } from "../../firebase/firebase.utils";
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors";
import UpdateAlert from "../../components/update-alert/update-alert.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
@@ -56,6 +56,7 @@ const ContractCreatePage = lazy(() => import("../contract-create/contract-create
const ContractDetailPage = lazy(() => import("../contract-detail/contract-detail.page.container"));
const ContractsList = lazy(() => import("../contracts/contracts.page.container"));
const BillsListPage = lazy(() => import("../bills/bills.page.container"));
const FeatureRequestPage = lazy(() => import("../feature-request/feature-request.page.jsx"));
const JobCostingModal = lazy(() => import("../../components/job-costing-modal/job-costing-modal.container"));
const ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container"));
@@ -180,15 +181,12 @@ export function Manage({ conflict, bodyshop, alerts, setAlerts }) {
});
}
}, [alerts, displayedAlertIds, notification]);
useEffect(() => {
const widgetId = InstanceRenderManager({
imex: "IABVNO4scRKY11XBQkNr",
rome: "mQdqARMzkZRUVugJ6TdS"
});
window.noticeable.render("widget", widgetId);
requestForToken().catch((error) => {
console.error(`Unable to request for token.`, error);
window.Canny("initChangelog", {
appID: "680bd2c7ee501290377f6686",
position: "top",
align: "left",
theme: "light" // options: light [default], dark, auto
});
}, []);
@@ -480,6 +478,8 @@ export function Manage({ conflict, bodyshop, alerts, setAlerts }) {
// element={<ShopTemplates />}
// />
}
<Route path="/feature-request/*" index element={<FeatureRequestPage />} />
<Route
path="/shop/vendors"
element={
@@ -669,7 +669,12 @@ export function Manage({ conflict, bodyshop, alerts, setAlerts }) {
margin: "1rem 0rem"
}}
>
<div style={{ display: "flex" }}>
<Link to="/manage/feature-request">
<Button icon={<BulbOutlined />} type="text">
{t("general.labels.feature-request")}
</Button>
</Link>
<Space>
<WssStatusDisplayComponent />
<div onClick={broadcastMessage}>
{`${InstanceRenderManager({
@@ -677,8 +682,10 @@ export function Manage({ conflict, bodyshop, alerts, setAlerts }) {
rome: t("titles.romeonline")
})} - ${import.meta.env.VITE_APP_GIT_SHA_DATE}`}
</div>
<div id="noticeable-widget" style={{ marginLeft: "1rem" }} />
</div>
<Button icon={<AlertOutlined />} data-canny-changelog type="text">
{t("general.labels.changelog")}
</Button>
</Space>
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
Disclaimer & Notices
</Link>

View File

@@ -335,7 +335,6 @@
"intellipay_config": {
"cash_discount_percentage": "Cash Discount %",
"enable_cash_discount": "Enable Cash Discounting",
"payment_type": "Payment Type Map",
"payment_map": {
"amex": "American Express",
"disc": "Discover",
@@ -344,7 +343,8 @@
"jcb": "JCB",
"mast": "MasterCard",
"visa": "Visa"
}
},
"payment_type": "Payment Type Map"
},
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
"invoice_local_tax_rate": "Invoices - Local Tax Rate",
@@ -1236,6 +1236,7 @@
"areyousure": "Are you sure?",
"barcode": "Barcode",
"cancel": "Are you sure you want to cancel? Your changes will not be saved.",
"changelog": "Change Log",
"clear": "Clear",
"confirmpassword": "Confirm Password",
"created_at": "Created At",
@@ -1245,6 +1246,7 @@
"errors": "Errors",
"excel": "Excel",
"exceptiontitle": "An error has occurred.",
"feature-request": "Have a feature request?",
"friday": "Friday",
"globalsearch": "Global Search",
"help": "Help",
@@ -1323,9 +1325,9 @@
"notfoundtitle": "We couldn't find what you're looking for...",
"partnernotrunning": "{{app}} has detected that the partner is not running. Please ensure it is running to enable full functionality.",
"rbacunauth": "You are not authorized to view this content. Please reach out to your shop manager to change your access level.",
"submit-for-testing": "Submitted Job for testing successfully.",
"unsavedchanges": "You have unsaved changes.",
"unsavedchangespopup": "You have unsaved changes. Are you sure you want to leave?",
"submit-for-testing": "Submitted Job for testing successfully."
"unsavedchangespopup": "You have unsaved changes. Are you sure you want to leave?"
},
"validation": {
"dateRangeExceeded": "The date range has been exceeded.",
@@ -1636,6 +1638,7 @@
"actual_completion": "Actual Completion",
"actual_delivery": "Actual Delivery",
"actual_in": "Actual In",
"acv_amount": "ACV Amount",
"adjustment_bottom_line": "Adjustments",
"adjustmenthours": "Adjustment Hours",
"alt_transport": "Alt. Trans.",
@@ -1761,9 +1764,10 @@
"est_ct_ln": "Estimator Last Name",
"est_ea": "Estimator Email",
"est_ph1": "Estimator Phone #",
"flat_rate_ats": "Flat Rate ATS?",
"federal_tax_payable": "Federal Tax Payable",
"federal_tax_rate": "Federal Tax Rate",
"flat_rate_ats": "Flat Rate ATS?",
"hit_and_run": "Hit and Run",
"ins_addr1": "Insurance Co. Address",
"ins_city": "Insurance Co. City",
"ins_co_id": "Insurance Co. ID",
@@ -2317,8 +2321,8 @@
"duplicate": "Duplicate this Job",
"duplicatenolines": "Duplicate this Job without Repair Data",
"newcccontract": "Create Courtesy Car Contract",
"void": "Void Job",
"submit-for-testing": "Submit for Testing"
"submit-for-testing": "Submit for Testing",
"void": "Void Job"
},
"jobsdetail": {
"claimdetail": "Claim Details",
@@ -2424,6 +2428,60 @@
"updated": "Note updated successfully."
}
},
"notifications": {
"actions": {
"remove": "Remove"
},
"aria": {
"toggle": "Toggle Watching Job"
},
"channels": {
"app": "App",
"email": "Email",
"fcm": "Push"
},
"labels": {
"add-watchers": "Add Watchers",
"add-watchers-team": "Add Team Members",
"employee-search": "Search for an Employee",
"mark-all-read": "Mark All Read",
"new-notification-title": "New Notification:",
"no-watchers": "No Watchers",
"notification-center": "Notification Center",
"notification-popup-title": "Changes for Job #{{ro_number}}",
"notification-settings-failure": "Error saving Notification Settings. {{error}}",
"notification-settings-success": "Notification Settings saved successfully.",
"notificationscenarios": "Job Notification Scenarios",
"ro-number": "RO #{{ro_number}}",
"save": "Save Scenarios",
"scenario": "Scenario",
"show-unread-only": "Show Unread Only",
"teams-search": "Search for a Team",
"unwatch": "Unwatch",
"watch": "Watch",
"watching-issue": "Watching"
},
"scenarios": {
"alternate-transport-changed": "Alternate Transport Changed",
"bill-posted": "Bill Posted",
"critical-parts-status-changed": "Critical Parts Status Changed",
"intake-delivery-checklist-completed": "Intake or Delivery Checklist Completed",
"job-added-to-production": "Job Added to Production",
"job-assigned-to-me": "Job Assigned to Me",
"job-status-change": "Job Status Changed",
"new-media-added-reassigned": "New Media Added or Reassigned",
"new-note-added": "New Note Added",
"new-time-ticket-posted": "New Time Ticket Posted",
"part-marked-back-ordered": "Part Marked Back Ordered",
"payment-collected-completed": "Payment Collected / Completed",
"schedule-dates-changed": "Schedule Dates Changed",
"supplement-imported": "Supplement Imported",
"tasks-updated-created": "Tasks Updated / Created"
},
"tooltips": {
"job-watchers": "Job Watchers"
}
},
"owner": {
"labels": {
"noownerinfo": "No owner information."
@@ -3420,6 +3478,7 @@
"dashboard": "Dashboard",
"dms": "DMS Export",
"export-logs": "Export Logs",
"feature-request": "Feature Requet",
"inventory": "Inventory",
"jobs": "Jobs",
"jobs-active": "Active Jobs",
@@ -3464,6 +3523,7 @@
"dashboard": "Dashboard | {{app}}",
"dms": "DMS Export | {{app}}",
"export-logs": "Export Logs | {{app}}",
"feature-request": "Feature Request | {{app}}",
"imexonline": "ImEX Online",
"inventory": "Inventory | {{app}}",
"jobs": "Active Jobs | {{app}}",
@@ -3681,10 +3741,10 @@
"users": {
"errors": {
"signinerror": {
"auth/invalid-email": "A user with this email does not exist.",
"auth/user-disabled": "User account disabled. ",
"auth/user-not-found": "A user with this email does not exist.",
"auth/wrong-password": "The email and password combination you provided is incorrect.",
"auth/invalid-email": "A user with this email does not exist."
"auth/wrong-password": "The email and password combination you provided is incorrect."
}
}
},
@@ -3784,60 +3844,6 @@
"validation": {
"unique_vendor_name": "You must enter a unique vendor name."
}
},
"notifications": {
"labels": {
"notification-center": "Notification Center",
"scenario": "Scenario",
"notificationscenarios": "Job Notification Scenarios",
"save": "Save Scenarios",
"watching-issue": "Watching",
"add-watchers": "Add Watchers",
"employee-search": "Search for an Employee",
"teams-search": "Search for a Team",
"add-watchers-team": "Add Team Members",
"new-notification-title": "New Notification:",
"show-unread-only": "Show Unread Only",
"mark-all-read": "Mark All Read",
"notification-popup-title": "Changes for Job #{{ro_number}}",
"ro-number": "RO #{{ro_number}}",
"no-watchers": "No Watchers",
"notification-settings-success": "Notification Settings saved successfully.",
"notification-settings-failure": "Error saving Notification Settings. {{error}}",
"watch": "Watch",
"unwatch": "Unwatch"
},
"actions": {
"remove": "Remove"
},
"aria": {
"toggle": "Toggle Watching Job"
},
"tooltips": {
"job-watchers": "Job Watchers"
},
"scenarios": {
"job-assigned-to-me": "Job Assigned to Me",
"bill-posted": "Bill Posted",
"critical-parts-status-changed": "Critical Parts Status Changed",
"part-marked-back-ordered": "Part Marked Back Ordered",
"new-note-added": "New Note Added",
"supplement-imported": "Supplement Imported",
"schedule-dates-changed": "Schedule Dates Changed",
"tasks-updated-created": "Tasks Updated / Created",
"new-media-added-reassigned": "New Media Added or Reassigned",
"new-time-ticket-posted": "New Time Ticket Posted",
"intake-delivery-checklist-completed": "Intake or Delivery Checklist Completed",
"job-added-to-production": "Job Added to Production",
"job-status-change": "Job Status Changed",
"payment-collected-completed": "Payment Collected / Completed",
"alternate-transport-changed": "Alternate Transport Changed"
},
"channels": {
"app": "App",
"email": "Email",
"fcm": "Push"
}
}
}
}

View File

@@ -335,7 +335,6 @@
"intellipay_config": {
"cash_discount_percentage": "",
"enable_cash_discount": "",
"payment_type": "",
"payment_map": {
"amex": "American Express",
"disc": "Discover",
@@ -344,7 +343,8 @@
"jcb": "JCB",
"mast": "MasterCard",
"visa": "Visa"
}
},
"payment_type": ""
},
"invoice_federal_tax_rate": "",
"invoice_local_tax_rate": "",
@@ -1236,6 +1236,7 @@
"areyousure": "",
"barcode": "código de barras",
"cancel": "",
"changelog": "",
"clear": "",
"confirmpassword": "",
"created_at": "",
@@ -1245,6 +1246,7 @@
"errors": "",
"excel": "",
"exceptiontitle": "",
"feature-request": "",
"friday": "",
"globalsearch": "",
"help": "",
@@ -1323,9 +1325,9 @@
"notfoundtitle": "",
"partnernotrunning": "",
"rbacunauth": "",
"submit-for-testing": "",
"unsavedchanges": "Usted tiene cambios no guardados.",
"unsavedchangespopup": "",
"submit-for-testing": ""
"unsavedchangespopup": ""
},
"validation": {
"dateRangeExceeded": "",
@@ -1636,6 +1638,7 @@
"actual_completion": "Realización real",
"actual_delivery": "Entrega real",
"actual_in": "Real en",
"acv_amount": "",
"adjustment_bottom_line": "Ajustes",
"adjustmenthours": "",
"alt_transport": "",
@@ -1761,9 +1764,10 @@
"est_ct_ln": "Apellido del tasador",
"est_ea": "Correo electrónico del tasador",
"est_ph1": "Número de teléfono del tasador",
"flat_rate_ats": "",
"federal_tax_payable": "Impuesto federal por pagar",
"federal_tax_rate": "",
"flat_rate_ats": "",
"hit_and_run": "",
"ins_addr1": "Dirección de Insurance Co.",
"ins_city": "Ciudad de seguros",
"ins_co_id": "ID de la compañía de seguros",
@@ -2317,8 +2321,8 @@
"duplicate": "",
"duplicatenolines": "",
"newcccontract": "",
"void": "",
"submit-for-testing": ""
"submit-for-testing": "",
"void": ""
},
"jobsdetail": {
"claimdetail": "Detalles de la reclamación",
@@ -2424,6 +2428,60 @@
"updated": "Nota actualizada con éxito."
}
},
"notifications": {
"actions": {
"remove": ""
},
"aria": {
"toggle": ""
},
"channels": {
"app": "",
"email": "",
"fcm": ""
},
"labels": {
"add-watchers": "",
"add-watchers-team": "",
"employee-search": "",
"mark-all-read": "",
"new-notification-title": "",
"no-watchers": "",
"notification-center": "",
"notification-popup-title": "",
"notification-settings-failure": "",
"notification-settings-success": "",
"notificationscenarios": "",
"ro-number": "",
"save": "",
"scenario": "",
"show-unread-only": "",
"teams-search": "",
"unwatch": "",
"watch": "",
"watching-issue": ""
},
"scenarios": {
"alternate-transport-changed": "",
"bill-posted": "",
"critical-parts-status-changed": "",
"intake-delivery-checklist-completed": "",
"job-added-to-production": "",
"job-assigned-to-me": "",
"job-status-change": "",
"new-media-added-reassigned": "",
"new-note-added": "",
"new-time-ticket-posted": "",
"part-marked-back-ordered": "",
"payment-collected-completed": "",
"schedule-dates-changed": "",
"supplement-imported": "",
"tasks-updated-created": ""
},
"tooltips": {
"job-watchers": ""
}
},
"owner": {
"labels": {
"noownerinfo": ""
@@ -3420,6 +3478,7 @@
"dashboard": "",
"dms": "",
"export-logs": "",
"feature-request": "",
"inventory": "",
"jobs": "",
"jobs-active": "",
@@ -3464,6 +3523,7 @@
"dashboard": "",
"dms": "",
"export-logs": "",
"feature-request": "",
"imexonline": "",
"inventory": "",
"jobs": "Todos los trabajos | {{app}}",
@@ -3681,10 +3741,10 @@
"users": {
"errors": {
"signinerror": {
"auth/invalid-email": "",
"auth/user-disabled": "",
"auth/user-not-found": "",
"auth/wrong-password": "",
"auth/invalid-email": ""
"auth/wrong-password": ""
}
}
},
@@ -3784,60 +3844,6 @@
"validation": {
"unique_vendor_name": ""
}
},
"notifications": {
"labels": {
"notification-center": "",
"scenario": "",
"notificationscenarios": "",
"save": "",
"watching-issue": "",
"add-watchers": "",
"employee-search": "",
"teams-search": "",
"add-watchers-team": "",
"new-notification-title": "",
"show-unread-only": "",
"mark-all-read": "",
"notification-popup-title": "",
"ro-number": "",
"no-watchers": "",
"notification-settings-success": "",
"notification-settings-failure": "",
"watch": "",
"unwatch": ""
},
"actions": {
"remove": ""
},
"aria": {
"toggle": ""
},
"tooltips": {
"job-watchers": ""
},
"scenarios": {
"job-assigned-to-me": "",
"bill-posted": "",
"critical-parts-status-changed": "",
"part-marked-back-ordered": "",
"new-note-added": "",
"supplement-imported": "",
"schedule-dates-changed": "",
"tasks-updated-created": "",
"new-media-added-reassigned": "",
"new-time-ticket-posted": "",
"intake-delivery-checklist-completed": "",
"job-added-to-production": "",
"job-status-change": "",
"payment-collected-completed": "",
"alternate-transport-changed": ""
},
"channels": {
"app": "",
"email": "",
"fcm": ""
}
}
}
}

View File

@@ -335,7 +335,6 @@
"intellipay_config": {
"cash_discount_percentage": "",
"enable_cash_discount": "",
"payment_type": "",
"payment_map": {
"amex": "American Express",
"disc": "Discover",
@@ -344,7 +343,8 @@
"jcb": "JCB",
"mast": "MasterCard",
"visa": "Visa"
}
},
"payment_type": ""
},
"invoice_federal_tax_rate": "",
"invoice_local_tax_rate": "",
@@ -1236,6 +1236,7 @@
"areyousure": "",
"barcode": "code à barre",
"cancel": "",
"changelog": "",
"clear": "",
"confirmpassword": "",
"created_at": "",
@@ -1245,6 +1246,7 @@
"errors": "",
"excel": "",
"exceptiontitle": "",
"feature-request": "",
"friday": "",
"globalsearch": "",
"help": "",
@@ -1323,10 +1325,9 @@
"notfoundtitle": "",
"partnernotrunning": "",
"rbacunauth": "",
"submit-for-testing": "",
"unsavedchanges": "Vous avez des changements non enregistrés.",
"unsavedchangespopup": "",
"submit-for-testing": ""
"unsavedchangespopup": ""
},
"validation": {
"dateRangeExceeded": "",
@@ -1637,6 +1638,7 @@
"actual_completion": "Achèvement réel",
"actual_delivery": "Livraison réelle",
"actual_in": "En réel",
"acv_amount": "",
"adjustment_bottom_line": "Ajustements",
"adjustmenthours": "",
"alt_transport": "",
@@ -1762,9 +1764,10 @@
"est_ct_ln": "Nom de l'évaluateur",
"est_ea": "Courriel de l'évaluateur",
"est_ph1": "Numéro de téléphone de l'évaluateur",
"flat_rate_ats": "",
"federal_tax_payable": "Impôt fédéral à payer",
"federal_tax_rate": "",
"flat_rate_ats": "",
"hit_and_run": "",
"ins_addr1": "Adresse Insurance Co.",
"ins_city": "Insurance City",
"ins_co_id": "ID de la compagnie d'assurance",
@@ -2318,8 +2321,8 @@
"duplicate": "",
"duplicatenolines": "",
"newcccontract": "",
"void": "",
"submit-for-testing": ""
"submit-for-testing": "",
"void": ""
},
"jobsdetail": {
"claimdetail": "Détails de la réclamation",
@@ -2425,6 +2428,60 @@
"updated": "Remarque mise à jour avec succès."
}
},
"notifications": {
"actions": {
"remove": ""
},
"aria": {
"toggle": ""
},
"channels": {
"app": "",
"email": "",
"fcm": ""
},
"labels": {
"add-watchers": "",
"add-watchers-team": "",
"employee-search": "",
"mark-all-read": "",
"new-notification-title": "",
"no-watchers": "",
"notification-center": "",
"notification-popup-title": "",
"notification-settings-failure": "",
"notification-settings-success": "",
"notificationscenarios": "",
"ro-number": "",
"save": "",
"scenario": "",
"show-unread-only": "",
"teams-search": "",
"unwatch": "",
"watch": "",
"watching-issue": ""
},
"scenarios": {
"alternate-transport-changed": "",
"bill-posted": "",
"critical-parts-status-changed": "",
"intake-delivery-checklist-completed": "",
"job-added-to-production": "",
"job-assigned-to-me": "",
"job-status-change": "",
"new-media-added-reassigned": "",
"new-note-added": "",
"new-time-ticket-posted": "",
"part-marked-back-ordered": "",
"payment-collected-completed": "",
"schedule-dates-changed": "",
"supplement-imported": "",
"tasks-updated-created": ""
},
"tooltips": {
"job-watchers": ""
}
},
"owner": {
"labels": {
"noownerinfo": ""
@@ -3421,6 +3478,7 @@
"dashboard": "",
"dms": "",
"export-logs": "",
"feature-request": "",
"inventory": "",
"jobs": "",
"jobs-active": "",
@@ -3465,6 +3523,7 @@
"dashboard": "",
"dms": "",
"export-logs": "",
"feature-request": "",
"imexonline": "",
"inventory": "",
"jobs": "Tous les emplois | {{app}}",
@@ -3682,10 +3741,10 @@
"users": {
"errors": {
"signinerror": {
"auth/invalid-email": "",
"auth/user-disabled": "",
"auth/user-not-found": "",
"auth/wrong-password": "",
"auth/invalid-email": ""
"auth/wrong-password": ""
}
}
},
@@ -3785,60 +3844,6 @@
"validation": {
"unique_vendor_name": ""
}
},
"notifications": {
"labels": {
"notification-center": "",
"scenario": "",
"notificationscenarios": "",
"save": "",
"watching-issue": "",
"add-watchers": "",
"employee-search": "",
"teams-search": "",
"add-watchers-team": "",
"new-notification-title": "",
"show-unread-only": "",
"mark-all-read": "",
"notification-popup-title": "",
"ro-number": "",
"no-watchers": "",
"notification-settings-success": "",
"notification-settings-failure": "",
"watch": "",
"unwatch": ""
},
"actions": {
"remove": ""
},
"aria": {
"toggle": ""
},
"tooltips": {
"job-watchers": ""
},
"scenarios": {
"job-assigned-to-me": "",
"bill-posted": "",
"critical-parts-status-changed": "",
"part-marked-back-ordered": "",
"new-note-added": "",
"supplement-imported": "",
"schedule-dates-changed": "",
"tasks-updated-created": "",
"new-media-added-reassigned": "",
"new-time-ticket-posted": "",
"intake-delivery-checklist-completed": "",
"job-added-to-production": "",
"job-status-change": "",
"payment-collected-completed": "",
"alternate-transport-changed": ""
},
"channels": {
"app": "",
"email": "",
"fcm": ""
}
}
}
}

View File

@@ -14,10 +14,7 @@ const onServiceWorkerUpdate = (registration) => {
<Button
onClick={async () => {
window.open(
InstanceRenderManager({
imex: "https://imex-online.noticeable.news/",
rome: "https://rome-online.noticeable.news/"
}),
`https://shopmanagement.canny.io/changelog`,
"_blank"
);
}}

View File

@@ -15,7 +15,7 @@ const currentDatePST = new Date()
.reverse()
.join("-");
const sentryRelease =
`${import.meta.env.VITE_APP_IS_TEST ? "test" : "production"}-${currentDatePST}-${process.env.VITE_GIT_COMMIT_HASH}`.trim();
`${import.meta.env.VITE_APP_IS_TEST ? "test" : "production"}-${currentDatePST}`.trim();
if (!import.meta.env.DEV) {
Sentry.init({

View File

@@ -3595,6 +3595,7 @@
- actual_completion
- actual_delivery
- actual_in
- acv_amount
- adj_g_disc
- adj_strdis
- adj_towdis
@@ -3700,6 +3701,7 @@
- federal_tax_rate
- flat_rate_ats
- g_bett_amt
- hit_and_run
- id
- inproduction
- ins_addr1
@@ -3866,6 +3868,7 @@
- actual_completion
- actual_delivery
- actual_in
- acv_amount
- adj_g_disc
- adj_strdis
- adj_towdis
@@ -3972,6 +3975,7 @@
- federal_tax_rate
- flat_rate_ats
- g_bett_amt
- hit_and_run
- id
- inproduction
- ins_addr1
@@ -4150,6 +4154,7 @@
- actual_completion
- actual_delivery
- actual_in
- acv_amount
- adj_g_disc
- adj_strdis
- adj_towdis
@@ -4256,6 +4261,7 @@
- federal_tax_rate
- flat_rate_ats
- g_bett_amt
- hit_and_run
- id
- inproduction
- ins_addr1

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."jobs" add column "hit_and_run" boolean
-- null default 'false';

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "hit_and_run" boolean
null default 'false';

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."jobs" add column "acv_amount" numeric
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "acv_amount" numeric
null;

2929
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -52,6 +52,7 @@
"intuit-oauth": "^4.2.0",
"ioredis": "^5.6.0",
"json-2-csv": "^5.5.9",
"jsonwebtoken": "^9.0.2",
"juice": "^11.0.1",
"lodash": "^4.17.21",
"moment": "^2.30.1",

View File

@@ -117,6 +117,7 @@ const applyRoutes = ({ app }) => {
app.use("/cdk", require("./server/routes/cdkRoutes"));
app.use("/csi", require("./server/routes/csiRoutes"));
app.use("/payroll", require("./server/routes/payrollRoutes"));
app.use("/sso", require("./server/routes/ssoRoutes"));
app.use("/integrations", require("./server/routes/intergrationRoutes"));
// Default route for forbidden access

View File

@@ -107,18 +107,25 @@ const handleInvoiceBasedPayment = async (values, logger, logMeta, res) => {
});
// Create payment response record
const responseResults = await gqlClient.request(INSERT_PAYMENT_RESPONSE, {
paymentResponse: {
amount: values.total,
bodyshopid: bodyshop.id,
paymentid: paymentResult.id,
jobid: job.id,
declinereason: "Approved",
ext_paymentid: values.paymentid,
successful: true,
response: values
}
});
const responseResults = await gqlClient
.request(INSERT_PAYMENT_RESPONSE, {
paymentResponse: {
amount: values.total,
bodyshopid: bodyshop.id,
paymentid: paymentResult.insert_payments.returning[0].id,
jobid: job.id,
declinereason: "Approved",
ext_paymentid: values.paymentid,
successful: true,
response: values
}
})
.catch((err) => {
logger.log("intellipay-postback-invoice-response-error", "ERROR", "api", null, {
err,
...logMeta
});
});
logger.log("intellipay-postback-invoice-response-success", "DEBUG", "api", null, {
responseResults,

View File

@@ -37,7 +37,9 @@ beforeEach(() => {
]
})
.mockResolvedValueOnce({
id: "payment123"
insert_payments: {
returning: [{ id: "payment123" }]
}
})
.mockResolvedValueOnce({
insert_payment_response: {

View File

@@ -63,7 +63,9 @@ const scenarioParser = async (req, jobIdField) => {
}
if (!jobId) {
logger.log(`No jobId found using path "${jobIdField}", skipping notification parsing`, "info", "notifications");
if (process?.env?.NODE_ENV === "development") {
logger.log(`No jobId found using path "${jobIdField}", skipping notification parsing`, "info", "notifications");
}
return;
}
@@ -88,7 +90,9 @@ const scenarioParser = async (req, jobIdField) => {
// Exit early if no job watchers are found for this job
if (isEmpty(jobWatchers)) {
logger.log(`No watchers found for jobId "${jobId}", skipping notification parsing`, "info", "notifications");
if (process?.env?.NODE_ENV === "development") {
logger.log(`No watchers found for jobId "${jobId}", skipping notification parsing`, "info", "notifications");
}
return;
}
@@ -130,11 +134,13 @@ const scenarioParser = async (req, jobIdField) => {
// Exit early if no matching scenarios are identified
if (isEmpty(matchingScenarios)) {
logger.log(
`No matching scenarios found for jobId "${jobId}", skipping notification dispatch`,
"info",
"notifications"
);
if (process?.env?.NODE_ENV === "development") {
logger.log(
`No matching scenarios found for jobId "${jobId}", skipping notification dispatch`,
"info",
"notifications"
);
}
return;
}
@@ -157,11 +163,13 @@ const scenarioParser = async (req, jobIdField) => {
// Exit early if no notification associations are found
if (isEmpty(associationsData?.associations)) {
logger.log(
`No notification associations found for jobId "${jobId}", skipping notification dispatch`,
"info",
"notifications"
);
if (process?.env?.NODE_ENV === "development") {
logger.log(
`No notification associations found for jobId "${jobId}", skipping notification dispatch`,
"info",
"notifications"
);
}
return;
}
@@ -196,11 +204,13 @@ const scenarioParser = async (req, jobIdField) => {
// Exit early if no scenarios have eligible watchers after filtering
if (isEmpty(finalScenarioData?.matchingScenarios)) {
logger.log(
`No eligible watchers after filtering for jobId "${jobId}", skipping notification dispatch`,
"info",
"notifications"
);
if (process?.env?.NODE_ENV === "development") {
logger.log(
`No eligible watchers after filtering for jobId "${jobId}", skipping notification dispatch`,
"info",
"notifications"
);
}
return;
}
@@ -259,7 +269,9 @@ const scenarioParser = async (req, jobIdField) => {
}
if (isEmpty(scenariosToDispatch)) {
logger.log(`No scenarios to dispatch for jobId "${jobId}" after building`, "info", "notifications");
if (process?.env?.NODE_ENV === "development") {
logger.log(`No scenarios to dispatch for jobId "${jobId}" after building`, "info", "notifications");
}
return;
}

View File

@@ -0,0 +1,13 @@
const express = require("express");
const router = express.Router();
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
const { cannySsoHandler } = require("../sso/canny");
router.use(validateFirebaseIdTokenMiddleware);
router.post("/canny", withUserGraphQLClientMiddleware, cannySsoHandler);
module.exports = router;

24
server/sso/canny.js Normal file
View File

@@ -0,0 +1,24 @@
const logger = require("../utils/logger");
const jwt = require("jsonwebtoken");
const cannySsoHandler = async (req, res) => {
try {
const userData = {
//avatarURL: user.avatarURL, // optional, but preferred
email: req.user.email,
id: req.user.uid,
name: req.user.displayName || req.user.email
};
res.status(200).send(jwt.sign(userData, process.env.CANNY_PRIVATE_KEY, { algorithm: "HS256" }));
} catch (error) {
logger.log("sso-canny-error", "error", req?.user?.email, null, {
message: error.message,
stack: error.stack
});
res.status(500).json({ error: error.message });
}
};
module.exports = {
cannySsoHandler
};