Compare commits

...

59 Commits

Author SHA1 Message Date
Dave Richer
971a81fc27 feature/IO-2979-DST - Finish DST Stuff
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 14:04:14 -07:00
Dave Richer
e605433379 Merge remote-tracking branch 'origin/release/2024-11-01' into feature/IO-2979-DST 2024-10-28 13:10:08 -07:00
Dave Richer
b9ebb70b7a release/2024-11-01 - Remove Trace Log Level
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 13:06:47 -07:00
Dave Richer
79ed6f2388 release/2024-11-01 - Remove Trace Log Level
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 12:24:40 -07:00
Dave Richer
785449a986 release/2024-11-01 - Remove Trace Log Level
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 12:03:48 -07:00
Dave Richer
0b7d469e0e feature/IO-2979-DST - Finish DST
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 11:55:13 -07:00
Dave Richer
a57156756e feature/IO-2979-DST - Normalize usages of dayjs to dayjs not day, move locale hook
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 11:03:30 -07:00
Dave Richer
c4c30d98d4 release/2024-11-01 - Adjust hostname check
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 10:07:10 -07:00
Dave Richer
d4e8803b13 release/2024-11-01 - Adjust client body buffer size
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 09:47:27 -07:00
Dave Richer
1f2786ddec release/2024-11-01 - Fix some log things
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-28 09:31:37 -07:00
Dave Richer
e90cda07e4 Merged in release/2024-10-25 (pull request #1848)
Release/2024-10-25 - IO-2921, IO-2966, IO-2973, IO-2974, IO-2978, IO-2992, IO-2996, IO-2998
2024-10-26 04:48:05 +00:00
Dave Richer
9793daa04c release/2024-10-25 - Add The local email viewer to the reference folder.
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-25 11:58:50 -07:00
Allan Carr
117ced8fe7 Merged in feature/IO-2973-Created-By-Tasks (pull request #1846)
IO-2973 Created By Tasks

Approved-by: Dave Richer
2024-10-25 18:53:11 +00:00
Dave Richer
e7909205d1 feature/IO-2973-Created-By-Tasks - Merge in release, fix conflicts
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-25 11:50:11 -07:00
Dave Richer
18028a70ab feature/IO-2973-Created-By-Tasks - Merge in release, fix conflicts
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-25 11:22:59 -07:00
Dave Richer
eeb8d8d26f release/2024-10-25 - Clean up referenceDocuments
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-25 11:21:10 -07:00
Allan Carr
23659fc412 IO-2973 Created By Tasks
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-25 11:19:40 -07:00
Patrick Fic
ba65057782 Merged in feature/IO-2974-add-comment-to-schedule (pull request #1842)
IO-2974 add comment to schedule.

Approved-by: Dave Richer
2024-10-25 17:03:06 +00:00
Dave Richer
60a859cac8 Merged in feature/IO-2998-enhanced-api-logging (pull request #1843)
feature/IO-2998-enhanced-api-logging - Missing package
2024-10-25 15:38:46 +00:00
Dave Richer
0cfe26093c feature/IO-2998-enhanced-api-logging - Missing package
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-25 08:37:56 -07:00
Patrick Fic
d085a9c7c9 IO-2974 add comment to schedule. 2024-10-25 08:33:17 -07:00
Dave Richer
ec518a0593 Merged in feature/IO-2998-enhanced-api-logging (pull request #1840)
feature/IO-2998-enhanced-api-logging - Missing package
2024-10-25 15:30:26 +00:00
Dave Richer
1cd64ab6f1 feature/IO-2998-enhanced-api-logging - Missing package
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-25 08:29:51 -07:00
Dave Richer
26836f662a Merged in feature/IO-2998-enhanced-api-logging (pull request #1838)
feature/IO-2998-enhanced-api-logging - Finish
2024-10-25 15:12:24 +00:00
Dave Richer
111f280674 feature/IO-2998-enhanced-api-logging - Finish
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-25 08:11:33 -07:00
Dave Richer
6e88faa9d8 Merged in feature/IO-2998-enhanced-api-logging (pull request #1836)
Feature/IO-2998 into Release/2024-10-25
2024-10-25 14:11:26 +00:00
Dave Richer
1ca8b2a78d feature/IO-2998-enhanced-api-logging - Finish
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-24 14:36:35 -07:00
Dave Richer
cd2a7cad7f feature/IO-2998-enhanced-api-logging - Finish
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-24 14:21:35 -07:00
Dave Richer
ed16156957 Merge remote-tracking branch 'origin/release/2024-10-25' into feature/IO-2998-enhanced-api-logging 2024-10-24 13:18:21 -07:00
Patrick Fic
8dc1f7e08f IO-2998 remove graylog and additional erroneous console logs. 2024-10-24 12:18:39 -07:00
Patrick Fic
2d3c13c587 IO-2998 Add winston and replace logger.js 2024-10-24 11:50:54 -07:00
Allan Carr
5486907639 Merged in feature/IO-2978-Production-not-in-Production-Status (pull request #1834)
IO-2978 Production not in Production Status

Approved-by: Dave Richer
2024-10-24 16:49:59 +00:00
Allan Carr
9233cef23a IO-2978 Production not in Production Status
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-23 17:02:45 -07:00
Dave Richer
c16eafe892 Merged in feature/IO-2996-Package-Updates-Docker-Debugging (pull request #1832)
feature/IO-2996-Package-Updates-Docker-Debugging - Maintenance
2024-10-23 17:03:44 +00:00
Dave Richer
b479684fe4 feature/IO-2996-Package-Updates-Docker-Debugging - Maintenance
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-23 13:00:51 -04:00
Allan Carr
4201f61548 Merged in feature/IO-2966-Contract-Create-Page-Leave-Warning (pull request #1831)
IO-2966 Contract Create Page Leave Warning

Approved-by: Dave Richer
2024-10-23 14:58:22 +00:00
Allan Carr
d04fc76840 IO-2966 Contract Create Page Leave Warning
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-22 17:48:40 -07:00
Allan Carr
0f84adc752 IO-2966 Contract Create Page Leave Warning
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-22 17:35:54 -07:00
Allan Carr
fbefd80959 Merged in feature/IO-2921-CARSTAR-Canada-Chatter-Integration (pull request #1830)
IO-2921 CHATTER correct secertmanager call from undefined variable to string

Approved-by: Dave Richer
2024-10-22 15:08:11 +00:00
Allan Carr
6a691b54c8 Merged in feature/IO-2992-Bill-Line-UI-Table (pull request #1829)
IO-2992 Bill Line column Width with Word Breaks

Approved-by: Dave Richer
2024-10-22 15:07:43 +00:00
Allan Carr
fc75717d32 IO-2921 CHATTER correct secertmanager call from undefined variable to string
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-21 12:43:20 -07:00
Allan Carr
1459c6e993 Merge branch 'master-AIO' into feature/IO-2921-CARSTAR-Canada-Chatter-Integration
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-21 12:42:45 -07:00
Allan Carr
f50292f9bf IO-2992 Bill Line column Width with Word Breaks
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-21 10:53:32 -07:00
Dave Richer
5b81912bd3 Merged in release/2024-10-18 (pull request #1828)
Release/2024-10-18 into master-AIO - IO-2971, IO-2976, IO-2977, IO-2984, IO-2985, IO-2987, IO-2988, IO-2989
2024-10-19 03:47:08 +00:00
Dave Richer
3c98a94c38 Merged in feature/IO-2976-GlobalSearch-First-link-Navigate (pull request #1826)
feature/IO-2976-GlobalSearch-First-link-Navigate - Fix global search so it goes to the first url if it is available, added an ID for rome tours.
2024-10-18 19:25:09 +00:00
Dave Richer
1d98de6d4d feature/IO-2976-GlobalSearch-First-link-Navigate - Fix global search so it goes to the first url if it is available, added an ID for rome tours.
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-18 15:22:56 -04:00
Allan Carr
0ce5d9063a Merged in feature/IO-2989-jobexported-missing-translation (pull request #1823)
IO-2989 jobexported missing en_us translation

Approved-by: Dave Richer
2024-10-18 16:30:02 +00:00
Dave Richer
3b84e1d6ec Merged in feature/IO-2977-Replace-On-Board-with-In-View (pull request #1824)
feature/IO-2977-Replace-On-Board-with-In-View - Replace production board terminology for Imex, on board vs in view
2024-10-18 16:29:25 +00:00
Dave Richer
d62f6e2116 feature/IO-2977-Replace-On-Board-with-In-View - Replace production board terminology for Imex, on board vs in view
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-18 12:28:01 -04:00
Allan Carr
71a26cc4ac IO-2989 jobexported missing en_us translation
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-18 08:45:13 -07:00
Allan Carr
32441e9406 Merged in feature/IO-2988-Jobline-Upsert-Undefined (pull request #1821)
IO-2988 Jobline Upsert Undefined handling

Approved-by: Dave Richer
2024-10-18 01:55:31 +00:00
Allan Carr
e6dade1206 IO-2988 Jobline Upsert Undefined handling
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-17 17:03:40 -07:00
Allan Carr
43d34cae07 Merged in feature/IO-2987-Non-Production-Board-Status-Status-Change (pull request #1820)
IO-2987 Non Production Board Status - Status Changes

Approved-by: Dave Richer
2024-10-17 22:27:53 +00:00
Allan Carr
a72a7948fe IO-2987 Non Production Board Status - Status Changes
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-17 15:22:05 -07:00
Dave Richer
a24f6639a1 Merged in feature/IO-2985-Production-List-View-Null-Handling (pull request #1818)
IO-2985-Production-List-View-Null-Handling - Handle Null md_production_config
2024-10-17 17:09:48 +00:00
Allan Carr
b2a0af32e9 Merged in feature/IO-2984-Open-in-Explorer (pull request #1817)
IO-2984 Open in Explorer correction

Approved-by: Dave Richer
2024-10-17 17:09:36 +00:00
Allan Carr
cc58d14d32 Merged in feature/IO-2971-Export-Table-Size (pull request #1816)
IO-2971 Export Table Size limit to 10

Approved-by: Dave Richer
2024-10-17 17:03:27 +00:00
Allan Carr
5053816be7 IO-2984 Open in Explorer correction
encodeURL

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-16 16:38:59 -07:00
Allan Carr
30ca34ea93 IO-2971 Export Table Size limit to 10
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-16 13:43:25 -07:00
97 changed files with 6512 additions and 4523 deletions

0
.localstack/.gitkeep Normal file
View File

View File

@@ -1 +1,2 @@
client_max_body_size 50M;
client_max_body_size 50M;
client_body_buffer_size 5M;

15
.vscode/launch.json vendored
View File

@@ -14,6 +14,21 @@
"request": "launch",
"url": "http://localhost:3000",
"webRoot": "${workspaceRoot}/client/src"
},
{
"name": "Attach to Node.js in Docker",
"type": "node",
"request": "attach",
"address": "localhost",
"port": 9229,
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app",
"protocol": "inspector",
"restart": true,
"sourceMaps": true,
"skipFiles": [
"<node_internals>/**"
]
}
]
}

View File

@@ -41,7 +41,7 @@ RUN npm i --no-package-lock
COPY . .
# Expose the port your app runs on (adjust if necessary)
EXPOSE 4000
EXPOSE 4000 9229
# Start the application
CMD ["nodemon", "--legacy-watch", "server.js"]
CMD ["nodemon", "--legacy-watch", "--inspect=0.0.0.0:9229", "server.js"]

View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,7 @@
This will connect to your dockers local stack session and render the email in HTML.
```shell
node index.js
```
http://localhost:3334

View File

@@ -0,0 +1,116 @@
// index.js
import express from 'express';
import fetch from 'node-fetch';
import {simpleParser} from 'mailparser';
const app = express();
const PORT = 3334;
app.get('/', async (req, res) => {
try {
const response = await fetch('http://localhost:4566/_aws/ses');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
const messagesHtml = await parseMessages(data.messages);
res.send(renderHtml(messagesHtml));
} catch (error) {
console.error('Error fetching messages:', error);
res.status(500).send('Error fetching messages');
}
});
async function parseMessages(messages) {
const parsedMessages = await Promise.all(
messages.map(async (message, index) => {
try {
const parsed = await simpleParser(message.RawData);
return `
<div class="shadow-md rounded-lg p-4 mb-6" style="background-color: lightgray">
<div class="shadow-md rounded-lg p-4 mb-6" style="background-color: white">
<div class="mb-2">
<span class="font-bold text-lg">Message ${index + 1}</span>
</div>
<div class="mb-2">
<span class="font-semibold">From:</span> ${message.Source}
</div>
<div class="mb-2">
<span class="font-semibold">Region:</span> ${message.Region}
</div>
<div class="mb-2">
<span class="font-semibold">Timestamp:</span> ${message.Timestamp}
</div>
</div>
<div class="prose">
${parsed.html || parsed.textAsHtml || 'No HTML content available'}
</div>
</div>
`;
} catch (error) {
console.error('Error parsing email:', error);
return `
<div class="bg-white shadow-md rounded-lg p-4 mb-6">
<div class="mb-2">
<span class="font-bold text-lg">Message ${index + 1}</span>
</div>
<div class="mb-2">
<span class="font-semibold">From:</span> ${message.Source}
</div>
<div class="mb-2">
<span class="font-semibold">Region:</span> ${message.Region}
</div>
<div class="mb-2">
<span class="font-semibold">Timestamp:</span> ${message.Timestamp}
</div>
<div class="text-red-500">
Error parsing email content
</div>
</div>
`;
}
})
);
return parsedMessages.join('');
}
function renderHtml(messagesHtml) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Messages Viewer</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background-color: #f3f4f6;
font-family: Arial, sans-serif;
}
.container {
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.prose {
line-height: 1.6;
}
</style>
</head>
<body>
<div class="container bg-white shadow-lg rounded-lg p-6">
<h1 class="text-2xl font-bold text-center mb-6">Email Messages Viewer</h1>
<div id="messages-container">
${messagesHtml}
</div>
</div>
</body>
</html>
`;
}
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
{
"name": "localemailviewer",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"express": "^4.21.1",
"mailparser": "^3.7.1",
"node-fetch": "^3.3.2"
}
}

27
certs/id_rsa Normal file
View File

@@ -0,0 +1,27 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAvNl5fuVmLNv72BZNxnTqX5CHf5Xi8UxjYaYxHITSCx7blnhpVYLd
qXvcOWXzbsfjch/den73QiW4n2FYz75oGMhUGlOYzdWKA9I9Sj09Qy1R06RhwDiZGd5qaM
swEeXpkNmi2u4Qd2kJeDfUQUigjC09V81O/vrniGtQAJScfiG/itdm+Ufn09Z4MYk0HWjq
iDokNEskoEPsibYIrb+Q6vdtuPkZO+wU/smXhPtgw5ST6oQdmm/gVNsRg5XNzxrire+z1G
WatnnVL3hPnnfpnf8W589dyms7GGJwhPerSGTN1bn0T4+9C69Cd7LBJtxiuFdRmdlGLLLP
RR48Rur71wAAA9AEfVsdBH1bHQAAAAdzc2gtcnNhAAABAQC82Xl+5WYs2/vYFk3GdOpfkI
d/leLxTGNhpjEchNILHtuWeGlVgt2pe9w5ZfNux+NyH916fvdCJbifYVjPvmgYyFQaU5jN
1YoD0j1KPT1DLVHTpGHAOJkZ3mpoyzAR5emQ2aLa7hB3aQl4N9RBSKCMLT1XzU7++ueIa1
AAlJx+Ib+K12b5R+fT1ngxiTQdaOqIOiQ0SySgQ+yJtgitv5Dq9224+Rk77BT+yZeE+2DD
lJPqhB2ab+BU2xGDlc3PGuKt77PUZZq2edUveE+ed+md/xbnz13KazsYYnCE96tIZM3Vuf
RPj70Lr0J3ssEm3GK4V1GZ2UYsss9FHjxG6vvXAAAAAwEAAQAAAQAQTosSLQbMmtY9S3e9
yjyusdExcCTfhyQRu4MEHmfws+JsNMuLqbgwOVTD1AzYJQR7x0qdmDcLjCxL/uDnV16vvS
Sd/Vf1dhnryIyoS29tzI0DRG94ZKq7tBvmHp1w/jRT4KcSVnovhW9e5Rs74+SRFhr06PKI
S+wQOIv48Nwue9+QUMsMCpWgKXHx7SHNTHvnAfqdhi9O29SWlMA+v+mELZ5Cl+HU0UTt2I
A1BxOe1N8FjN7KE2viJexsl3is1PuqMkpLl/wyHBJTVzUadl6DRALJQIm7/YO5goE72YOV
Lpo27do3zjhC87dlKdATvZUzfKV0LuUVdxq/PNDZMUbBAAAAgQDShAqDZiDrdTUaGXfUVm
QzcnVNbh2/KgZh4uux9QNHST562W6cnN7qxoRwVrM4BCOk1Kl73QQZW4nDvXX3PVC5j038
8AXkcBHS9j9f4h72ue7D2jqlbHFa7aGU9zYgk9mbBF+GX3tDntkAIQjLtwOLfj1iiJ/clX
mHFUAY1V4L8AAAAIEA3E4t/v0yU5D9AOI0r17UNYqfeyDoKAEDR4QbbFjO1l0kLnEJy7Zx
Mhj18GilYg2y0P0v8dSM/oWXS8Hua2t5i9Exlv6gHhGlQ80mwYcVGIxewZ/pPeCPw0U+kt
EKUjt09m9Oe7+6xHQsTBj9hY8/vqPmQwRalZFcLdhHiDiVKTcAAACBANtykaPXdVzEFx7D
UOlsjVL7zM0EVOFXf9JJQ6BhazhmsEI2PYt3IpgGMo8cXkoUofAOIYjf421AabN1BqSO5J
XTMxM0ZV3JmLLi804Mu9h1iFrVTBdLYOMJdc2VCo1EwHWpo9SXOyjxce/znvcIOU04aZhu
TaPg816X+E+gw5JhAAAAFGRhdmVARGF2ZVJpY2hlci1JTUVYAQIDBAUG
-----END OPENSSH PRIVATE KEY-----

1
certs/id_rsa.pub Normal file
View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC82Xl+5WYs2/vYFk3GdOpfkId/leLxTGNhpjEchNILHtuWeGlVgt2pe9w5ZfNux+NyH916fvdCJbifYVjPvmgYyFQaU5jN1YoD0j1KPT1DLVHTpGHAOJkZ3mpoyzAR5emQ2aLa7hB3aQl4N9RBSKCMLT1XzU7++ueIa1AAlJx+Ib+K12b5R+fT1ngxiTQdaOqIOiQ0SySgQ+yJtgitv5Dq9224+Rk77BT+yZeE+2DDlJPqhB2ab+BU2xGDlc3PGuKt77PUZZq2edUveE+ed+md/xbnz13KazsYYnCE96tIZM3VufRPj70Lr0J3ssEm3GK4V1GZ2UYsss9FHjxG6vvX dave@DaveRicher-IMEX

View File

@@ -2,8 +2,6 @@ import { ApolloProvider } from "@apollo/client";
import { SplitFactoryProvider, SplitSdk } from "@splitsoftware/splitio-react";
import { ConfigProvider } from "antd";
import enLocale from "antd/es/locale/en_US";
import dayjs from "../utils/day";
import "dayjs/locale/en";
import React from "react";
import { useTranslation } from "react-i18next";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
@@ -19,8 +17,6 @@ if (import.meta.env.DEV) {
Userpilot.initialize("NX-69145f08");
}
dayjs.locale("en");
const config = {
core: {
authorizationKey: import.meta.env.VITE_APP_SPLIT_API,

View File

@@ -9,7 +9,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { pageLimit } from "../../utils/config";
import { exportPageLimit } from "../../utils/config";
import { alphaSort, dateSort } from "../../utils/sorters";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component";
@@ -175,7 +175,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
<Table
loading={loading}
dataSource={dataSource}
pagination={{ position: "top", pageSize: pageLimit }}
pagination={{ position: "top", pageSize: exportPageLimit }}
columns={columns}
rowKey="id"
onChange={handleTableChange}

View File

@@ -8,7 +8,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { pageLimit } from "../../utils/config";
import { exportPageLimit } from "../../utils/config";
import { alphaSort, dateSort } from "../../utils/sorters";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
@@ -177,7 +177,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments,
<Table
loading={loading}
dataSource={dataSource}
pagination={{ position: "top", pageSize: pageLimit }}
pagination={{ position: "top", pageSize: exportPageLimit }}
columns={columns}
rowKey="id"
onChange={handleTableChange}

View File

@@ -1,18 +1,18 @@
import { Button, Card, Input, Space, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { exportPageLimit } from "../../utils/config";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateFormatter } from "../../utils/DateFormatter";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
import JobMarkSelectedExported from "../jobs-mark-selected-exported/jobs-mark-selected-exported";
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
@@ -201,7 +201,7 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
<Table
loading={loading}
dataSource={dataSource}
pagination={{ position: "top" }}
pagination={{ position: "top", pageSize: exportPageLimit }}
columns={columns}
rowKey="id"
onChange={handleTableChange}

View File

@@ -7,10 +7,10 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CiecaSelect from "../../utils/Ciecaselect";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
import BilllineAddInventory from "../billline-add-inventory/billline-add-inventory.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -72,7 +72,14 @@ export function BillEnterModalLinesComponent({
<BillLineSearchSelect
disabled={disabled}
options={lineData}
style={{ width: "100%", minWidth: "10rem" }}
style={{
width: "20rem",
maxWidth: "20rem",
minWidth: "10rem",
whiteSpace: "normal",
height: "auto",
minHeight: "32px" // default height of Ant Design inputs
}}
allowRemoved={form.getFieldValue("is_credit_memo") || false}
onSelect={(value, opt) => {
setFieldsValue({
@@ -105,7 +112,7 @@ export function BillEnterModalLinesComponent({
title: t("billlines.fields.line_desc"),
dataIndex: "line_desc",
editable: true,
width: "20rem",
formItemProps: (field) => {
return {
key: `${field.index}line_desc`,
@@ -119,7 +126,7 @@ export function BillEnterModalLinesComponent({
]
};
},
formInput: (record, index) => <Input disabled={disabled} />
formInput: (record, index) => <Input.TextArea disabled={disabled} autoSize />
},
{
title: t("billlines.fields.quantity"),

View File

@@ -11,7 +11,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
disabled={disabled}
ref={ref}
showSearch
popupMatchSelectWidth={false}
popupMatchSelectWidth={true}
optionLabelProp={"name"}
// optionFilterProp="line_desc"
filterOption={(inputValue, option) => {
@@ -43,7 +43,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
item.oem_partno ? ` - ${item.oem_partno}` : ""
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim(),
label: (
<>
<div style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
<span>
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
@@ -57,7 +57,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
<span style={{ float: "right", paddingleft: "1rem" }}>
{item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``}
</span>
</>
</div>
)
}))
]}

View File

@@ -1,15 +1,17 @@
import { WarningFilled } from "@ant-design/icons";
import { Form, Input, InputNumber, Space } from "antd";
import dayjs from "../../utils/day";
import React from "react";
import { useTranslation } from "react-i18next";
import { DateFormatter } from "../../utils/DateFormatter";
import dayjs from "../../utils/day";
//import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component";
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
import ContractsRatesChangeButton from "../contracts-rates-change-button/contracts-rates-change-button.component";
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import {
default as DateTimePicker,
default as FormDateTimePicker
} from "../form-date-time-picker/form-date-time-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import InputPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
@@ -18,10 +20,10 @@ import ContractFormJobPrefill from "./contract-form-job-prefill.component";
export default function ContractFormComponent({ form, create = false, selectedJobState, selectedCar }) {
const { t } = useTranslation();
return (
<div>
<FormFieldsChanged form={form} />
<>
{!create && <FormFieldsChanged form={form} />}
<LayoutFormRow>
{create ? null : (
{!create && (
<Form.Item
label={t("contracts.fields.status")}
name="status"
@@ -50,7 +52,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo
<Form.Item label={t("contracts.fields.scheduledreturn")} name="scheduledreturn">
<FormDateTimePicker />
</Form.Item>
{create ? null : (
{!create && (
<Form.Item label={t("contracts.fields.actualreturn")} name="actualreturn">
<FormDateTimePicker />
</Form.Item>
@@ -122,7 +124,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo
}}
</Form.Item>
)}
{create ? null : (
{!create && (
<Form.Item label={t("contracts.fields.kmend")} name="kmend">
<InputNumber />
</Form.Item>
@@ -145,25 +147,21 @@ export default function ContractFormComponent({ form, create = false, selectedJo
>
<CourtesyCarFuelSlider />
</Form.Item>
{create ? null : (
{!create && (
<Form.Item label={t("contracts.fields.fuelin")} name="fuelin" span={8}>
<CourtesyCarFuelSlider />
</Form.Item>
)}
</LayoutFormRow>
<div>
<Space wrap>
{selectedJobState && (
<div>
<ContractFormJobPrefill jobId={selectedJobState && selectedJobState[0]} form={form} />
</div>
)}
{
//<ContractLicenseDecodeButton form={form} />
}
</Space>
</div>
<LayoutFormRow header={t("contracts.labels.driverinformation")}>
<Space wrap>
{create && selectedJobState && (
<ContractFormJobPrefill jobId={selectedJobState && selectedJobState[0]} form={form} />
)}
{/* {<ContractLicenseDecodeButton form={form} />} */}
</Space>
</LayoutFormRow>
<LayoutFormRow noDivider={true}>
<Form.Item
label={t("contracts.fields.driver_dlnumber")}
name="driver_dlnumber"
@@ -183,9 +181,8 @@ export default function ContractFormComponent({ form, create = false, selectedJo
const dlExpiresBeforeReturn = dayjs(form.getFieldValue("driver_dlexpiry")).isBefore(
dayjs(form.getFieldValue("scheduledreturn"))
);
return (
<div>
<>
<Form.Item
label={t("contracts.fields.driver_dlexpiry")}
name="driver_dlexpiry"
@@ -204,11 +201,10 @@ export default function ContractFormComponent({ form, create = false, selectedJo
<span>{t("contracts.labels.dlexpirebeforereturn")}</span>
</Space>
)}
</div>
</>
);
}}
</Form.Item>
<Form.Item label={t("contracts.fields.driver_dlst")} name="driver_dlst">
<Input />
</Form.Item>
@@ -315,6 +311,6 @@ export default function ContractFormComponent({ form, create = false, selectedJo
<InputNumber precision={2} />
</Form.Item>
</LayoutFormRow>
</div>
</>
);
}

View File

@@ -40,8 +40,6 @@ export function DmsLogEvents({ socket, logs, bodyshop }) {
function LogLevelHierarchy(level) {
switch (level) {
case "TRACE":
return "pink";
case "DEBUG":
return "orange";
case "INFO":

View File

@@ -8,7 +8,7 @@ import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries";
import { useMutation } from "@apollo/client";
import { acceptEula } from "../../redux/user/user.actions";
import { useTranslation } from "react-i18next";
import day from "../../utils/day";
import dayjs from "../../utils/day";
import "./eula.styles.scss";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
@@ -208,7 +208,7 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
{
required: true,
validator: (_, value) => {
if (day(value).isSame(day(), "day")) {
if (dayjs(value).isSame(dayjs(), "day")) {
return Promise.resolve();
}
return Promise.reject(new Error(t("eula.messages.date_accepted")));

View File

@@ -4,19 +4,37 @@ import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import dayjs from "../../utils/day";
import { fuzzyMatchDate } from "./formats.js";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
import { connect } from "react-redux";
const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, onlyToday, isDateOnly = false, ...restProps }) => {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const DateTimePicker = ({
value,
onChange,
onBlur,
id,
onlyFuture,
onlyToday,
isDateOnly = false,
bodyshop,
...restProps
}) => {
const [isManualInput, setIsManualInput] = useState(false);
const { t } = useTranslation();
const handleChange = useCallback(
(newDate) => {
if (!newDate) return;
if (onChange) {
onChange(newDate || null);
onChange(bodyshop?.timezone ? dayjs(newDate).tz(bodyshop.timezone, true) : newDate);
}
setIsManualInput(false);
},
[onChange]
[onChange, bodyshop?.timezone]
);
const handleBlur = useCallback(
@@ -102,4 +120,4 @@ DateTimePicker.propTypes = {
isDateOnly: PropTypes.bool
};
export default React.memo(DateTimePicker);
export default connect(mapStateToProps, null)(DateTimePicker);

View File

@@ -3,13 +3,15 @@ import axios from "axios";
import _ from "lodash";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
export default function GlobalSearchOs() {
const { t } = useTranslation();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [data, setData] = useState(false);
@@ -177,7 +179,18 @@ export default function GlobalSearchOs() {
};
return (
<AutoComplete options={data} onSearch={handleSearch} defaultActiveFirstOption onClear={() => setData([])}>
<AutoComplete
options={data}
onSearch={handleSearch}
onKeyDown={(e) => {
if (e.key !== "Enter") return;
const firstUrlForSearch = data?.[0]?.options?.[0]?.label?.props?.to;
if (!firstUrlForSearch) return;
navigate(firstUrlForSearch);
}}
defaultActiveFirstOption
onClear={() => setData([])}
>
<Input.Search
size="large"
placeholder={t("general.labels.globalsearch")}

View File

@@ -3,7 +3,7 @@ import { AutoComplete, Divider, Input, Space } from "antd";
import _ from "lodash";
import React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import { GLOBAL_SEARCH_QUERY } from "../../graphql/search.queries";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import AlertComponent from "../alert/alert.component";
@@ -13,6 +13,7 @@ import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.compon
export default function GlobalSearch() {
const { t } = useTranslation();
const [callSearch, { loading, error, data }] = useLazyQuery(GLOBAL_SEARCH_QUERY);
const navigate = useNavigate();
const executeSearch = (v) => {
if (v && v.variables.search && v.variables.search !== "" && v.variables.search.length >= 3) callSearch(v);
@@ -20,7 +21,6 @@ export default function GlobalSearch() {
const debouncedExecuteSearch = _.debounce(executeSearch, 750);
const handleSearch = (value) => {
console.log("Handle Search");
debouncedExecuteSearch({ variables: { search: value } });
};
@@ -156,7 +156,17 @@ export default function GlobalSearch() {
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<AutoComplete options={options} onSearch={handleSearch} defaultActiveFirstOption>
<AutoComplete
options={options}
onSearch={handleSearch}
defaultActiveFirstOption
onKeyDown={(e) => {
if (e.key !== "Enter") return;
const firstUrlForSearch = options?.[0]?.options?.[0]?.label?.props?.to;
if (!firstUrlForSearch) return;
navigate(firstUrlForSearch);
}}
>
<Input.Search
size="large"
placeholder={t("general.labels.globalsearch")}

View File

@@ -23,6 +23,7 @@ import ScheduleEventColor from "./schedule-event.color.component";
import ScheduleEventNote from "./schedule-event.note.component";
import { useMutation } from "@apollo/client";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -127,6 +128,9 @@ export function ScheduleEventComponent({
{(event.job && event.job.alt_transport) || ""}
<ScheduleAtChange job={event && event.job} />
</DataLabel>
<DataLabel label={t("jobs.fields.comment")} valueStyle={{ overflow: "hidden", textOverflow: "ellipsis" }}>
<ProductionListColumnComment record={event && event.job} />
</DataLabel>
<ScheduleEventNote event={event} />
</div>
) : (
@@ -316,6 +320,7 @@ export function ScheduleEventComponent({
})`}
{event.job && event.job.alt_transport && <div style={{ margin: ".1rem" }}>{event.job.alt_transport}</div>}
{event?.job?.comment && `C: ${event.job.comment}`}
</Space>
) : (
<div

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from "react";
import day from "../../utils/day";
import dayjs from "../../utils/day";
import axios from "axios";
import { Badge, Card, Space, Table, Tag } from "antd";
import { gql, useQuery } from "@apollo/client";
@@ -72,7 +72,7 @@ export function JobLifecycleComponent({ job, statuses, ...rest }) {
dataIndex: "start",
key: "start",
render: (text) => DateTimeFormatterFunction(text),
sorter: (a, b) => day(a.start).unix() - day(b.start).unix()
sorter: (a, b) => dayjs(a.start).unix() - dayjs(b.start).unix()
},
{
title: t("job_lifecycle.columns.relative_start"),
@@ -90,7 +90,7 @@ export function JobLifecycleComponent({ job, statuses, ...rest }) {
}
return isEmpty(a.end) ? 1 : -1;
}
return day(a.end).unix() - day(b.end).unix();
return dayjs(a.end).unix() - dayjs(b.end).unix();
},
render: (text) => (isEmpty(text) ? t("job_lifecycle.content.not_available") : DateTimeFormatterFunction(text))
},

View File

@@ -2,7 +2,7 @@ import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { Button, Form, notification, Popover, Select, Space } from "antd";
import day from "../../utils/day";
import dayjs from "../../utils/day";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -48,7 +48,7 @@ export function JobLineDispatchButton({
const result = await dispatchLines({
variables: {
partsDispatch: {
dispatched_at: day(),
dispatched_at: dayjs(),
employeeid: values.employeeid,
jobid: job.id,
dispatched_by: currentUser.email,
@@ -138,7 +138,11 @@ export function JobLineDispatchButton({
return (
<Popover open={visible} content={popMenu}>
<Button disabled={selectedLines.length === 0 || jobRO || disabled} loading={loading} onClick={() => setVisible(true)}>
<Button
disabled={selectedLines.length === 0 || jobRO || disabled}
loading={loading}
onClick={() => setVisible(true)}
>
{t("joblines.actions.dispatchparts", { count: selectedLines.length })}
</Button>
</Popover>

View File

@@ -1,5 +1,8 @@
import { useMutation } from "@apollo/client";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { notification } from "antd";
import Axios from "axios";
import Dinero from "dinero.js";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -7,13 +10,10 @@ import { createStructuredSelector } from "reselect";
import { INSERT_NEW_JOB_LINE, UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectJobLineEditModal } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import UndefinedToNull from "../../utils/undefinedtonull";
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
import Axios from "axios";
import Dinero from "dinero.js";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
jobLineEditModal: selectJobLineEditModal,
@@ -82,13 +82,15 @@ function JobLinesUpsertModalContainer({ jobLineEditModal, toggleModalVisible, bo
variables: {
lineId: jobLineEditModal.context.id,
line: {
...values,
prt_dsmk_m: Dinero({
amount: Math.round(values.act_price * 100)
...UndefinedToNull({
...values,
prt_dsmk_m: Dinero({
amount: Math.round(values.act_price * 100)
})
.percentage(Math.abs(values.prt_dsmk_p || 0))
.multiply(values.prt_dsmk_p >= 0 ? 1 : -1)
.toFormat(0.0)
})
.percentage(Math.abs(values.prt_dsmk_p || 0))
.multiply(values.prt_dsmk_p >= 0 ? 1 : -1)
.toFormat(0.0)
}
},
refetchQueries: ["GET_LINE_TICKET_BY_PK"]

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Col, notification, Row, Table } from "antd";
import day from "../../utils/day";
import dayjs from "../../utils/day";
import React from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_PARTS_DISPATCH_LINE } from "../../graphql/parts-dispatch.queries";
@@ -11,7 +11,7 @@ export default function PartsDispatchExpander({ dispatch, job }) {
const [updateDispatchLine] = useMutation(UPDATE_PARTS_DISPATCH_LINE);
const handleAccept = async ({ partsDispatchLineId }) => {
const accepted_at = day();
const accepted_at = dayjs();
const result = await updateDispatchLine({
variables: { id: partsDispatchLineId, line: { accepted_at } },
optimisticResponse: {

View File

@@ -242,7 +242,8 @@ export function PartsOrderListTableComponent({
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
render: (text, record) => recordActions(record, true)
render: (text, record) => recordActions(record, true),
id: "parts-order-list-table-actions"
}
];

View File

@@ -1,12 +1,14 @@
import { SyncOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import Board from "./trello-board/index";
import { Button, notification, Skeleton, Space } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import { useApolloClient } from "@apollo/client";
import { Button, notification, Skeleton, Space } from "antd";
import cloneDeep from "lodash/cloneDeep";
import isEqual from "lodash/isEqual";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import NoteUpsertModal from "../../components/note-upsert-modal/note-upsert-modal.container";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
@@ -15,14 +17,13 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component";
import ProductionListPrint from "../production-list-table/production-list-print.component.jsx";
import CardColorLegend from "./production-board-kanban-card-color-legend.component.jsx";
import "./production-board-kanban.styles.scss";
import { createBoardData } from "./production-board-kanban.utils.js";
import ProductionBoardKanbanSettings from "./settings/production-board-kanban.settings.component.jsx";
import cloneDeep from "lodash/cloneDeep";
import isEqual from "lodash/isEqual";
import { defaultFilters, mergeWithDefaults } from "./settings/defaultKanbanSettings.js";
import NoteUpsertModal from "../../components/note-upsert-modal/note-upsert-modal.container";
import ProductionBoardKanbanSettings from "./settings/production-board-kanban.settings.component.jsx";
import Board from "./trello-board/index";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -214,6 +215,7 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr
bodyshop={bodyshop}
data={data}
/>
<ProductionListPrint />
</Space>
}
/>

View File

@@ -1,15 +1,66 @@
import InstanceRenderManager from "../../../utils/instanceRenderMgr.js";
const statisticsItems = [
{ id: 0, name: "totalHrs", label: "total_hours_in_production" },
{ id: 1, name: "totalAmountInProduction", label: "total_amount_in_production" },
{ id: 2, name: "totalLAB", label: "total_lab_in_production" },
{ id: 3, name: "totalLAR", label: "total_lar_in_production" },
{ id: 4, name: "jobsInProduction", label: "jobs_in_production" },
{ id: 5, name: "totalHrsOnBoard", label: "total_hours_on_board" },
{ id: 6, name: "totalAmountOnBoard", label: "total_amount_on_board" },
{ id: 7, name: "totalLABOnBoard", label: "total_lab_on_board" },
{ id: 8, name: "totalLAROnBoard", label: "total_lar_on_board" },
{ id: 9, name: "jobsOnBoard", label: "total_jobs_on_board" },
{ id: 10, name: "tasksOnBoard", label: "tasks_on_board" },
{
id: 5,
name: "totalHrsOnBoard",
label: InstanceRenderManager({
imex: "total_hours_in_view",
rome: "total_hours_on_board",
promanager: "total_hours_on_board"
})
},
{
id: 6,
name: "totalAmountOnBoard",
label: InstanceRenderManager({
imex: "total_amount_in_view",
rome: "total_amount_on_board",
promanager: "total_amount_on_board"
})
},
{
id: 7,
name: "totalLABOnBoard",
label: InstanceRenderManager({
imex: "total_lab_in_view",
rome: "total_lab_on_board",
promanager: "total_lab_on_board"
})
},
{
id: 8,
name: "totalLAROnBoard",
label: InstanceRenderManager({
imex: "total_lar_in_view",
rome: "total_lar_on_board",
promanager: "total_lar_on_board"
})
},
{
id: 9,
name: "jobsOnBoard",
label: InstanceRenderManager({
imex: "total_jobs_in_view",
rome: "total_jobs_on_board",
promanager: "total_jobs_on_board"
})
},
{
id: 10,
name: "tasksOnBoard",
label: InstanceRenderManager({
imex: "tasks_in_view",
rome: "tasks_on_board",
promanager: "tasks_on_board"
})
},
{ id: 11, name: "tasksInProduction", label: "tasks_in_production" }
];

View File

@@ -21,25 +21,26 @@ export function ProductionListColumnStatus({ record, bodyshop, insertAuditTrail
const [loading, setLoading] = useState(false);
const handleSetStatus = async (e) => {
logImEXEvent("production_change_status");
// e.stopPropagation();
setLoading(true);
const { key } = e;
await updateJob({
variables: {
jobId: record.id,
job: {
status: key
if (bodyshop.md_ro_statuses.production_statuses.includes(record.status) && !bodyshop.md_ro_statuses.post_production_statuses.includes(record.status)) {
logImEXEvent("production_change_status");
// e.stopPropagation();
setLoading(true);
const { key } = e;
await updateJob({
variables: {
jobId: record.id,
job: {
status: key
}
}
}
});
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.jobstatuschange(key),
type: "jobstatuschange"
});
setLoading(false);
});
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.jobstatuschange(key),
type: "jobstatuschange"
});
setLoading(false);
}
};
const menu = {

View File

@@ -77,7 +77,7 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici
const matchingColumnConfig = useMemo(() => {
return bodyshop?.production_config?.find((p) => p.name === defaultView);
}, [bodyshop.production_config]);
}, [bodyshop.production_config, defaultView]);
useEffect(() => {
const newColumns =

View File

@@ -120,14 +120,6 @@ var formats = {
};
const localizer = (dayjsLib) => {
// load dayjs plugins
dayjsLib.extend(isBetween);
dayjsLib.extend(isSameOrAfter);
dayjsLib.extend(isSameOrBefore);
dayjsLib.extend(localeData);
dayjsLib.extend(localizedFormat);
dayjsLib.extend(minMax);
dayjsLib.extend(utc);
var locale = function locale(dj, c) {
return c ? dj.locale(c) : dj;
};
@@ -136,7 +128,8 @@ const localizer = (dayjsLib) => {
// then use the timezone aware version
//TODO This was the issue entirely...
// var dayjs = dayjsLib.tz ? dayjsLib.tz : dayjsLib;
// var dayjs = dayjsLib.tz ? dayjsLib.tz : dayjsLib;
var dayjs = dayjsLib;
function getTimezoneOffset(date) {

View File

@@ -20,6 +20,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
problemJobs: selectProblemJobs
});
const localizer = local(dayjs);
export function ScheduleCalendarWrapperComponent({

View File

@@ -1,10 +1,3 @@
import { Button, Card, Space, Switch, Table } from "antd";
import queryString from "query-string";
import React, { useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { pageLimit } from "../../utils/config";
import dayjs from "../../utils/day";
import {
CheckCircleFilled,
CheckCircleOutlined,
@@ -15,9 +8,16 @@ import {
PlusCircleFilled,
SyncOutlined
} from "@ant-design/icons";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter.jsx";
import { Button, Card, Space, Switch, Table } from "antd";
import queryString from "query-string";
import React, { useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { setModalContext } from "../../redux/modals/modals.actions";
import { pageLimit } from "../../utils/config";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter.jsx";
import dayjs from "../../utils/day";
/**
* Task List Component
@@ -140,6 +140,17 @@ function TaskListComponent({
render: (text, record) => <DateTimeFormatter>{record.created_at}</DateTimeFormatter>
});
columns.push({
title: t("tasks.fields.created_by"),
dataIndex: "created_by",
key: "created_by",
width: "10%",
defaultSortOrder: "descend",
sorter: true,
sortOrder: sortcolumn === "created_by" && sortorder,
render: (text, record) => record.created_by
});
if (!onlyMine) {
columns.push({
title: t("tasks.fields.assigned_to"),

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import day from "../../utils/day";
import dayjs from "../../utils/day";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -29,7 +29,7 @@ export function TimeTicketsCommit({ bodyshop, currentUser, timeticket, disabled,
? { commited_by: null, committed_at: null }
: {
commited_by: currentUser.email,
committed_at: day()
committed_at: dayjs()
};
const result = await updateTimeTicket({

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import day from "../../utils/day";
import dayjs from "../../utils/day";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -34,7 +34,7 @@ export function TimeTicketsCommit({
timeticketIds: timetickets.map((ticket) => ticket.id),
timeticket: {
commited_by: currentUser.email,
committed_at: day()
committed_at: dayjs()
}
},
update(cache) {
@@ -47,7 +47,7 @@ export function TimeTicketsCommit({
return {
...ticket,
commited_by: currentUser.email,
committed_at: day()
committed_at: dayjs()
};
}
return ticket;

View File

@@ -1,7 +1,7 @@
import { useApolloClient } from "@apollo/client";
import { Button, notification } from "antd";
import _ from "lodash";
import day from "../../utils/day";
import dayjs from "../../utils/day";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -49,7 +49,7 @@ export function TtApproveButton({
})),
approvalIds: selectedTickets,
approvalUpdate: {
approved_at: day(),
approved_at: dayjs(),
approved_by: currentUser.email
}
}

View File

@@ -48,6 +48,7 @@ export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
v_model_desc
est_ct_fn
est_ct_ln
comment
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
aggregate {
sum {

View File

@@ -123,7 +123,6 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
socket.emit("set-log-level", value);
}}
>
<Select.Option key="TRACE">TRACE</Select.Option>
<Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARNING">WARNING</Select.Option>

View File

@@ -173,7 +173,6 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
socket.emit("set-log-level", value);
}}
>
<Select.Option key="TRACE">TRACE</Select.Option>
<Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARNING">WARNING</Select.Option>

View File

@@ -28,7 +28,7 @@ import {
} from "../../firebase/firebase.utils";
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
import client from "../../utils/GraphQLClient";
import day from "../../utils/day";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import {
checkInstanceId,
@@ -96,7 +96,7 @@ export function* isUserAuthenticated() {
const eulaQuery = yield client.query({
query: QUERY_EULA,
variables: {
now: day()
now: dayjs()
}
});
@@ -314,8 +314,7 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
try {
const userEmail = yield select((state) => state.user.currentUser.email);
try {
console.log("Setting shop timezone.");
day.tz.setDefault(payload.timezone);
dayjs.tz.setDefault(payload.timezone);
} catch (error) {
console.log(error);
}

View File

@@ -119,7 +119,7 @@
"jobclosedwithbypass": "Job was invoiced using the master bypass password. ",
"jobconverted": "Job converted and assigned number {{ro_number}}.",
"jobdelivery": "Job intake completed. Status set to {{status}}. Actual completion is {{actual_completion}}.",
"jobexported": "",
"jobexported": "Job has been exported",
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
"jobimported": "Job imported.",
"jobinproductionchange": "Job production status set to {{inproduction}}",
@@ -2849,15 +2849,21 @@
"jobs_in_production": "Jobs in Production",
"tasks_in_production": "Tasks in Production",
"tasks_on_board": "Tasks on Board",
"tasks_in_view": "Tasks in View",
"total_amount_in_production": "Dollars in Production",
"total_amount_on_board": "Dollars on Board",
"total_amount_in_view": "Dollars in View",
"total_hours_in_production": "Hours in Production",
"total_hours_on_board": "Hours on Board",
"total_hours_in_view": "Hours in View",
"total_jobs_on_board": "Jobs on Board",
"total_jobs_in_view": "Jobs in View",
"total_lab_in_production": "Body Hours in Production",
"total_lab_on_board": "Body Hours on Board",
"total_lab_in_view": "Body Hours in View",
"total_lar_in_production": "Refinish Hours in Production",
"total_lar_on_board": "Refinish Hours on Board"
"total_lar_on_board": "Refinish Hours on Board",
"total_lar_in_view": "Refinish Hours in View"
},
"statistics_title": "Statistics"
},
@@ -2869,15 +2875,21 @@
"tasks": "Tasks",
"tasks_in_production": "Tasks in Production",
"tasks_on_board": "Tasks on Board",
"tasks_in_view": "Tasks in View",
"total_amount_in_production": "Dollars in Production",
"total_amount_on_board": "Dollars on Board",
"total_amount_in_view": "Dollars in View",
"total_hours_in_production": "Hours in Production",
"total_hours_on_board": "Hours on Board",
"total_hours_in_view": "Hours in View",
"total_jobs_on_board": "Jobs on Board",
"total_jobs_in_view": "Jobs in View",
"total_lab_in_production": "Body Hours in Production",
"total_lab_on_board": "Body Hours on Board",
"total_lab_in_view": "Body Hours in View",
"total_lar_in_production": "Refinish Hours in Production",
"total_lar_on_board": "Refinish Hours on Board"
"total_lar_on_board": "Refinish Hours on Board",
"total_lar_in_view": "Refinish Hours in View"
},
"successes": {
"removed": "Job removed from production."
@@ -3032,6 +3044,7 @@
"production_by_target_date": "Production by Target Date",
"production_by_technician": "Production by Technician",
"production_by_technician_one": "Production filtered by Technician",
"production_not_production_status": "Production not in Production Status",
"production_over_time": "Production Level over Time",
"psr_by_make": "Percent of Sales by Vehicle Make",
"purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)",
@@ -3174,6 +3187,7 @@
"billid": "Bill",
"completed": "Completed",
"created_at": "Created At",
"created_by": "Created By",
"description": "Description",
"due_date": "Due Date",
"job": {

View File

@@ -2849,15 +2849,21 @@
"jobs_in_production": "",
"tasks_in_production": "",
"tasks_on_board": "",
"tasks_in_view": "",
"total_amount_in_production": "",
"total_amount_on_board": "",
"total_amount_in_view": "",
"total_hours_in_production": "",
"total_hours_on_board": "",
"total_hours_in_view": "",
"total_jobs_on_board": "",
"total_jobs_in_view": "",
"total_lab_in_production": "",
"total_lab_on_board": "",
"total_lab_in_view": "",
"total_lar_in_production": "",
"total_lar_on_board": ""
"total_lar_on_board": "",
"total_lar_in_view": ""
},
"statistics_title": ""
},
@@ -2869,15 +2875,21 @@
"tasks": "",
"tasks_in_production": "",
"tasks_on_board": "",
"tasks_in_view": "",
"total_amount_in_production": "",
"total_amount_on_board": "",
"total_amount_in_view": "",
"total_hours_in_production": "",
"total_hours_on_board": "",
"total_hours_in_view": "",
"total_jobs_on_board": "",
"total_jobs_in_view": "",
"total_lab_in_production": "",
"total_lab_on_board": "",
"total_lab_in_view": "",
"total_lar_in_production": "",
"total_lar_on_board": ""
"total_lar_on_board": "",
"total_lar_in_view": ""
},
"successes": {
"removed": ""
@@ -3032,6 +3044,7 @@
"production_by_target_date": "",
"production_by_technician": "",
"production_by_technician_one": "",
"production_not_production_status": "",
"production_over_time": "",
"psr_by_make": "",
"purchase_return_ratio_grouped_by_vendor_detail": "",
@@ -3174,6 +3187,7 @@
"billid": "",
"completed": "",
"created_at": "",
"created_by": "",
"description": "",
"due_date": "",
"job": {

File diff suppressed because it is too large Load Diff

View File

@@ -2333,6 +2333,14 @@ export const TemplateList = (type, context) => {
key: "production_by_technician",
//idtype: "vendor",
disabled: false
},
production_not_production_status: {
title: i18n.t("reportcenter.templates.production_not_production_status"),
description: "",
subject: i18n.t("reportcenter.templates.production_not_production_status"),
key: "production_not_production_status",
//idtype: "vendor",
disabled: false
}
}
: {}),

View File

@@ -1,3 +1,4 @@
// Sometimes referred to as PageSize, this variable controls the amount of records
// to show on one page during pagination.
export const pageLimit = 50;
export const exportPageLimit = 10;

View File

@@ -1,5 +1,6 @@
import dayjs from "dayjs";
import "dayjs/locale/en";
import dayjsBusinessDays from "dayjs-business-days2";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
import updateLocale from "dayjs/plugin/updateLocale";
@@ -64,4 +65,6 @@ dayjs.extend(minMax);
dayjs.extend(isBetween);
dayjs.extend(dayjsBusinessDays);
dayjs.locale("en");
export default dayjs;

View File

@@ -2,5 +2,5 @@ import { store } from "../redux/store";
export function CreateExplorerLinkForJob({ jobid }) {
const bodyshop = store.getState().user.bodyshop;
return `imexmedia://${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`;
return `imexmedia://`.concat(encodeURIComponent(`${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`));
}

View File

@@ -74,7 +74,7 @@ services:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- SERVICES=ses
- SERVICES=ses,secretsmanager,cloudwatch,logs
- DEBUG=0
- AWS_ACCESS_KEY_ID=test
- AWS_SECRET_ACCESS_KEY=test
@@ -101,6 +101,10 @@ services:
depends_on:
localstack:
condition: service_healthy
volumes:
- './localstack:/tmp/localstack'
- './certs:/tmp/certs'
environment:
- AWS_ACCESS_KEY_ID=test
- AWS_SECRET_ACCESS_KEY=test
@@ -110,6 +114,8 @@ services:
"
aws --endpoint-url=http://localstack:4566 ses verify-domain-identity --domain imex.online --region ca-central-1
aws --endpoint-url=http://localstack:4566 ses verify-email-identity --email-address noreply@imex.online --region ca-central-1
aws --endpoint-url=http://localstack:4566 secretsmanager create-secret --name CHATTER_PRIVATE_KEY --secret-string file:///tmp/certs/id_rsa
aws --endpoint-url=http://localstack:4566 logs create-log-group --log-group-name development --region ca-central-1
"
# Node App: The Main IMEX API
node-app:
@@ -134,6 +140,7 @@ services:
condition: service_completed_successfully
ports:
- "4000:4000"
- "9229:9229"
volumes:
- .:/app
- node-app-npm-cache:/app/node_modules

1532
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,10 +19,11 @@
"makeitpretty": "prettier --write \"**/*.{css,js,json,jsx,scss}\""
},
"dependencies": {
"@aws-sdk/client-elasticache": "^3.665.0",
"@aws-sdk/client-secrets-manager": "^3.654.0",
"@aws-sdk/client-ses": "^3.654.0",
"@aws-sdk/credential-provider-node": "^3.654.0",
"@aws-sdk/client-cloudwatch-logs": "^3.679.0",
"@aws-sdk/client-elasticache": "^3.675.0",
"@aws-sdk/client-secrets-manager": "^3.675.0",
"@aws-sdk/client-ses": "^3.675.0",
"@aws-sdk/credential-provider-node": "^3.675.0",
"@opensearch-project/opensearch": "^2.12.0",
"@socket.io/admin-ui": "^0.5.1",
"@socket.io/redis-adapter": "^8.3.0",
@@ -32,40 +33,41 @@
"bluebird": "^3.7.2",
"body-parser": "^1.20.3",
"canvas": "^2.11.2",
"chart.js": "^4.4.4",
"cloudinary": "^2.5.0",
"chart.js": "^4.4.5",
"cloudinary": "^2.5.1",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"cookie-parser": "^1.4.7",
"cors": "2.8.5",
"csrf": "^3.1.0",
"dinero.js": "^1.9.1",
"dotenv": "^16.4.5",
"express": "^4.21.0",
"firebase-admin": "^12.5.0",
"express": "^4.21.1",
"firebase-admin": "^12.6.0",
"graphql": "^16.9.0",
"graphql-request": "^6.1.0",
"graylog2": "^0.2.1",
"inline-css": "^4.0.2",
"intuit-oauth": "^4.1.2",
"ioredis": "^5.4.1",
"json-2-csv": "^5.5.5",
"json-2-csv": "^5.5.6",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"moment-timezone": "^0.5.46",
"multer": "^1.4.5-lts.1",
"node-mailjet": "^6.0.6",
"node-persist": "^4.0.3",
"nodemailer": "^6.9.15",
"phone": "^3.1.50",
"phone": "^3.1.51",
"recursive-diff": "^1.0.9",
"redis": "^4.7.0",
"rimraf": "^6.0.1",
"soap": "^1.1.4",
"soap": "^1.1.5",
"socket.io": "^4.8.0",
"socket.io-adapter": "^2.5.5",
"ssh2-sftp-client": "^10.0.3",
"twilio": "^4.23.0",
"uuid": "^10.0.0",
"winston": "^3.15.0",
"winston-cloudwatch": "^6.3.0",
"xml2js": "^0.6.2",
"xmlbuilder2": "^3.1.1"
},

View File

@@ -1,5 +1,10 @@
const cors = require("cors");
// Load environment variables THIS MUST BE AT THE TOP
const path = require("path");
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
const cors = require("cors");
const http = require("http");
const Redis = require("ioredis");
const express = require("express");
@@ -18,11 +23,6 @@ const { redisSocketEvents } = require("./server/web-sockets/redisSocketEvents");
const { ElastiCacheClient, DescribeCacheClustersCommand } = require("@aws-sdk/client-elasticache");
const { default: InstanceManager } = require("./server/utils/instanceMgr");
// Load environment variables
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
const CLUSTER_RETRY_BASE_DELAY = 100;
const CLUSTER_RETRY_MAX_DELAY = 5000;
const CLUSTER_RETRY_JITTER = 100;
@@ -153,18 +153,13 @@ const connectToRedisCluster = async () => {
} else {
// Use the Dockerized Redis cluster in development
if (isEmpty(process.env?.REDIS_URL) || !isString(process.env?.REDIS_URL)) {
logger.log(`[${process.env.NODE_ENV}] No or Malformed REDIS_URL present.`, "ERROR", "redis", "api");
logger.log(`No or Malformed REDIS_URL present.`, "ERROR", "redis", "api");
process.exit(1);
}
try {
redisServers = JSON.parse(process.env.REDIS_URL);
} catch (error) {
logger.log(
`[${process.env.NODE_ENV}] Failed to parse REDIS_URL: ${error.message}. Exiting...`,
"ERROR",
"redis",
"api"
);
logger.log(`Failed to parse REDIS_URL: ${error.message}. Exiting...`, "ERROR", "redis", "api");
process.exit(1);
}
}
@@ -172,12 +167,7 @@ const connectToRedisCluster = async () => {
const clusterRetryStrategy = (times) => {
const delay =
Math.min(CLUSTER_RETRY_BASE_DELAY + times * 50, CLUSTER_RETRY_MAX_DELAY) + Math.random() * CLUSTER_RETRY_JITTER;
logger.log(
`[${process.env.NODE_ENV}] Redis cluster not yet ready. Retrying in ${delay.toFixed(2)}ms`,
"ERROR",
"redis",
"api"
);
logger.log(`Redis cluster not yet ready. Retrying in ${delay.toFixed(2)}ms`, "WARN", "redis", "api");
return delay;
};
@@ -194,12 +184,12 @@ const connectToRedisCluster = async () => {
return new Promise((resolve, reject) => {
redisCluster.on("ready", () => {
logger.log(`[${process.env.NODE_ENV}] Redis cluster connection established.`, "INFO", "redis", "api");
logger.log(`Redis cluster connection established.`, "INFO", "redis", "api");
resolve(redisCluster);
});
redisCluster.on("error", (err) => {
logger.log(`[${process.env.NODE_ENV}] Redis cluster connection failed: ${err.message}`, "ERROR", "redis", "api");
logger.log(`Redis cluster connection failed: ${err.message}`, "ERROR", "redis", "api");
reject(err);
});
});
@@ -215,7 +205,7 @@ const applySocketIO = async ({ server, app }) => {
// Handle errors
redisCluster.on("error", (err) => {
logger.log(`[${process.env.NODE_ENV}] Redis ERROR`, "ERROR", "redis", "api");
logger.log(`Redis ERROR`, "ERROR", "redis", "api");
});
const pubClient = redisCluster;
@@ -249,7 +239,7 @@ const applySocketIO = async ({ server, app }) => {
});
if (isString(process.env.REDIS_ADMIN_PASS) && !isEmpty(process.env.REDIS_ADMIN_PASS)) {
logger.log(`[${process.env.NODE_ENV}] Initializing Redis Admin UI....`, "INFO", "redis", "api");
logger.log(`Initializing Redis Admin UI....`, "INFO", "redis", "api");
instrument(ioRedis, {
auth: {
type: "basic",
@@ -312,9 +302,9 @@ const main = async () => {
try {
await server.listen(port);
logger.log(`[${process.env.NODE_ENV}] Server started on port ${port}`, "INFO", "api");
logger.log(`Server started on port ${port}`, "INFO", "api");
} catch (error) {
logger.log(`[${process.env.NODE_ENV}] Server failed to start on port ${port}`, "ERROR", "api", error);
logger.log(`Server failed to start on port ${port}`, "ERROR", "api", error);
}
};

View File

@@ -24,9 +24,9 @@ axios.interceptors.request.use((x) => {
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
console.log(printable);
//console.log(printable);
CdkBase.createJsonEvent(socket, "TRACE", `Raw Request: ${printable}`, x.data);
CdkBase.createJsonEvent(socket, "SILLY", `Raw Request: ${printable}`, x.data);
return x;
});
@@ -35,8 +35,8 @@ axios.interceptors.response.use((x) => {
const socket = x.config.socket;
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`;
console.log(printable);
CdkBase.createJsonEvent(socket, "TRACE", `Raw Response: ${printable}`, x.data);
//console.log(printable);
CdkBase.createJsonEvent(socket, "SILLY", `Raw Response: ${printable}`, x.data);
return x;
});
@@ -181,7 +181,7 @@ async function QueryBillData(socket, billids) {
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.GET_PBS_AP_ALLOCATIONS, { billids: billids });
CdkBase.createLogEvent(socket, "TRACE", `Bill data query result ${JSON.stringify(result, null, 2)}`);
CdkBase.createLogEvent(socket, "SILLY", `Bill data query result ${JSON.stringify(result, null, 2)}`);
return result;
}

View File

@@ -26,9 +26,9 @@ axios.interceptors.request.use((x) => {
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
console.log(printable);
//console.log(printable);
CdkBase.createJsonEvent(socket, "TRACE", `Raw Request: ${printable}`, x.data);
CdkBase.createJsonEvent(socket, "SILLY", `Raw Request: ${printable}`, x.data);
return x;
});
@@ -37,8 +37,8 @@ axios.interceptors.response.use((x) => {
const socket = x.config.socket;
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`;
console.log(printable);
CdkBase.createJsonEvent(socket, "TRACE", `Raw Response: ${printable}`, x.data);
//console.log(printable);
CdkBase.createJsonEvent(socket, "SILLY", `Raw Response: ${printable}`, x.data);
return x;
});
@@ -118,7 +118,7 @@ async function CheckForErrors(socket, response) {
CdkBase.createLogEvent(socket, "DEBUG", `Successful response from DMS. ${response.Message || ""}`);
} else {
CdkBase.createLogEvent(socket, "ERROR", `Error received from DMS: ${response.Message}`);
CdkBase.createLogEvent(socket, "TRACE", `Error received from DMS: ${JSON.stringify(response)}`);
CdkBase.createLogEvent(socket, "SILLY", `Error received from DMS: ${JSON.stringify(response)}`);
}
}
@@ -130,7 +130,7 @@ async function QueryJobData(socket, jobid) {
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.QUERY_JOBS_FOR_PBS_EXPORT, { id: jobid });
CdkBase.createLogEvent(socket, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
CdkBase.createLogEvent(socket, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}

View File

@@ -207,7 +207,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
});
if (!hasMapaLine && jobs_by_pk.job_totals.rates.mapa.total.amount > 0) {
// console.log("Adding MAPA Line Manually.");
// //console.log("Adding MAPA Line Manually.");
const mapaAccountName = responsibilityCenters.defaults.profits.MAPA;
const mapaAccount = responsibilityCenters.profits.find((c) => c.name === mapaAccountName);
@@ -272,12 +272,12 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
});
}
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
////console.log("NO MAPA ACCOUNT FOUND!!");
}
}
if (!hasMashLine && jobs_by_pk.job_totals.rates.mash.total.amount > 0) {
// console.log("Adding MASH Line Manually.");
// //console.log("Adding MASH Line Manually.");
const mashAccountName = responsibilityCenters.defaults.profits.MASH;
@@ -341,7 +341,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
});
}
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
// //console.log("NO MASH ACCOUNT FOUND!!");
}
}
@@ -795,7 +795,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
: taxCodes[taxAccountCode];
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
const taxAmount = Dinero(job_totals.totals.us_sales_tax_breakdown[`ty${tyCounter}Tax`]);
console.log(`Tax ${tyCounter}`, taxAmount.toFormat());
//console.log(`Tax ${tyCounter}`, taxAmount.toFormat());
if (taxAmount.getAmount() > 0) {
if (qbo) {
InvoiceLineAdd.push({

View File

@@ -121,7 +121,7 @@ exports.default = async (req, res) => {
res.status(200).json(ret);
} catch (error) {
console.log(error);
//console.log(error);
logger.log("qbo-payable-create-error", "ERROR", req.user.email, {
error: error.message,
stack: error.stack

View File

@@ -182,7 +182,7 @@ exports.default = async (req, res) => {
res.status(200).json(ret);
} catch (error) {
console.log(error);
//console.log(error);
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
error: error.message,
stack: error.stack

View File

@@ -185,7 +185,7 @@ exports.default = async (req, res) => {
error?.response?.data ||
error?.message
});
console.log(error);
//console.log(error);
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
error: error.message,
stack: error.stack
@@ -211,7 +211,7 @@ exports.default = async (req, res) => {
res.status(200).json(ret);
} catch (error) {
console.log(error);
//console.log(error);
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
error: error.message,
stack: error.stack

View File

@@ -9,7 +9,7 @@ require("dotenv").config({
const client = require("../graphql-client/graphql-client").client;
exports.createAssociation = async (req, res) => {
logger.log("admin-create-association", "ADMIN", req.user.email, null, {
logger.log("admin-create-association", "debug", req.user.email, null, {
request: req.body,
ioadmin: true
});
@@ -31,7 +31,7 @@ exports.createAssociation = async (req, res) => {
res.json(result);
};
exports.createShop = async (req, res) => {
logger.log("admin-create-shop", "ADMIN", req.user.email, null, {
logger.log("admin-create-shop", "debug", req.user.email, null, {
request: req.body,
ioadmin: true
});
@@ -64,7 +64,7 @@ exports.createShop = async (req, res) => {
}
};
exports.updateCounter = async (req, res) => {
logger.log("admin-update-counter", "ADMIN", req.user.email, null, {
logger.log("admin-update-counter", "debug", req.user.email, null, {
request: req.body,
ioadmin: true
});
@@ -91,7 +91,7 @@ exports.updateCounter = async (req, res) => {
}
};
exports.updateShop = async (req, res) => {
logger.log("admin-update-shop", "ADMIN", req.user.email, null, {
logger.log("admin-update-shop", "debug", req.user.email, null, {
request: req.body,
ioadmin: true
});

View File

@@ -22,7 +22,7 @@ exports.defaultRoute = async function (req, res) {
const jobData = await QueryJobData(req, req.BearerToken, req.body.jobid);
return res.status(200).json({ data: calculateAllocations(req, jobData) });
} catch (error) {
console.log(error);
////console.log(error);
CdkBase.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
res.status(500).json({ error: `Error encountered in CdkCalculateAllocations. ${error}` });
}
@@ -33,7 +33,7 @@ exports.default = async function (socket, jobid) {
const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid);
return calculateAllocations(socket, jobData);
} catch (error) {
console.log(error);
////console.log(error);
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
}
};
@@ -42,7 +42,7 @@ async function QueryJobData(connectionData, token, jobid) {
CdkBase.createLogEvent(connectionData, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client.setHeaders({ Authorization: token }).request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
CdkBase.createLogEvent(connectionData, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
CdkBase.createLogEvent(connectionData, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
@@ -212,7 +212,7 @@ function calculateAllocations(connectionData, job) {
});
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
// console.log("Adding MAPA Line Manually.");
// //console.log("Adding MAPA Line Manually.");
const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
@@ -224,7 +224,7 @@ function calculateAllocations(connectionData, job) {
Dinero(job.job_totals.rates.mapa.total)
);
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
////console.log("NO MAPA ACCOUNT FOUND!!");
}
}
@@ -373,13 +373,19 @@ function calculateAllocations(connectionData, job) {
});
//profile level adjustments for labor and materials
Object.keys(job.job_totals.rates).forEach((key) => {
if (job.job_totals.rates[key] && job.job_totals.rates[key].adjustment && Dinero(job.job_totals.rates[key].adjustment).isZero() === false) {
if (
job.job_totals.rates[key] &&
job.job_totals.rates[key].adjustment &&
Dinero(job.job_totals.rates[key].adjustment).isZero() === false
) {
const accountName = selectedDmsAllocationConfig.profits[key.toUpperCase()];
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName);
if (otherAccount) {
if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero();
profitCenterHash[accountName] = profitCenterHash[accountName].add(Dinero(job.job_totals.rates[key].adjustments));
profitCenterHash[accountName] = profitCenterHash[accountName].add(
Dinero(job.job_totals.rates[key].adjustments)
);
} else {
CdkBase.createLogEvent(
connectionData,

View File

@@ -151,7 +151,7 @@ async function QueryJobData(socket, jobid) {
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.QUERY_JOBS_FOR_CDK_EXPORT, { id: jobid });
CdkBase.createLogEvent(socket, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
CdkBase.createLogEvent(socket, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
@@ -171,7 +171,7 @@ async function CalculateDmsVid(socket, JobData) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientVehicleInsertUpdate.getVehIdsAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
@@ -214,7 +214,7 @@ async function QueryDmsVehicleById(socket, JobData, DMSVid) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientVehicleInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.readAsync response.`);
@@ -246,7 +246,7 @@ async function QueryDmsCustomerById(socket, JobData, CustomerId) {
CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.readAsync response.`);
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientCustomerInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
@@ -295,7 +295,7 @@ async function QueryDmsCustomerByName(socket, JobData) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerSearch);
@@ -337,7 +337,7 @@ async function GenerateDmsCustomerNumber(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientCustomerInsertUpdate.getCustomerNumberAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
@@ -425,7 +425,7 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.insertAsync response.`);
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
@@ -505,7 +505,7 @@ async function InsertDmsVehicle(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientVehicleInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.insertAsync response.`);
@@ -611,7 +611,7 @@ async function UpdateDmsVehicle(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"DEBUG",
`soapClientVehicleInsertUpdate.updateAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.updateAsync response.`);
@@ -650,7 +650,7 @@ async function InsertServiceVehicleHistory(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientServiceHistoryInsert.serviceHistoryHeaderInsert Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert response.`);
@@ -690,7 +690,7 @@ async function InsertDmsStartWip(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientAccountingGLInsertUpdate.doStartWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doStartWIPAsync response.`);
@@ -721,7 +721,7 @@ async function InsertDmsBatchWip(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync response.`);
@@ -885,7 +885,7 @@ async function PostDmsBatchWip(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`);
@@ -914,7 +914,7 @@ async function QueryDmsErrWip(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"DEBUG",
`soapClientAccountingGLInsertUpdate.doErrWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doErrWIPAsync response.`);
@@ -945,7 +945,7 @@ async function DeleteDmsWip(socket) {
CdkBase.createLogEvent(
socket,
"TRACE",
"SILLY",
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`);

View File

@@ -820,7 +820,7 @@ exports.default = async (req, res) => {
job: JSON.stringify({ id: job.id, ro_number: job.ro_number }),
error: error.message || JSON.stringify(error)
});
console.log(error);
//console.log(error);
}
} catch (error) {
logger.log("arms-failed-job", "ERROR", "api", job.shopid, {
@@ -886,7 +886,10 @@ exports.default = async (req, res) => {
const [result, rawResponse, , rawRequest] = entegralResponse;
} catch (error) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
console.log(error);
//console.log(error);
logger.log("arms-error-shop", "ERROR", "api", bodyshop.id, {
error
});
}
} catch (error) {
//Error at the shop level.

View File

@@ -206,9 +206,6 @@ const CreateRepairOrderTag = (job, errorCallback) => {
const repairCosts = CreateCosts(job);
if (job.ro_number === "QBD209") {
console.log("Stop here");
}
//Calculate detail only lines.
const detailAdjustments = job.joblines
.filter((jl) => jl.ah_detail_line && jl.mod_lbr_ty)

View File

@@ -154,7 +154,7 @@ exports.default = async (req, res) => {
async function getPrivateKey() {
// Connect to AWS Secrets Manager
const client = new SecretsManagerClient({ region: "ca-central-1" });
const command = new GetSecretValueCommand({ SecretId: CHATTER_PRIVATE_KEY });
const command = new GetSecretValueCommand({ SecretId: "CHATTER_PRIVATE_KEY" });
logger.log("chatter-get-private-key", "DEBUG", "api", null, null);
try {

View File

@@ -77,6 +77,7 @@ const formatPriority = (priority) => {
* @param priority
* @param description
* @param dueDate
* @param createdBy
* @param bodyshop
* @param job
* @param taskId
@@ -96,11 +97,11 @@ const getEndpoints = (bodyshop) =>
: "https://romeonline.io"
});
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId, dateLine) => {
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId, dateLine, createdBy) => {
const endPoints = getEndpoints(bodyshop);
return {
header: title,
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)}`,
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)} | Created By: ${createdBy || "N/A"}`,
body: `Reference: ${job.ro_number || "N/A"} | ${job.ownr_co_nm ? job.ownr_co_nm : `${job.ownr_fn || ""} ${job.ownr_ln || ""}`.trim()} | ${`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim()}<br>${description ? description.concat("<br>") : ""}<a href="${endPoints}/manage/tasks/alltasks?taskid=${taskId}">View this task.</a>`,
dateLine
};
@@ -181,7 +182,8 @@ const taskAssignedEmail = async (req, res) => {
tasks_by_pk.bodyshop,
tasks_by_pk.job,
newTask.id,
dateLine
dateLine,
newTask.created_by
)
),
null,
@@ -268,7 +270,8 @@ const tasksRemindEmail = async (req, res) => {
onlyTask.bodyshop,
onlyTask.job,
onlyTask.id,
dateLine
dateLine,
onlyTask.created_by
)
);
}

View File

@@ -16,7 +16,7 @@ admin.initializeApp({
});
const createUser = async (req, res) => {
logger.log("admin-create-user", "ADMIN", req.user.email, null, {
logger.log("admin-create-user", "debug", req.user.email, null, {
request: req.body,
ioadmin: true
});
@@ -97,7 +97,7 @@ const sendPromanagerWelcomeEmail = (req, res) => {
// Validate email before proceeding
if (!dbUser.validemail) {
logger.log("admin-send-welcome-email-skip", "ADMIN", req.user.email, null, {
logger.log("admin-send-welcome-email-skip", "debug", req.user.email, null, {
message: "User email is not valid, skipping email.",
email
});
@@ -107,7 +107,7 @@ const sendPromanagerWelcomeEmail = (req, res) => {
// Check if the user's company is ProManager
const convenientCompany = dbUser.associations?.[0]?.bodyshop?.convenient_company;
if (convenientCompany !== "promanager") {
logger.log("admin-send-welcome-email-skip", "ADMIN", req.user.email, null, {
logger.log("admin-send-welcome-email-skip", "debug", req.user.email, null, {
message: 'convenient_company is not "promanager", skipping email.',
convenientCompany
});
@@ -141,7 +141,7 @@ const sendPromanagerWelcomeEmail = (req, res) => {
})
.then(() => {
// Log success and return response
logger.log("admin-send-welcome-email", "ADMIN", req.user.email, null, {
logger.log("admin-send-welcome-email", "debug", req.user.email, null, {
request: req.body,
ioadmin: true,
emailSentTo: email
@@ -161,7 +161,7 @@ const sendPromanagerWelcomeEmail = (req, res) => {
};
const updateUser = (req, res) => {
logger.log("admin-update-user", "ADMIN", req.user.email, null, {
logger.log("admin-update-user", "debug", req.user.email, null, {
request: req.body,
ioadmin: true
});
@@ -184,7 +184,7 @@ const updateUser = (req, res) => {
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
logger.log("admin-update-user-success", "ADMIN", req.user.email, null, {
logger.log("admin-update-user-success", "debug", req.user.email, null, {
userRecord,
ioadmin: true
});
@@ -199,7 +199,7 @@ const updateUser = (req, res) => {
};
const getUser = (req, res) => {
logger.log("admin-get-user", "ADMIN", req.user.email, null, {
logger.log("admin-get-user", "debug", req.user.email, null, {
request: req.body,
ioadmin: true
});
@@ -321,7 +321,7 @@ module.exports = {
// admin.auth().setCustomUserClaims(uid, {
// ioadmin: true,
// "https://hasura.io/jwt/claims": {
// "x-hasura-default-role": "admin",
// "x-hasura-default-role": "debug",
// "x-hasura-allowed-roles": ["admin"],
// "x-hasura-user-id": uid,
// },

View File

@@ -2506,6 +2506,7 @@ exports.QUERY_TASK_BY_ID = `
query QUERY_TASK_BY_ID($id: uuid!) {
tasks_by_pk(id: $id) {
id
created_by
assigned_to_employee{
id
user_email

View File

@@ -78,7 +78,7 @@ exports.lightbox_credentials = async (req, res) => {
res.send(response.data);
} catch (error) {
console.log(error);
//console.log(error);
logger.log("intellipay-lightbox-credentials-error", "ERROR", req.user?.email, null, {
error: JSON.stringify(error)
});
@@ -109,7 +109,7 @@ exports.payment_refund = async (req, res) => {
res.send(response.data);
} catch (error) {
console.log(error);
//console.log(error);
logger.log("intellipay-refund-error", "ERROR", req.user?.email, null, {
error: JSON.stringify(error)
});
@@ -143,7 +143,7 @@ exports.generate_payment_url = async (req, res) => {
res.send(response.data);
} catch (error) {
console.log(error);
//console.log(error);
logger.log("intellipay-payment-url-error", "ERROR", req.user?.email, null, {
error: JSON.stringify(error)
});

View File

@@ -28,14 +28,14 @@ exports.default = async (req, res) => {
// }
// });
ioRedis.to(getBodyshopRoom(bodyshopid)).emit("bodyshop-message", {
operationName,
useremail
});
// ioRedis.to(getBodyshopRoom(bodyshopid)).emit("bodyshop-message", {
// operationName,
// useremail
// });
res.sendStatus(200);
} catch (error) {
logger.log("ioevent-error", "trace", user, null, {
logger.log("ioevent-error", "silly", user, null, {
operationname: operationName,
time,
dbevent,

View File

@@ -866,8 +866,8 @@ function CalculateTaxesTotals(job, otherTotals) {
}
});
console.log("*** Taxable Amounts***");
console.table(JSON.parse(JSON.stringify(taxableAmounts)));
// console.log("*** Taxable Amounts***");
// console.table(JSON.parse(JSON.stringify(taxableAmounts)));
//For the taxable amounts, figure out which tax type applies.
//Then sum up the total of that tax type and then calculate the thresholds.
@@ -979,8 +979,8 @@ function CalculateTaxesTotals(job, otherTotals) {
}
const remainingTaxableAmounts = taxableAmountsByTier;
console.log("*** Taxable Amounts by Tier***");
console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));
// console.log("*** Taxable Amounts by Tier***");
// console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));
Object.keys(taxableAmountsByTier).forEach((taxTierKey) => {
try {
@@ -1030,8 +1030,8 @@ function CalculateTaxesTotals(job, otherTotals) {
}
});
console.log("*** Total Tax by Tier Amounts***");
console.table(JSON.parse(JSON.stringify(totalTaxByTier)));
// console.log("*** Total Tax by Tier Amounts***");
// console.table(JSON.parse(JSON.stringify(totalTaxByTier)));
stateTax = stateTax
.add(totalTaxByTier.ty1Tax)

View File

@@ -182,7 +182,7 @@ async function AutoAddAtsIfRequired({ job, client }) {
job.joblines[atsLineIndex].act_price = atsAmount;
}
console.log(job.jobLines);
//console.log(job.jobLines);
}
}

View File

@@ -148,7 +148,7 @@ async function OpenSearchUpdateHandler(req, res) {
};
const response = await osClient.index(payload);
console.log(response.body);
//console.log(response.body);
res.status(200).json(response.body);
}
} catch (error) {
@@ -255,7 +255,7 @@ async function OpenSearchSearchHandler(req, res) {
res.json(body);
} catch (error) {
console.log(error);
//console.log(error);
logger.log("os-search-error", "ERROR", req.user.email, null, {
error: JSON.stringify(error)
});

View File

@@ -1,18 +1,19 @@
const { createCanvas } = require("canvas");
const Chart = require("chart.js/auto");
const logger = require("../utils/logger");
const { backgroundColors, borderColors } = require("./canvas-colors");
const { isObject, defaultsDeep, isNumber } = require("lodash");
exports.canvastest = function (req, res) {
console.log("Incoming test request.", req);
//console.log("Incoming test request.", req);
res.status(200).send("OK");
};
exports.canvas = function (req, res) {
const { w, h, values, keys, override } = req.body;
console.log("Incoming Canvas Request:", w, h, values, keys, override);
//console.log("Incoming Canvas Request:", w, h, values, keys, override);
logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override });
// Gate required values
if (!values || !keys) {
res.status(400).send("Missing required data");

View File

@@ -47,7 +47,7 @@ exports.receive = async (req, res) => {
//Found a bodyshop - should always happen.
if (response.bodyshops[0].conversations.length === 0) {
//No conversation Found, create one.
console.log("[SMS Receive] No conversation found. Creating one.");
//console.log("[SMS Receive] No conversation found. Creating one.");
newMessage.conversation = {
data: {
bodyshopid: response.bodyshops[0].id,
@@ -56,7 +56,7 @@ exports.receive = async (req, res) => {
};
} else if (response.bodyshops[0].conversations.length === 1) {
//Just add it to the conversation
console.log("[SMS Receive] Conversation found. Added ID.");
//console.log("[SMS Receive] Conversation found. Added ID.");
newMessage.conversationid = response.bodyshops[0].conversations[0].id;
} else {
//We should never get here.
@@ -123,7 +123,14 @@ exports.receive = async (req, res) => {
}
}
} catch (e1) {
console.log("e1", e1);
logger.log("sms-inbound-error", "ERROR", "api", null, {
msid: req.body.SmsMessageSid,
text: req.body.Body,
image: !!req.body.MediaUrl0,
image_path: generateMediaArray(req.body),
messagingServiceSid: req.body.MessagingServiceSid,
error: e1
});
res.sendStatus(500).json(e1);
}
}

View File

@@ -1,25 +1,133 @@
const graylog2 = require("graylog2");
const logger = new graylog2.graylog({
servers: [{ host: "logs.bodyshop.app", port: 12201 }]
// Load environment variables THIS MUST BE AT THE TOP
const path = require("path");
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
function log(message, type, user, record, object) {
if (type !== "ioevent")
console.log(message, {
type,
env: process.env.NODE_ENV || "development",
user,
record,
...object
});
logger.log(message, message, {
type,
env: process.env.NODE_ENV || "development",
user,
record,
...object
});
}
const InstanceManager = require("../utils/instanceMgr").default;
const winston = require("winston");
const WinstonCloudWatch = require("winston-cloudwatch");
const { isString, isEmpty } = require("lodash");
const { networkInterfaces } = require("node:os");
module.exports = { log };
const createLogger = () => {
try {
const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME);
const logGroupName = isLocal ? "development" : process.env.CLOUDWATCH_LOG_GROUP;
const winstonCloudwatchTransportDefaults = {
logGroupName: logGroupName,
awsOptions: {
region: InstanceManager({
imex: "ca-central-1",
rome: "us-east-2"
})
},
jsonMessage: true
};
if (isLocal) {
winstonCloudwatchTransportDefaults.awsOptions.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`;
console.log(
`Winston Transports set to LocalStack end point: ${winstonCloudwatchTransportDefaults.awsOptions.endpoint}`
);
}
const levelFilter = (levels) => {
return winston.format((info) => {
if (Array.isArray(levels)) {
return levels.includes(info.level) ? info : false;
} else {
return info.level === levels ? info : false;
}
})();
};
const getPrivateIP = () => {
const interfaces = networkInterfaces();
for (const name of Object.keys(interfaces)) {
for (const iface of interfaces[name]) {
// Find an IPv4 address that's not internal (like localhost)
if (iface.family === "IPv4" && !iface.internal) {
return iface.address;
}
}
}
return "127.0.0.1";
};
const createProductionTransport = (level, logStreamName, filters) => {
return new WinstonCloudWatch({
level,
logStreamName: logStreamName || level,
format: levelFilter(filters || level),
...winstonCloudwatchTransportDefaults
});
};
const hostname = process.env.HOSTNAME || getPrivateIP();
const getDevelopmentTransports = () => [
new winston.transports.Console({
level: "silly",
format: winston.format.combine(
winston.format.colorize(),
winston.format.timestamp(),
winston.format.printf(({ level, message, timestamp, user, record, meta }) => {
const hostnameColor = `\x1b[34m${hostname}\x1b[0m`; // Blue
const timestampColor = `\x1b[36m${timestamp}\x1b[0m`; // Cyan
const labelColor = "\x1b[33m"; // Yellow
const separatorColor = "\x1b[35m|\x1b[0m"; // Magenta for separators
return `${timestampColor} [${hostnameColor}] [${level}]: ${message} ${
user ? `${separatorColor} ${labelColor}user:\x1b[0m ${JSON.stringify(user)}` : ""
} ${record ? `${separatorColor} ${labelColor}record:\x1b[0m ${JSON.stringify(record)}` : ""}${
meta
? `\n${separatorColor} ${labelColor}meta:\x1b[0m ${JSON.stringify(meta, null, 2)} ${separatorColor}`
: ""
}`;
})
)
})
];
const getProductionTransports = () => [
createProductionTransport("error"),
createProductionTransport("warn"),
createProductionTransport("info"),
createProductionTransport("silly", "debug", ["http", "verbose", "debug", "silly"])
];
const winstonLogger = winston.createLogger({
format: winston.format.json(),
transports:
process.env.NODE_ENV === "production"
? getProductionTransports()
: [...getDevelopmentTransports(), ...getProductionTransports()]
});
const log = (message, type, user, record, meta) => {
winstonLogger.log({
level: type.toLowerCase(),
message,
user,
record,
hostname,
meta
});
};
return {
log,
logger: winstonLogger
};
} catch (e) {
console.error("Error setting up enhanced Logger, defaulting to console.: " + e?.message || "");
return {
log: console.log,
logger: console.log
};
}
};
module.exports = createLogger();

View File

@@ -8,7 +8,6 @@ const redisSocketEvents = ({
}) => {
// Logging helper functions
const createLogEvent = (socket, level, message) => {
console.log(`[IOREDIS LOG EVENT] - ${socket?.user?.email} - ${socket.id} - ${message}`);
logger.log("ioredis-log-event", level, socket?.user?.email, null, { wsmessage: message });
};
@@ -33,7 +32,6 @@ const redisSocketEvents = ({
next(new Error("Authentication error - no authorization token."));
}
} catch (error) {
console.log("Uncaught connection error:::", error);
logger.log("websocket-connection-error", "error", null, null, {
...error
});
@@ -43,7 +41,7 @@ const redisSocketEvents = ({
// Register Socket Events
const registerSocketEvents = (socket) => {
createLogEvent(socket, "DEBUG", `Registering RedisIO Socket Events.`);
createLogEvent(socket, "debug", `Registering RedisIO Socket Events.`);
// Token Update Events
const registerUpdateEvents = (socket) => {
@@ -56,7 +54,7 @@ const redisSocketEvents = ({
// If We ever want to persist user Data across workers
// await setSessionData(socket.id, "user", user);
createLogEvent(socket, "INFO", "Token updated successfully");
createLogEvent(socket, "debug", "Token updated successfully");
socket.emit("token-updated", { success: true });
} catch (error) {
@@ -67,7 +65,7 @@ const redisSocketEvents = ({
error: "Stale token."
});
} else {
createLogEvent(socket, "ERROR", `Token update failed: ${error.message}`);
createLogEvent(socket, "error", `Token update failed: ${error.message}`);
socket.emit("token-updated", { success: false, error: error.message });
// For any other errors, optionally disconnect the socket
socket.disconnect();
@@ -82,9 +80,9 @@ const redisSocketEvents = ({
try {
const room = getBodyshopRoom(bodyshopUUID);
socket.join(room);
createLogEvent(socket, "DEBUG", `Client joined bodyshop room: ${room}`);
createLogEvent(socket, "debug", `Client joined bodyshop room: ${room}`);
} catch (error) {
createLogEvent(socket, "ERROR", `Error joining room: ${error}`);
createLogEvent(socket, "error", `Error joining room: ${error}`);
}
};
@@ -92,9 +90,9 @@ const redisSocketEvents = ({
try {
const room = getBodyshopRoom(bodyshopUUID);
socket.leave(room);
createLogEvent(socket, "DEBUG", `Client left bodyshop room: ${room}`);
createLogEvent(socket, "debug", `Client left bodyshop room: ${room}`);
} catch (error) {
createLogEvent(socket, "ERROR", `Error joining room: ${error}`);
createLogEvent(socket, "error", `Error joining room: ${error}`);
}
};
@@ -102,9 +100,10 @@ const redisSocketEvents = ({
try {
const room = getBodyshopRoom(bodyshopUUID);
io.to(room).emit("bodyshop-message", message);
createLogEvent(socket, "DEBUG", `Broadcast message to bodyshop ${room}`);
// We do not need this as these can be debugged live
// createLogEvent(socket, "debug", `Broadcast message to bodyshop ${room}`);
} catch (error) {
createLogEvent(socket, "ERROR", `Error getting room: ${error}`);
createLogEvent(socket, "error", `Error getting room: ${error}`);
}
};
@@ -115,7 +114,7 @@ const redisSocketEvents = ({
// Disconnect Events
const registerDisconnectEvents = (socket) => {
const disconnect = () => {
createLogEvent(socket, "DEBUG", `User disconnected.`);
createLogEvent(socket, "debug", `User disconnected.`);
const rooms = Array.from(socket.rooms).filter((room) => room !== socket.id);
for (const room of rooms) {
socket.leave(room);

View File

@@ -31,7 +31,7 @@ io.use(function (socket, next) {
next(new Error("Authentication error - no authorization token."));
}
} catch (error) {
console.log("Uncaught connection error:::", error);
//console.log("Uncaught connection error:::", error);
logger.log("websocket-connection-error", "error", null, null, {
token: socket.handshake.auth.token,
...error
@@ -41,7 +41,7 @@ io.use(function (socket, next) {
});
io.on("connection", (socket) => {
socket.log_level = "TRACE";
socket.log_level = "DEBUG";
createLogEvent(socket, "DEBUG", `Connected and Authenticated.`);
socket.on("set-log-level", (level) => {
@@ -75,7 +75,7 @@ io.on("connection", (socket) => {
socket.on("cdk-calculate-allocations", async (jobid, callback) => {
const allocations = await CdkCalculateAllocations(socket, jobid);
createLogEvent(socket, "DEBUG", `Allocations calculated.`);
createLogEvent(socket, "TRACE", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
createLogEvent(socket, "SILLY", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
callback(allocations);
});
@@ -85,7 +85,7 @@ io.on("connection", (socket) => {
socket.on("pbs-calculate-allocations", async (jobid, callback) => {
const allocations = await CdkCalculateAllocations(socket, jobid);
createLogEvent(socket, "DEBUG", `Allocations calculated.`);
createLogEvent(socket, "TRACE", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
createLogEvent(socket, "SILLY", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
callback(allocations);
});
@@ -103,7 +103,7 @@ io.on("connection", (socket) => {
socket.on("pbs-calculate-allocations-ap", async (billids, callback) => {
const allocations = await PbsCalculateAllocationsAp(socket, billids);
createLogEvent(socket, "DEBUG", `AP Allocations calculated.`);
createLogEvent(socket, "TRACE", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
createLogEvent(socket, "DEBUG", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
socket.apAllocations = allocations;
callback(allocations);
});
@@ -122,7 +122,7 @@ io.on("connection", (socket) => {
function createLogEvent(socket, level, message) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`);
// console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`);
socket.emit("log-event", {
timestamp: new Date(),
level,
@@ -148,7 +148,7 @@ function createLogEvent(socket, level, message) {
function createJsonEvent(socket, level, message, json) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`);
//console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`);
socket.emit("log-event", {
timestamp: new Date(),
level,
@@ -173,17 +173,17 @@ function createJsonEvent(socket, level, message, json) {
}
function createXmlEvent(socket, xml, message, isError = false) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("TRACE")) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("SILLY")) {
socket.emit("log-event", {
timestamp: new Date(),
level: isError ? "ERROR" : "TRACE",
level: isError ? "ERROR" : "SILLY",
message: `${message}: ${xml}`
});
}
logger.log(
isError ? "ws-log-event-xml-error" : "ws-log-event-xml",
isError ? "ERROR" : "TRACE",
isError ? "ERROR" : "SILLY",
socket.user.email,
socket.recordid,
{
@@ -195,7 +195,7 @@ function createXmlEvent(socket, xml, message, isError = false) {
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
timestamp: new Date(),
level: isError ? "ERROR" : "TRACE",
level: isError ? "ERROR" : "SILLY",
message,
xml
});
@@ -206,7 +206,7 @@ function LogLevelHierarchy(level) {
switch (level) {
case "XML":
return 5;
case "TRACE":
case "SILLY":
return 5;
case "DEBUG":
return 4;