Compare commits
37 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cfe26093c | ||
|
|
1cd64ab6f1 | ||
|
|
111f280674 | ||
|
|
1ca8b2a78d | ||
|
|
cd2a7cad7f | ||
|
|
ed16156957 | ||
|
|
8dc1f7e08f | ||
|
|
2d3c13c587 | ||
|
|
5486907639 | ||
|
|
9233cef23a | ||
|
|
c16eafe892 | ||
|
|
b479684fe4 | ||
|
|
4201f61548 | ||
|
|
d04fc76840 | ||
|
|
0f84adc752 | ||
|
|
fbefd80959 | ||
|
|
6a691b54c8 | ||
|
|
fc75717d32 | ||
|
|
1459c6e993 | ||
|
|
f50292f9bf | ||
|
|
5b81912bd3 | ||
|
|
3c98a94c38 | ||
|
|
0ce5d9063a | ||
|
|
3b84e1d6ec | ||
|
|
d62f6e2116 | ||
|
|
71a26cc4ac | ||
|
|
32441e9406 | ||
|
|
e6dade1206 | ||
|
|
43d34cae07 | ||
|
|
a72a7948fe | ||
|
|
a24f6639a1 | ||
|
|
b2a0af32e9 | ||
|
|
cc58d14d32 | ||
|
|
9ce419b949 | ||
|
|
5053816be7 | ||
|
|
30ca34ea93 | ||
|
|
68d1a404b3 |
0
.localstack/.gitkeep
Normal file
0
.localstack/.gitkeep
Normal file
15
.vscode/launch.json
vendored
15
.vscode/launch.json
vendored
@@ -14,6 +14,21 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"url": "http://localhost:3000",
|
"url": "http://localhost:3000",
|
||||||
"webRoot": "${workspaceRoot}/client/src"
|
"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>/**"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ RUN npm i --no-package-lock
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Expose the port your app runs on (adjust if necessary)
|
# Expose the port your app runs on (adjust if necessary)
|
||||||
EXPOSE 4000
|
EXPOSE 4000 9229
|
||||||
|
|
||||||
# Start the application
|
# Start the application
|
||||||
CMD ["nodemon", "--legacy-watch", "server.js"]
|
CMD ["nodemon", "--legacy-watch", "--inspect=0.0.0.0:9229", "server.js"]
|
||||||
|
|||||||
@@ -4,139 +4,16 @@ This guide will walk you through the steps to configure your WSL2 (Windows Subsy
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
1. **Windows 10/11** with **WSL2** installed.
|
1. **Windows 11**
|
||||||
2. **Hyper-V** enabled on your system. If not, follow these steps to enable it:
|
2. **Docker Desktop For Windows (Latest Version)
|
||||||
- Open PowerShell as Administrator and run:
|
|
||||||
```powershell
|
|
||||||
dism.exe /Online /Enable-Feature /All /FeatureName:Microsoft-Hyper-V
|
|
||||||
```
|
|
||||||
- Restart your computer.
|
|
||||||
|
|
||||||
3. A basic understanding of networking and WSL2 configuration.
|
# Docker Setup
|
||||||
|
Inside the root of the project exists the `docker-compose.yaml` file, you can simply run
|
||||||
|
`docker-compose up` to launch the backend.
|
||||||
|
|
||||||
---
|
Things to note:
|
||||||
|
- When installing NPM packages, you will need to rebuild the `node-app` container
|
||||||
## Step 1: Create an External Hyper-V Switch
|
- Making changes to the server files will restart the `node-app`
|
||||||
|
|
||||||
1. **Open Hyper-V Manager**:
|
|
||||||
- Press `Windows Key + X`, select `Hyper-V Manager`.
|
|
||||||
|
|
||||||
2. **Create a Virtual Switch**:
|
|
||||||
- In the right-hand pane, click `Virtual Switch Manager`.
|
|
||||||
- Choose `External` and click `Create Virtual Switch`.
|
|
||||||
- Select your external network adapter (this is usually your Ethernet or Wi-Fi adapter).
|
|
||||||
- Give the switch a name (e.g., `WSL External Switch`), then click `Apply` and `OK`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 2: Configure WSL2 to Use the External Hyper-V Switch
|
|
||||||
|
|
||||||
Now that you've created the external virtual switch, follow these steps to configure your WSL2 instance to use this switch.
|
|
||||||
|
|
||||||
1. **Set WSL2 to Use the External Switch**:
|
|
||||||
- By default, WSL2 uses NAT to connect to your local network. You need to configure WSL2 to use the external Hyper-V switch instead.
|
|
||||||
|
|
||||||
2. **Check WSL2 Networking**:
|
|
||||||
- Inside WSL, run:
|
|
||||||
```bash
|
|
||||||
ip a
|
|
||||||
```
|
|
||||||
- You should see an IP address in the range of your local network (e.g., `192.168.x.x`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 3: Configure a Static IP Address for WSL2
|
|
||||||
|
|
||||||
Once WSL2 is connected to the external network, you can assign a static IP address to your WSL2 instance.
|
|
||||||
|
|
||||||
1. **Open WSL2** and Edit the Network Configuration:
|
|
||||||
- Depending on your Linux distribution, the file paths may vary, but typically for Ubuntu-based systems:
|
|
||||||
```bash
|
|
||||||
sudo nano /etc/netplan/01-netcfg.yaml
|
|
||||||
```
|
|
||||||
- If this file doesn’t exist, create a new file or use the correct configuration file path.
|
|
||||||
|
|
||||||
2. **Configure Static IP**:
|
|
||||||
- Add or update the following configuration:
|
|
||||||
```yaml
|
|
||||||
network:
|
|
||||||
version: 2
|
|
||||||
renderer: networkd
|
|
||||||
ethernets:
|
|
||||||
eth0:
|
|
||||||
dhcp4: no
|
|
||||||
addresses:
|
|
||||||
- 192.168.1.100/24 # Choose an IP address in your network range
|
|
||||||
gateway4: 192.168.1.1 # Your router's IP address
|
|
||||||
nameservers:
|
|
||||||
addresses:
|
|
||||||
- 8.8.8.8
|
|
||||||
- 8.8.4.4
|
|
||||||
```
|
|
||||||
- Adjust the values according to your local network settings:
|
|
||||||
- `addresses`: This is the static IP you want to assign.
|
|
||||||
- `gateway4`: This should be the IP address of your router.
|
|
||||||
- `nameservers`: These are DNS servers, you can use Google's public DNS or any other DNS provider.
|
|
||||||
|
|
||||||
3. **Apply the Changes**:
|
|
||||||
- Run the following command to apply the network configuration:
|
|
||||||
```bash
|
|
||||||
sudo netplan apply
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Verify the Static IP**:
|
|
||||||
- Check if the static IP is correctly set by running:
|
|
||||||
```bash
|
|
||||||
ip a
|
|
||||||
```
|
|
||||||
- You should see the static IP you configured (e.g., `192.168.1.100`) on the appropriate network interface (usually `eth0`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 4: Restart WSL2 to Apply Changes
|
|
||||||
|
|
||||||
To ensure the changes are fully applied, restart WSL2:
|
|
||||||
|
|
||||||
1. Open PowerShell or Command Prompt and run:
|
|
||||||
```powershell
|
|
||||||
wsl --shutdown
|
|
||||||
2. Then, start your WSL2 instance again.
|
|
||||||
|
|
||||||
## Step 5: Verify Connectivity
|
|
||||||
|
|
||||||
1. Check Internet and Local Network Connectivity:
|
|
||||||
- Run a ping command from within WSL to verify that it can reach the internet: ```ping 8.8.8.8```
|
|
||||||
2. Test Access from other Devices:
|
|
||||||
- If you're running services inside WSL (e.g., a web server), ensure they are accessible from other devices on your local network using the static IP address you configured (e.g., `http://192.168.1.100:4000`).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Configuring `vm.overcommit_memory` in sysctl for WSL2
|
|
||||||
|
|
||||||
To prevent memory overcommitment issues and optimize performance, you can configure the `vm.overcommit_memory` setting in WSL2. This is particularly useful when running Redis or other memory-intensive services inside WSL2, as it helps control how the Linux kernel handles memory allocation.
|
|
||||||
|
|
||||||
### 1. **Open the sysctl Configuration File**:
|
|
||||||
To set the `vm.overcommit_memory` value, you'll need to edit the sysctl configuration file. Inside your WSL2 instance, run the following command to open the `sysctl.conf` file for editing:
|
|
||||||
```bash
|
|
||||||
sudo nano /etc/sysctl.conf
|
|
||||||
```
|
|
||||||
### 2. Add the Overcommit Memory Setting:
|
|
||||||
Add the following line at the end of the file to allow memory overcommitment:
|
|
||||||
```bash
|
|
||||||
vm.overcommit_memory = 1
|
|
||||||
```
|
|
||||||
|
|
||||||
This setting tells the Linux kernel to always allow memory allocation, regardless of how much memory is available, which can prevent out-of-memory errors when running certain applications.
|
|
||||||
|
|
||||||
### 3. Apply the Changes:
|
|
||||||
After editing the file, save it and then apply the new sysctl configuration by running:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo sysctl -p
|
|
||||||
```
|
|
||||||
|
|
||||||
# Install Docker and Docker Compose in WSL2
|
|
||||||
- https://docs.docker.com/desktop/wsl/
|
|
||||||
|
|
||||||
# Local Stack
|
# Local Stack
|
||||||
- LocalStack Front end (Optional) - https://apps.microsoft.com/detail/9ntrnft9zws2?hl=en-us&gl=US
|
- LocalStack Front end (Optional) - https://apps.microsoft.com/detail/9ntrnft9zws2?hl=en-us&gl=US
|
||||||
|
|||||||
27
certs/id_rsa
Normal file
27
certs/id_rsa
Normal 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
1
certs/id_rsa.pub
Normal 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
|
||||||
@@ -9,7 +9,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { pageLimit } from "../../utils/config";
|
import { exportPageLimit } from "../../utils/config";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||||
import PayableExportAll from "../payable-export-all-button/payable-export-all-button.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
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
pagination={{ position: "top", pageSize: pageLimit }}
|
pagination={{ position: "top", pageSize: exportPageLimit }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import { pageLimit } from "../../utils/config";
|
import { exportPageLimit } from "../../utils/config";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||||
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
@@ -177,7 +177,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments,
|
|||||||
<Table
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
pagination={{ position: "top", pageSize: pageLimit }}
|
pagination={{ position: "top", pageSize: exportPageLimit }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { Button, Card, Input, Space, Table } from "antd";
|
import { Button, Card, Input, Space, Table } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { exportPageLimit } from "../../utils/config";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
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 JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
||||||
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-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 JobMarkSelectedExported from "../jobs-mark-selected-exported/jobs-mark-selected-exported";
|
||||||
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||||
@@ -201,7 +201,7 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
|
|||||||
<Table
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
pagination={{ position: "top" }}
|
pagination={{ position: "top", pageSize: exportPageLimit }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import { connect } from "react-redux";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CiecaSelect from "../../utils/Ciecaselect";
|
import CiecaSelect from "../../utils/Ciecaselect";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
||||||
import BilllineAddInventory from "../billline-add-inventory/billline-add-inventory.component";
|
import BilllineAddInventory from "../billline-add-inventory/billline-add-inventory.component";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
@@ -72,7 +72,14 @@ export function BillEnterModalLinesComponent({
|
|||||||
<BillLineSearchSelect
|
<BillLineSearchSelect
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
options={lineData}
|
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}
|
allowRemoved={form.getFieldValue("is_credit_memo") || false}
|
||||||
onSelect={(value, opt) => {
|
onSelect={(value, opt) => {
|
||||||
setFieldsValue({
|
setFieldsValue({
|
||||||
@@ -105,7 +112,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
title: t("billlines.fields.line_desc"),
|
title: t("billlines.fields.line_desc"),
|
||||||
dataIndex: "line_desc",
|
dataIndex: "line_desc",
|
||||||
editable: true,
|
editable: true,
|
||||||
|
width: "20rem",
|
||||||
formItemProps: (field) => {
|
formItemProps: (field) => {
|
||||||
return {
|
return {
|
||||||
key: `${field.index}line_desc`,
|
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"),
|
title: t("billlines.fields.quantity"),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
showSearch
|
showSearch
|
||||||
popupMatchSelectWidth={false}
|
popupMatchSelectWidth={true}
|
||||||
optionLabelProp={"name"}
|
optionLabelProp={"name"}
|
||||||
// optionFilterProp="line_desc"
|
// optionFilterProp="line_desc"
|
||||||
filterOption={(inputValue, option) => {
|
filterOption={(inputValue, option) => {
|
||||||
@@ -43,7 +43,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
|
|||||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||||
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim(),
|
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim(),
|
||||||
label: (
|
label: (
|
||||||
<>
|
<div style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
|
||||||
<span>
|
<span>
|
||||||
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
|
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
|
||||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||||
@@ -57,7 +57,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
|
|||||||
<span style={{ float: "right", paddingleft: "1rem" }}>
|
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||||
{item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``}
|
{item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</div>
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import { WarningFilled } from "@ant-design/icons";
|
import { WarningFilled } from "@ant-design/icons";
|
||||||
import { Form, Input, InputNumber, Space } from "antd";
|
import { Form, Input, InputNumber, Space } from "antd";
|
||||||
import dayjs from "../../utils/day";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
//import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component";
|
//import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component";
|
||||||
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
|
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
|
||||||
import ContractsRatesChangeButton from "../contracts-rates-change-button/contracts-rates-change-button.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 CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||||
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
import {
|
||||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
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 FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
import InputPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
import InputPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.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 }) {
|
export default function ContractFormComponent({ form, create = false, selectedJobState, selectedCar }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<FormFieldsChanged form={form} />
|
{!create && <FormFieldsChanged form={form} />}
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
{create ? null : (
|
{!create && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("contracts.fields.status")}
|
label={t("contracts.fields.status")}
|
||||||
name="status"
|
name="status"
|
||||||
@@ -50,7 +52,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo
|
|||||||
<Form.Item label={t("contracts.fields.scheduledreturn")} name="scheduledreturn">
|
<Form.Item label={t("contracts.fields.scheduledreturn")} name="scheduledreturn">
|
||||||
<FormDateTimePicker />
|
<FormDateTimePicker />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{create ? null : (
|
{!create && (
|
||||||
<Form.Item label={t("contracts.fields.actualreturn")} name="actualreturn">
|
<Form.Item label={t("contracts.fields.actualreturn")} name="actualreturn">
|
||||||
<FormDateTimePicker />
|
<FormDateTimePicker />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -122,7 +124,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo
|
|||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{create ? null : (
|
{!create && (
|
||||||
<Form.Item label={t("contracts.fields.kmend")} name="kmend">
|
<Form.Item label={t("contracts.fields.kmend")} name="kmend">
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -145,25 +147,21 @@ export default function ContractFormComponent({ form, create = false, selectedJo
|
|||||||
>
|
>
|
||||||
<CourtesyCarFuelSlider />
|
<CourtesyCarFuelSlider />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{create ? null : (
|
{!create && (
|
||||||
<Form.Item label={t("contracts.fields.fuelin")} name="fuelin" span={8}>
|
<Form.Item label={t("contracts.fields.fuelin")} name="fuelin" span={8}>
|
||||||
<CourtesyCarFuelSlider />
|
<CourtesyCarFuelSlider />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
</LayoutFormRow>
|
</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")}>
|
<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
|
<Form.Item
|
||||||
label={t("contracts.fields.driver_dlnumber")}
|
label={t("contracts.fields.driver_dlnumber")}
|
||||||
name="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(
|
const dlExpiresBeforeReturn = dayjs(form.getFieldValue("driver_dlexpiry")).isBefore(
|
||||||
dayjs(form.getFieldValue("scheduledreturn"))
|
dayjs(form.getFieldValue("scheduledreturn"))
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("contracts.fields.driver_dlexpiry")}
|
label={t("contracts.fields.driver_dlexpiry")}
|
||||||
name="driver_dlexpiry"
|
name="driver_dlexpiry"
|
||||||
@@ -204,11 +201,10 @@ export default function ContractFormComponent({ form, create = false, selectedJo
|
|||||||
<span>{t("contracts.labels.dlexpirebeforereturn")}</span>
|
<span>{t("contracts.labels.dlexpirebeforereturn")}</span>
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t("contracts.fields.driver_dlst")} name="driver_dlst">
|
<Form.Item label={t("contracts.fields.driver_dlst")} name="driver_dlst">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -315,6 +311,6 @@ export default function ContractFormComponent({ form, create = false, selectedJo
|
|||||||
<InputNumber precision={2} />
|
<InputNumber precision={2} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { notification } from "antd";
|
import { notification } from "antd";
|
||||||
|
import Axios from "axios";
|
||||||
|
import Dinero from "dinero.js";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
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 { INSERT_NEW_JOB_LINE, UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
import { selectJobLineEditModal } from "../../redux/modals/modals.selectors";
|
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 UndefinedToNull from "../../utils/undefinedtonull";
|
||||||
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
|
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({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobLineEditModal: selectJobLineEditModal,
|
jobLineEditModal: selectJobLineEditModal,
|
||||||
@@ -82,13 +82,15 @@ function JobLinesUpsertModalContainer({ jobLineEditModal, toggleModalVisible, bo
|
|||||||
variables: {
|
variables: {
|
||||||
lineId: jobLineEditModal.context.id,
|
lineId: jobLineEditModal.context.id,
|
||||||
line: {
|
line: {
|
||||||
...values,
|
...UndefinedToNull({
|
||||||
prt_dsmk_m: Dinero({
|
...values,
|
||||||
amount: Math.round(values.act_price * 100)
|
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"]
|
refetchQueries: ["GET_LINE_TICKET_BY_PK"]
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { SyncOutlined } from "@ant-design/icons";
|
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 { 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 React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import NoteUpsertModal from "../../components/note-upsert-modal/note-upsert-modal.container";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries";
|
import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
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 IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
|
||||||
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
|
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
|
||||||
import ProductionListDetailComponent from "../production-list-detail/production-list-detail.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 CardColorLegend from "./production-board-kanban-card-color-legend.component.jsx";
|
||||||
import "./production-board-kanban.styles.scss";
|
import "./production-board-kanban.styles.scss";
|
||||||
import { createBoardData } from "./production-board-kanban.utils.js";
|
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 { 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({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -185,7 +186,7 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr
|
|||||||
const cardSettings = useMemo(() => {
|
const cardSettings = useMemo(() => {
|
||||||
const kanbanSettings = associationSettings?.kanban_settings;
|
const kanbanSettings = associationSettings?.kanban_settings;
|
||||||
return mergeWithDefaults(kanbanSettings);
|
return mergeWithDefaults(kanbanSettings);
|
||||||
}, [associationSettings]);
|
}, [associationSettings?.kanban_settings]);
|
||||||
|
|
||||||
const handleSettingsChange = () => {
|
const handleSettingsChange = () => {
|
||||||
setFilter(defaultFilters);
|
setFilter(defaultFilters);
|
||||||
@@ -214,6 +215,7 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr
|
|||||||
bodyshop={bodyshop}
|
bodyshop={bodyshop}
|
||||||
data={data}
|
data={data}
|
||||||
/>
|
/>
|
||||||
|
<ProductionListPrint />
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,15 +1,66 @@
|
|||||||
|
import InstanceRenderManager from "../../../utils/instanceRenderMgr.js";
|
||||||
|
|
||||||
const statisticsItems = [
|
const statisticsItems = [
|
||||||
{ id: 0, name: "totalHrs", label: "total_hours_in_production" },
|
{ id: 0, name: "totalHrs", label: "total_hours_in_production" },
|
||||||
{ id: 1, name: "totalAmountInProduction", label: "total_amount_in_production" },
|
{ id: 1, name: "totalAmountInProduction", label: "total_amount_in_production" },
|
||||||
{ id: 2, name: "totalLAB", label: "total_lab_in_production" },
|
{ id: 2, name: "totalLAB", label: "total_lab_in_production" },
|
||||||
{ id: 3, name: "totalLAR", label: "total_lar_in_production" },
|
{ id: 3, name: "totalLAR", label: "total_lar_in_production" },
|
||||||
{ id: 4, name: "jobsInProduction", label: "jobs_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: 5,
|
||||||
{ id: 8, name: "totalLAROnBoard", label: "total_lar_on_board" },
|
name: "totalHrsOnBoard",
|
||||||
{ id: 9, name: "jobsOnBoard", label: "total_jobs_on_board" },
|
label: InstanceRenderManager({
|
||||||
{ id: 10, name: "tasksOnBoard", label: "tasks_on_board" },
|
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" }
|
{ id: 11, name: "tasksInProduction", label: "tasks_in_production" }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -21,25 +21,26 @@ export function ProductionListColumnStatus({ record, bodyshop, insertAuditTrail
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const handleSetStatus = async (e) => {
|
const handleSetStatus = async (e) => {
|
||||||
logImEXEvent("production_change_status");
|
if (bodyshop.md_ro_statuses.production_statuses.includes(record.status) && !bodyshop.md_ro_statuses.post_production_statuses.includes(record.status)) {
|
||||||
// e.stopPropagation();
|
logImEXEvent("production_change_status");
|
||||||
setLoading(true);
|
// e.stopPropagation();
|
||||||
const { key } = e;
|
setLoading(true);
|
||||||
await updateJob({
|
const { key } = e;
|
||||||
variables: {
|
await updateJob({
|
||||||
jobId: record.id,
|
variables: {
|
||||||
job: {
|
jobId: record.id,
|
||||||
status: key
|
job: {
|
||||||
|
status: key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
insertAuditTrail({
|
||||||
insertAuditTrail({
|
jobid: record.id,
|
||||||
jobid: record.id,
|
operation: AuditTrailMapping.jobstatuschange(key),
|
||||||
operation: AuditTrailMapping.jobstatuschange(key),
|
type: "jobstatuschange"
|
||||||
type: "jobstatuschange"
|
});
|
||||||
});
|
setLoading(false);
|
||||||
|
}
|
||||||
setLoading(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
|
|||||||
@@ -457,41 +457,42 @@ export function ProductionListConfigManager({
|
|||||||
value={activeView}
|
value={activeView}
|
||||||
disabled={open || isAddingNewProfile} // Disable the Select box when the popover is open or adding a new profile
|
disabled={open || isAddingNewProfile} // Disable the Select box when the popover is open or adding a new profile
|
||||||
>
|
>
|
||||||
{bodyshop.production_config
|
{bodyshop?.production_config &&
|
||||||
.slice()
|
bodyshop.production_config
|
||||||
.sort((a, b) =>
|
.slice()
|
||||||
a.name === t("production.constants.main_profile")
|
.sort((a, b) =>
|
||||||
? -1
|
a.name === t("production.constants.main_profile")
|
||||||
: b.name === t("production.constants.main_profile")
|
? -1
|
||||||
? 1
|
: b.name === t("production.constants.main_profile")
|
||||||
: 0
|
? 1
|
||||||
) //
|
: 0
|
||||||
.map((config) => (
|
) //
|
||||||
<Select.Option key={config.name} label={config.name}>
|
.map((config) => (
|
||||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
<Select.Option key={config.name} label={config.name}>
|
||||||
<span
|
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||||
style={{
|
<span
|
||||||
flex: 1,
|
style={{
|
||||||
maxWidth: "80%",
|
flex: 1,
|
||||||
marginRight: "1rem",
|
maxWidth: "80%",
|
||||||
textOverflow: "ellipsis"
|
marginRight: "1rem",
|
||||||
}}
|
textOverflow: "ellipsis"
|
||||||
>
|
}}
|
||||||
{config.name}
|
|
||||||
</span>
|
|
||||||
{config.name !== t("production.constants.main_profile") && (
|
|
||||||
<Popconfirm
|
|
||||||
placement="right"
|
|
||||||
title={t("general.labels.areyousure")}
|
|
||||||
onConfirm={() => handleTrash(config.name)}
|
|
||||||
onCancel={(e) => e.stopPropagation()}
|
|
||||||
>
|
>
|
||||||
<DeleteOutlined onClick={(e) => e.stopPropagation()} />
|
{config.name}
|
||||||
</Popconfirm>
|
</span>
|
||||||
)}
|
{config.name !== t("production.constants.main_profile") && (
|
||||||
</div>
|
<Popconfirm
|
||||||
</Select.Option>
|
placement="right"
|
||||||
))}
|
title={t("general.labels.areyousure")}
|
||||||
|
onConfirm={() => handleTrash(config.name)}
|
||||||
|
onCancel={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<DeleteOutlined onClick={(e) => e.stopPropagation()} />
|
||||||
|
</Popconfirm>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
<Select.Option key="add_new" label={t("production.labels.addnewprofile")}>
|
<Select.Option key="add_new" label={t("production.labels.addnewprofile")}>
|
||||||
<div style={{ display: "flex", alignItems: "center" }}>
|
<div style={{ display: "flex", alignItems: "center" }}>
|
||||||
<PlusOutlined style={{ marginRight: "0.5rem" }} />
|
<PlusOutlined style={{ marginRight: "0.5rem" }} />
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici
|
|||||||
|
|
||||||
const initialColumnsRef = useRef(
|
const initialColumnsRef = useRef(
|
||||||
(initialStateRef.current &&
|
(initialStateRef.current &&
|
||||||
bodyshop.production_config
|
bodyshop?.production_config
|
||||||
.find((p) => p.name === defaultView)
|
?.find((p) => p.name === defaultView)
|
||||||
?.columns.columnKeys.map((k) => {
|
?.columns.columnKeys.map((k) => {
|
||||||
return {
|
return {
|
||||||
...ProductionListColumns({
|
...ProductionListColumns({
|
||||||
@@ -76,7 +76,7 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const matchingColumnConfig = useMemo(() => {
|
const matchingColumnConfig = useMemo(() => {
|
||||||
return bodyshop.production_config.find((p) => p.name === defaultView);
|
return bodyshop?.production_config?.find((p) => p.name === defaultView);
|
||||||
}, [bodyshop.production_config, defaultView]);
|
}, [bodyshop.production_config, defaultView]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -119,7 +119,7 @@
|
|||||||
"jobclosedwithbypass": "Job was invoiced using the master bypass password. ",
|
"jobclosedwithbypass": "Job was invoiced using the master bypass password. ",
|
||||||
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
||||||
"jobdelivery": "Job intake completed. Status set to {{status}}. Actual completion is {{actual_completion}}.",
|
"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}}.",
|
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
|
||||||
"jobimported": "Job imported.",
|
"jobimported": "Job imported.",
|
||||||
"jobinproductionchange": "Job production status set to {{inproduction}}",
|
"jobinproductionchange": "Job production status set to {{inproduction}}",
|
||||||
@@ -2849,15 +2849,21 @@
|
|||||||
"jobs_in_production": "Jobs in Production",
|
"jobs_in_production": "Jobs in Production",
|
||||||
"tasks_in_production": "Tasks in Production",
|
"tasks_in_production": "Tasks in Production",
|
||||||
"tasks_on_board": "Tasks on Board",
|
"tasks_on_board": "Tasks on Board",
|
||||||
|
"tasks_in_view": "Tasks in View",
|
||||||
"total_amount_in_production": "Dollars in Production",
|
"total_amount_in_production": "Dollars in Production",
|
||||||
"total_amount_on_board": "Dollars on Board",
|
"total_amount_on_board": "Dollars on Board",
|
||||||
|
"total_amount_in_view": "Dollars in View",
|
||||||
"total_hours_in_production": "Hours in Production",
|
"total_hours_in_production": "Hours in Production",
|
||||||
"total_hours_on_board": "Hours on Board",
|
"total_hours_on_board": "Hours on Board",
|
||||||
|
"total_hours_in_view": "Hours in View",
|
||||||
"total_jobs_on_board": "Jobs on Board",
|
"total_jobs_on_board": "Jobs on Board",
|
||||||
|
"total_jobs_in_view": "Jobs in View",
|
||||||
"total_lab_in_production": "Body Hours in Production",
|
"total_lab_in_production": "Body Hours in Production",
|
||||||
"total_lab_on_board": "Body Hours on Board",
|
"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_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"
|
"statistics_title": "Statistics"
|
||||||
},
|
},
|
||||||
@@ -2869,15 +2875,21 @@
|
|||||||
"tasks": "Tasks",
|
"tasks": "Tasks",
|
||||||
"tasks_in_production": "Tasks in Production",
|
"tasks_in_production": "Tasks in Production",
|
||||||
"tasks_on_board": "Tasks on Board",
|
"tasks_on_board": "Tasks on Board",
|
||||||
|
"tasks_in_view": "Tasks in View",
|
||||||
"total_amount_in_production": "Dollars in Production",
|
"total_amount_in_production": "Dollars in Production",
|
||||||
"total_amount_on_board": "Dollars on Board",
|
"total_amount_on_board": "Dollars on Board",
|
||||||
|
"total_amount_in_view": "Dollars in View",
|
||||||
"total_hours_in_production": "Hours in Production",
|
"total_hours_in_production": "Hours in Production",
|
||||||
"total_hours_on_board": "Hours on Board",
|
"total_hours_on_board": "Hours on Board",
|
||||||
|
"total_hours_in_view": "Hours in View",
|
||||||
"total_jobs_on_board": "Jobs on Board",
|
"total_jobs_on_board": "Jobs on Board",
|
||||||
|
"total_jobs_in_view": "Jobs in View",
|
||||||
"total_lab_in_production": "Body Hours in Production",
|
"total_lab_in_production": "Body Hours in Production",
|
||||||
"total_lab_on_board": "Body Hours on Board",
|
"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_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": {
|
"successes": {
|
||||||
"removed": "Job removed from production."
|
"removed": "Job removed from production."
|
||||||
@@ -3032,6 +3044,7 @@
|
|||||||
"production_by_target_date": "Production by Target Date",
|
"production_by_target_date": "Production by Target Date",
|
||||||
"production_by_technician": "Production by Technician",
|
"production_by_technician": "Production by Technician",
|
||||||
"production_by_technician_one": "Production filtered 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",
|
"production_over_time": "Production Level over Time",
|
||||||
"psr_by_make": "Percent of Sales by Vehicle Make",
|
"psr_by_make": "Percent of Sales by Vehicle Make",
|
||||||
"purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)",
|
"purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)",
|
||||||
|
|||||||
@@ -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": "",
|
||||||
"total_amount_in_production": "",
|
"total_amount_in_production": "",
|
||||||
"total_amount_on_board": "",
|
"total_amount_on_board": "",
|
||||||
|
"total_amount_in_view": "",
|
||||||
"total_hours_in_production": "",
|
"total_hours_in_production": "",
|
||||||
"total_hours_on_board": "",
|
"total_hours_on_board": "",
|
||||||
|
"total_hours_in_view": "",
|
||||||
"total_jobs_on_board": "",
|
"total_jobs_on_board": "",
|
||||||
|
"total_jobs_in_view": "",
|
||||||
"total_lab_in_production": "",
|
"total_lab_in_production": "",
|
||||||
"total_lab_on_board": "",
|
"total_lab_on_board": "",
|
||||||
|
"total_lab_in_view": "",
|
||||||
"total_lar_in_production": "",
|
"total_lar_in_production": "",
|
||||||
"total_lar_on_board": ""
|
"total_lar_on_board": "",
|
||||||
|
"total_lar_in_view": ""
|
||||||
},
|
},
|
||||||
"statistics_title": ""
|
"statistics_title": ""
|
||||||
},
|
},
|
||||||
@@ -2869,15 +2875,21 @@
|
|||||||
"tasks": "",
|
"tasks": "",
|
||||||
"tasks_in_production": "",
|
"tasks_in_production": "",
|
||||||
"tasks_on_board": "",
|
"tasks_on_board": "",
|
||||||
|
"tasks_in_view": "",
|
||||||
"total_amount_in_production": "",
|
"total_amount_in_production": "",
|
||||||
"total_amount_on_board": "",
|
"total_amount_on_board": "",
|
||||||
|
"total_amount_in_view": "",
|
||||||
"total_hours_in_production": "",
|
"total_hours_in_production": "",
|
||||||
"total_hours_on_board": "",
|
"total_hours_on_board": "",
|
||||||
|
"total_hours_in_view": "",
|
||||||
"total_jobs_on_board": "",
|
"total_jobs_on_board": "",
|
||||||
|
"total_jobs_in_view": "",
|
||||||
"total_lab_in_production": "",
|
"total_lab_in_production": "",
|
||||||
"total_lab_on_board": "",
|
"total_lab_on_board": "",
|
||||||
|
"total_lab_in_view": "",
|
||||||
"total_lar_in_production": "",
|
"total_lar_in_production": "",
|
||||||
"total_lar_on_board": ""
|
"total_lar_on_board": "",
|
||||||
|
"total_lar_in_view": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"removed": ""
|
"removed": ""
|
||||||
@@ -3032,6 +3044,7 @@
|
|||||||
"production_by_target_date": "",
|
"production_by_target_date": "",
|
||||||
"production_by_technician": "",
|
"production_by_technician": "",
|
||||||
"production_by_technician_one": "",
|
"production_by_technician_one": "",
|
||||||
|
"production_not_production_status": "",
|
||||||
"production_over_time": "",
|
"production_over_time": "",
|
||||||
"psr_by_make": "",
|
"psr_by_make": "",
|
||||||
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2333,6 +2333,14 @@ export const TemplateList = (type, context) => {
|
|||||||
key: "production_by_technician",
|
key: "production_by_technician",
|
||||||
//idtype: "vendor",
|
//idtype: "vendor",
|
||||||
disabled: false
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
// Sometimes referred to as PageSize, this variable controls the amount of records
|
// Sometimes referred to as PageSize, this variable controls the amount of records
|
||||||
// to show on one page during pagination.
|
// to show on one page during pagination.
|
||||||
export const pageLimit = 50;
|
export const pageLimit = 50;
|
||||||
|
export const exportPageLimit = 10;
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ import { store } from "../redux/store";
|
|||||||
|
|
||||||
export function CreateExplorerLinkForJob({ jobid }) {
|
export function CreateExplorerLinkForJob({ jobid }) {
|
||||||
const bodyshop = store.getState().user.bodyshop;
|
const bodyshop = store.getState().user.bodyshop;
|
||||||
return `imexmedia://${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`;
|
return `imexmedia://`.concat(encodeURIComponent(`${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
environment:
|
environment:
|
||||||
- SERVICES=ses
|
- SERVICES=ses,secretsmanager,cloudwatch,logs
|
||||||
- DEBUG=0
|
- DEBUG=0
|
||||||
- AWS_ACCESS_KEY_ID=test
|
- AWS_ACCESS_KEY_ID=test
|
||||||
- AWS_SECRET_ACCESS_KEY=test
|
- AWS_SECRET_ACCESS_KEY=test
|
||||||
@@ -101,6 +101,10 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
localstack:
|
localstack:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
- './localstack:/tmp/localstack'
|
||||||
|
- './certs:/tmp/certs'
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- AWS_ACCESS_KEY_ID=test
|
- AWS_ACCESS_KEY_ID=test
|
||||||
- AWS_SECRET_ACCESS_KEY=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-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 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: The Main IMEX API
|
||||||
node-app:
|
node-app:
|
||||||
@@ -134,6 +140,7 @@ services:
|
|||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
ports:
|
ports:
|
||||||
- "4000:4000"
|
- "4000:4000"
|
||||||
|
- "9229:9229"
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
- node-app-npm-cache:/app/node_modules
|
- node-app-npm-cache:/app/node_modules
|
||||||
|
|||||||
1532
package-lock.json
generated
1532
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -19,10 +19,11 @@
|
|||||||
"makeitpretty": "prettier --write \"**/*.{css,js,json,jsx,scss}\""
|
"makeitpretty": "prettier --write \"**/*.{css,js,json,jsx,scss}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-elasticache": "^3.665.0",
|
"@aws-sdk/client-cloudwatch-logs": "^3.679.0",
|
||||||
"@aws-sdk/client-secrets-manager": "^3.654.0",
|
"@aws-sdk/client-elasticache": "^3.675.0",
|
||||||
"@aws-sdk/client-ses": "^3.654.0",
|
"@aws-sdk/client-secrets-manager": "^3.675.0",
|
||||||
"@aws-sdk/credential-provider-node": "^3.654.0",
|
"@aws-sdk/client-ses": "^3.675.0",
|
||||||
|
"@aws-sdk/credential-provider-node": "^3.675.0",
|
||||||
"@opensearch-project/opensearch": "^2.12.0",
|
"@opensearch-project/opensearch": "^2.12.0",
|
||||||
"@socket.io/admin-ui": "^0.5.1",
|
"@socket.io/admin-ui": "^0.5.1",
|
||||||
"@socket.io/redis-adapter": "^8.3.0",
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
@@ -32,40 +33,41 @@
|
|||||||
"bluebird": "^3.7.2",
|
"bluebird": "^3.7.2",
|
||||||
"body-parser": "^1.20.3",
|
"body-parser": "^1.20.3",
|
||||||
"canvas": "^2.11.2",
|
"canvas": "^2.11.2",
|
||||||
"chart.js": "^4.4.4",
|
"chart.js": "^4.4.5",
|
||||||
"cloudinary": "^2.5.0",
|
"cloudinary": "^2.5.1",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dinero.js": "^1.9.1",
|
"dinero.js": "^1.9.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.21.0",
|
"express": "^4.21.1",
|
||||||
"firebase-admin": "^12.5.0",
|
"firebase-admin": "^12.6.0",
|
||||||
"graphql": "^16.9.0",
|
"graphql": "^16.9.0",
|
||||||
"graphql-request": "^6.1.0",
|
"graphql-request": "^6.1.0",
|
||||||
"graylog2": "^0.2.1",
|
|
||||||
"inline-css": "^4.0.2",
|
"inline-css": "^4.0.2",
|
||||||
"intuit-oauth": "^4.1.2",
|
"intuit-oauth": "^4.1.2",
|
||||||
"ioredis": "^5.4.1",
|
"ioredis": "^5.4.1",
|
||||||
"json-2-csv": "^5.5.5",
|
"json-2-csv": "^5.5.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"moment-timezone": "^0.5.45",
|
"moment-timezone": "^0.5.46",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"node-mailjet": "^6.0.6",
|
"node-mailjet": "^6.0.6",
|
||||||
"node-persist": "^4.0.3",
|
"node-persist": "^4.0.3",
|
||||||
"nodemailer": "^6.9.15",
|
"nodemailer": "^6.9.15",
|
||||||
"phone": "^3.1.50",
|
"phone": "^3.1.51",
|
||||||
"recursive-diff": "^1.0.9",
|
"recursive-diff": "^1.0.9",
|
||||||
"redis": "^4.7.0",
|
"redis": "^4.7.0",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"soap": "^1.1.4",
|
"soap": "^1.1.5",
|
||||||
"socket.io": "^4.8.0",
|
"socket.io": "^4.8.0",
|
||||||
"socket.io-adapter": "^2.5.5",
|
"socket.io-adapter": "^2.5.5",
|
||||||
"ssh2-sftp-client": "^10.0.3",
|
"ssh2-sftp-client": "^10.0.3",
|
||||||
"twilio": "^4.23.0",
|
"twilio": "^4.23.0",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
|
"winston": "^3.15.0",
|
||||||
|
"winston-cloudwatch": "^6.3.0",
|
||||||
"xml2js": "^0.6.2",
|
"xml2js": "^0.6.2",
|
||||||
"xmlbuilder2": "^3.1.1"
|
"xmlbuilder2": "^3.1.1"
|
||||||
},
|
},
|
||||||
|
|||||||
14
server.js
14
server.js
@@ -1,5 +1,10 @@
|
|||||||
const cors = require("cors");
|
// Load environment variables THIS MUST BE AT THE TOP
|
||||||
const path = require("path");
|
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 http = require("http");
|
||||||
const Redis = require("ioredis");
|
const Redis = require("ioredis");
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
@@ -18,11 +23,6 @@ const { redisSocketEvents } = require("./server/web-sockets/redisSocketEvents");
|
|||||||
const { ElastiCacheClient, DescribeCacheClustersCommand } = require("@aws-sdk/client-elasticache");
|
const { ElastiCacheClient, DescribeCacheClustersCommand } = require("@aws-sdk/client-elasticache");
|
||||||
const { default: InstanceManager } = require("./server/utils/instanceMgr");
|
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_BASE_DELAY = 100;
|
||||||
const CLUSTER_RETRY_MAX_DELAY = 5000;
|
const CLUSTER_RETRY_MAX_DELAY = 5000;
|
||||||
const CLUSTER_RETRY_JITTER = 100;
|
const CLUSTER_RETRY_JITTER = 100;
|
||||||
@@ -174,7 +174,7 @@ const connectToRedisCluster = async () => {
|
|||||||
Math.min(CLUSTER_RETRY_BASE_DELAY + times * 50, CLUSTER_RETRY_MAX_DELAY) + Math.random() * CLUSTER_RETRY_JITTER;
|
Math.min(CLUSTER_RETRY_BASE_DELAY + times * 50, CLUSTER_RETRY_MAX_DELAY) + Math.random() * CLUSTER_RETRY_JITTER;
|
||||||
logger.log(
|
logger.log(
|
||||||
`[${process.env.NODE_ENV}] Redis cluster not yet ready. Retrying in ${delay.toFixed(2)}ms`,
|
`[${process.env.NODE_ENV}] Redis cluster not yet ready. Retrying in ${delay.toFixed(2)}ms`,
|
||||||
"ERROR",
|
"WARN",
|
||||||
"redis",
|
"redis",
|
||||||
"api"
|
"api"
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ axios.interceptors.request.use((x) => {
|
|||||||
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
|
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
|
||||||
x.url
|
x.url
|
||||||
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
|
} | ${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, "TRACE", `Raw Request: ${printable}`, x.data);
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ axios.interceptors.response.use((x) => {
|
|||||||
const socket = x.config.socket;
|
const socket = x.config.socket;
|
||||||
|
|
||||||
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`;
|
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`;
|
||||||
console.log(printable);
|
//console.log(printable);
|
||||||
CdkBase.createJsonEvent(socket, "TRACE", `Raw Response: ${printable}`, x.data);
|
CdkBase.createJsonEvent(socket, "TRACE", `Raw Response: ${printable}`, x.data);
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ axios.interceptors.request.use((x) => {
|
|||||||
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
|
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
|
||||||
x.url
|
x.url
|
||||||
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
|
} | ${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, "TRACE", `Raw Request: ${printable}`, x.data);
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ axios.interceptors.response.use((x) => {
|
|||||||
const socket = x.config.socket;
|
const socket = x.config.socket;
|
||||||
|
|
||||||
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`;
|
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`;
|
||||||
console.log(printable);
|
//console.log(printable);
|
||||||
CdkBase.createJsonEvent(socket, "TRACE", `Raw Response: ${printable}`, x.data);
|
CdkBase.createJsonEvent(socket, "TRACE", `Raw Response: ${printable}`, x.data);
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
|
|||||||
@@ -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) {
|
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 mapaAccountName = responsibilityCenters.defaults.profits.MAPA;
|
||||||
|
|
||||||
const mapaAccount = responsibilityCenters.profits.find((c) => c.name === mapaAccountName);
|
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 {
|
} 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) {
|
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;
|
const mashAccountName = responsibilityCenters.defaults.profits.MASH;
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} 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];
|
: taxCodes[taxAccountCode];
|
||||||
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
||||||
const taxAmount = Dinero(job_totals.totals.us_sales_tax_breakdown[`ty${tyCounter}Tax`]);
|
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 (taxAmount.getAmount() > 0) {
|
||||||
if (qbo) {
|
if (qbo) {
|
||||||
InvoiceLineAdd.push({
|
InvoiceLineAdd.push({
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ exports.default = async (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json(ret);
|
res.status(200).json(ret);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
logger.log("qbo-payable-create-error", "ERROR", req.user.email, {
|
logger.log("qbo-payable-create-error", "ERROR", req.user.email, {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack
|
stack: error.stack
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ exports.default = async (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json(ret);
|
res.status(200).json(ret);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
|
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack
|
stack: error.stack
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ exports.default = async (req, res) => {
|
|||||||
error?.response?.data ||
|
error?.response?.data ||
|
||||||
error?.message
|
error?.message
|
||||||
});
|
});
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
|
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack
|
stack: error.stack
|
||||||
@@ -211,7 +211,7 @@ exports.default = async (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json(ret);
|
res.status(200).json(ret);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
|
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack
|
stack: error.stack
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ require("dotenv").config({
|
|||||||
const client = require("../graphql-client/graphql-client").client;
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
|
|
||||||
exports.createAssociation = async (req, res) => {
|
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,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
@@ -31,7 +31,7 @@ exports.createAssociation = async (req, res) => {
|
|||||||
res.json(result);
|
res.json(result);
|
||||||
};
|
};
|
||||||
exports.createShop = async (req, res) => {
|
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,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
@@ -64,7 +64,7 @@ exports.createShop = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
exports.updateCounter = 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,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
@@ -91,7 +91,7 @@ exports.updateCounter = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
exports.updateShop = 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,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ exports.defaultRoute = async function (req, res) {
|
|||||||
const jobData = await QueryJobData(req, req.BearerToken, req.body.jobid);
|
const jobData = await QueryJobData(req, req.BearerToken, req.body.jobid);
|
||||||
return res.status(200).json({ data: calculateAllocations(req, jobData) });
|
return res.status(200).json({ data: calculateAllocations(req, jobData) });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
////console.log(error);
|
||||||
CdkBase.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
|
CdkBase.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
|
||||||
res.status(500).json({ 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);
|
const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid);
|
||||||
return calculateAllocations(socket, jobData);
|
return calculateAllocations(socket, jobData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
////console.log(error);
|
||||||
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
|
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -212,7 +212,7 @@ function calculateAllocations(connectionData, job) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
|
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 mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
|
||||||
|
|
||||||
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
|
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)
|
Dinero(job.job_totals.rates.mapa.total)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
//console.log("NO MAPA ACCOUNT FOUND!!");
|
////console.log("NO MAPA ACCOUNT FOUND!!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -820,7 +820,7 @@ exports.default = async (req, res) => {
|
|||||||
job: JSON.stringify({ id: job.id, ro_number: job.ro_number }),
|
job: JSON.stringify({ id: job.id, ro_number: job.ro_number }),
|
||||||
error: error.message || JSON.stringify(error)
|
error: error.message || JSON.stringify(error)
|
||||||
});
|
});
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("arms-failed-job", "ERROR", "api", job.shopid, {
|
logger.log("arms-failed-job", "ERROR", "api", job.shopid, {
|
||||||
@@ -886,7 +886,10 @@ exports.default = async (req, res) => {
|
|||||||
const [result, rawResponse, , rawRequest] = entegralResponse;
|
const [result, rawResponse, , rawRequest] = entegralResponse;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
|
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) {
|
} catch (error) {
|
||||||
//Error at the shop level.
|
//Error at the shop level.
|
||||||
|
|||||||
@@ -206,9 +206,6 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
|
|
||||||
const repairCosts = CreateCosts(job);
|
const repairCosts = CreateCosts(job);
|
||||||
|
|
||||||
if (job.ro_number === "QBD209") {
|
|
||||||
console.log("Stop here");
|
|
||||||
}
|
|
||||||
//Calculate detail only lines.
|
//Calculate detail only lines.
|
||||||
const detailAdjustments = job.joblines
|
const detailAdjustments = job.joblines
|
||||||
.filter((jl) => jl.ah_detail_line && jl.mod_lbr_ty)
|
.filter((jl) => jl.ah_detail_line && jl.mod_lbr_ty)
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ exports.default = async (req, res) => {
|
|||||||
async function getPrivateKey() {
|
async function getPrivateKey() {
|
||||||
// Connect to AWS Secrets Manager
|
// Connect to AWS Secrets Manager
|
||||||
const client = new SecretsManagerClient({ region: "ca-central-1" });
|
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);
|
logger.log("chatter-get-private-key", "DEBUG", "api", null, null);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ admin.initializeApp({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const createUser = async (req, res) => {
|
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,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
@@ -97,7 +97,7 @@ const sendPromanagerWelcomeEmail = (req, res) => {
|
|||||||
|
|
||||||
// Validate email before proceeding
|
// Validate email before proceeding
|
||||||
if (!dbUser.validemail) {
|
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.",
|
message: "User email is not valid, skipping email.",
|
||||||
email
|
email
|
||||||
});
|
});
|
||||||
@@ -107,7 +107,7 @@ const sendPromanagerWelcomeEmail = (req, res) => {
|
|||||||
// Check if the user's company is ProManager
|
// Check if the user's company is ProManager
|
||||||
const convenientCompany = dbUser.associations?.[0]?.bodyshop?.convenient_company;
|
const convenientCompany = dbUser.associations?.[0]?.bodyshop?.convenient_company;
|
||||||
if (convenientCompany !== "promanager") {
|
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.',
|
message: 'convenient_company is not "promanager", skipping email.',
|
||||||
convenientCompany
|
convenientCompany
|
||||||
});
|
});
|
||||||
@@ -141,7 +141,7 @@ const sendPromanagerWelcomeEmail = (req, res) => {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Log success and return response
|
// 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,
|
request: req.body,
|
||||||
ioadmin: true,
|
ioadmin: true,
|
||||||
emailSentTo: email
|
emailSentTo: email
|
||||||
@@ -161,7 +161,7 @@ const sendPromanagerWelcomeEmail = (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateUser = (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,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
@@ -184,7 +184,7 @@ const updateUser = (req, res) => {
|
|||||||
.then((userRecord) => {
|
.then((userRecord) => {
|
||||||
// See the UserRecord reference doc for the contents of 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,
|
userRecord,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
@@ -199,7 +199,7 @@ const updateUser = (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getUser = (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,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
@@ -321,7 +321,7 @@ module.exports = {
|
|||||||
// admin.auth().setCustomUserClaims(uid, {
|
// admin.auth().setCustomUserClaims(uid, {
|
||||||
// ioadmin: true,
|
// ioadmin: true,
|
||||||
// "https://hasura.io/jwt/claims": {
|
// "https://hasura.io/jwt/claims": {
|
||||||
// "x-hasura-default-role": "admin",
|
// "x-hasura-default-role": "debug",
|
||||||
// "x-hasura-allowed-roles": ["admin"],
|
// "x-hasura-allowed-roles": ["admin"],
|
||||||
// "x-hasura-user-id": uid,
|
// "x-hasura-user-id": uid,
|
||||||
// },
|
// },
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ exports.lightbox_credentials = async (req, res) => {
|
|||||||
|
|
||||||
res.send(response.data);
|
res.send(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
logger.log("intellipay-lightbox-credentials-error", "ERROR", req.user?.email, null, {
|
logger.log("intellipay-lightbox-credentials-error", "ERROR", req.user?.email, null, {
|
||||||
error: JSON.stringify(error)
|
error: JSON.stringify(error)
|
||||||
});
|
});
|
||||||
@@ -109,7 +109,7 @@ exports.payment_refund = async (req, res) => {
|
|||||||
|
|
||||||
res.send(response.data);
|
res.send(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
logger.log("intellipay-refund-error", "ERROR", req.user?.email, null, {
|
logger.log("intellipay-refund-error", "ERROR", req.user?.email, null, {
|
||||||
error: JSON.stringify(error)
|
error: JSON.stringify(error)
|
||||||
});
|
});
|
||||||
@@ -143,7 +143,7 @@ exports.generate_payment_url = async (req, res) => {
|
|||||||
|
|
||||||
res.send(response.data);
|
res.send(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
logger.log("intellipay-payment-url-error", "ERROR", req.user?.email, null, {
|
logger.log("intellipay-payment-url-error", "ERROR", req.user?.email, null, {
|
||||||
error: JSON.stringify(error)
|
error: JSON.stringify(error)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ exports.default = async (req, res) => {
|
|||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
||||||
ioRedis.to(getBodyshopRoom(bodyshopid)).emit("bodyshop-message", {
|
// ioRedis.to(getBodyshopRoom(bodyshopid)).emit("bodyshop-message", {
|
||||||
operationName,
|
// operationName,
|
||||||
useremail
|
// useremail
|
||||||
});
|
// });
|
||||||
|
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -866,8 +866,8 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("*** Taxable Amounts***");
|
// console.log("*** Taxable Amounts***");
|
||||||
console.table(JSON.parse(JSON.stringify(taxableAmounts)));
|
// console.table(JSON.parse(JSON.stringify(taxableAmounts)));
|
||||||
|
|
||||||
//For the taxable amounts, figure out which tax type applies.
|
//For the taxable amounts, figure out which tax type applies.
|
||||||
//Then sum up the total of that tax type and then calculate the thresholds.
|
//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;
|
const remainingTaxableAmounts = taxableAmountsByTier;
|
||||||
console.log("*** Taxable Amounts by Tier***");
|
// console.log("*** Taxable Amounts by Tier***");
|
||||||
console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));
|
// console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));
|
||||||
|
|
||||||
Object.keys(taxableAmountsByTier).forEach((taxTierKey) => {
|
Object.keys(taxableAmountsByTier).forEach((taxTierKey) => {
|
||||||
try {
|
try {
|
||||||
@@ -1030,8 +1030,8 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("*** Total Tax by Tier Amounts***");
|
// console.log("*** Total Tax by Tier Amounts***");
|
||||||
console.table(JSON.parse(JSON.stringify(totalTaxByTier)));
|
// console.table(JSON.parse(JSON.stringify(totalTaxByTier)));
|
||||||
|
|
||||||
stateTax = stateTax
|
stateTax = stateTax
|
||||||
.add(totalTaxByTier.ty1Tax)
|
.add(totalTaxByTier.ty1Tax)
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ async function AutoAddAtsIfRequired({ job, client }) {
|
|||||||
job.joblines[atsLineIndex].act_price = atsAmount;
|
job.joblines[atsLineIndex].act_price = atsAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(job.jobLines);
|
//console.log(job.jobLines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ async function OpenSearchUpdateHandler(req, res) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const response = await osClient.index(payload);
|
const response = await osClient.index(payload);
|
||||||
console.log(response.body);
|
//console.log(response.body);
|
||||||
res.status(200).json(response.body);
|
res.status(200).json(response.body);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -255,7 +255,7 @@ async function OpenSearchSearchHandler(req, res) {
|
|||||||
|
|
||||||
res.json(body);
|
res.json(body);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
//console.log(error);
|
||||||
logger.log("os-search-error", "ERROR", req.user.email, null, {
|
logger.log("os-search-error", "ERROR", req.user.email, null, {
|
||||||
error: JSON.stringify(error)
|
error: JSON.stringify(error)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
const { createCanvas } = require("canvas");
|
const { createCanvas } = require("canvas");
|
||||||
const Chart = require("chart.js/auto");
|
const Chart = require("chart.js/auto");
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
|
||||||
const { backgroundColors, borderColors } = require("./canvas-colors");
|
const { backgroundColors, borderColors } = require("./canvas-colors");
|
||||||
const { isObject, defaultsDeep, isNumber } = require("lodash");
|
const { isObject, defaultsDeep, isNumber } = require("lodash");
|
||||||
|
|
||||||
exports.canvastest = function (req, res) {
|
exports.canvastest = function (req, res) {
|
||||||
console.log("Incoming test request.", req);
|
//console.log("Incoming test request.", req);
|
||||||
res.status(200).send("OK");
|
res.status(200).send("OK");
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.canvas = function (req, res) {
|
exports.canvas = function (req, res) {
|
||||||
const { w, h, values, keys, override } = req.body;
|
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
|
// Gate required values
|
||||||
if (!values || !keys) {
|
if (!values || !keys) {
|
||||||
res.status(400).send("Missing required data");
|
res.status(400).send("Missing required data");
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ exports.receive = async (req, res) => {
|
|||||||
//Found a bodyshop - should always happen.
|
//Found a bodyshop - should always happen.
|
||||||
if (response.bodyshops[0].conversations.length === 0) {
|
if (response.bodyshops[0].conversations.length === 0) {
|
||||||
//No conversation Found, create one.
|
//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 = {
|
newMessage.conversation = {
|
||||||
data: {
|
data: {
|
||||||
bodyshopid: response.bodyshops[0].id,
|
bodyshopid: response.bodyshops[0].id,
|
||||||
@@ -56,7 +56,7 @@ exports.receive = async (req, res) => {
|
|||||||
};
|
};
|
||||||
} else if (response.bodyshops[0].conversations.length === 1) {
|
} else if (response.bodyshops[0].conversations.length === 1) {
|
||||||
//Just add it to the conversation
|
//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;
|
newMessage.conversationid = response.bodyshops[0].conversations[0].id;
|
||||||
} else {
|
} else {
|
||||||
//We should never get here.
|
//We should never get here.
|
||||||
@@ -123,7 +123,14 @@ exports.receive = async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e1) {
|
} 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);
|
res.sendStatus(500).json(e1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,109 @@
|
|||||||
const graylog2 = require("graylog2");
|
// Load environment variables THIS MUST BE AT THE TOP
|
||||||
|
const path = require("path");
|
||||||
const logger = new graylog2.graylog({
|
require("dotenv").config({
|
||||||
servers: [{ host: "logs.bodyshop.app", port: 12201 }]
|
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||||
});
|
});
|
||||||
|
|
||||||
function log(message, type, user, record, object) {
|
const InstanceManager = require("../utils/instanceMgr").default;
|
||||||
if (type !== "ioevent")
|
const winston = require("winston");
|
||||||
console.log(message, {
|
const WinstonCloudWatch = require("winston-cloudwatch");
|
||||||
type,
|
const { isString, isEmpty } = require("lodash");
|
||||||
env: process.env.NODE_ENV || "development",
|
|
||||||
user,
|
|
||||||
record,
|
|
||||||
...object
|
|
||||||
});
|
|
||||||
logger.log(message, message, {
|
|
||||||
type,
|
|
||||||
env: process.env.NODE_ENV || "development",
|
|
||||||
user,
|
|
||||||
record,
|
|
||||||
...object
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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 createProductionTransport = (level, logStreamName, filters) => {
|
||||||
|
return new WinstonCloudWatch({
|
||||||
|
level,
|
||||||
|
logStreamName: logStreamName || level,
|
||||||
|
format: levelFilter(filters || level),
|
||||||
|
...winstonCloudwatchTransportDefaults
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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, object }) => {
|
||||||
|
return `${timestamp} [${level}]: ${message} ${
|
||||||
|
user ? `| user: ${JSON.stringify(user)}` : ""
|
||||||
|
} ${record ? `| record: ${JSON.stringify(record)}` : ""} ${
|
||||||
|
object ? `| object: ${JSON.stringify(object, null, 2)}` : ""
|
||||||
|
}`;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
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,
|
||||||
|
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();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const redisSocketEvents = ({
|
|||||||
}) => {
|
}) => {
|
||||||
// Logging helper functions
|
// Logging helper functions
|
||||||
const createLogEvent = (socket, level, message) => {
|
const createLogEvent = (socket, level, message) => {
|
||||||
console.log(`[IOREDIS LOG EVENT] - ${socket?.user?.email} - ${socket.id} - ${message}`);
|
//console.log(`[IOREDIS LOG EVENT] - ${socket?.user?.email} - ${socket.id} - ${message}`);
|
||||||
logger.log("ioredis-log-event", level, socket?.user?.email, null, { wsmessage: message });
|
logger.log("ioredis-log-event", level, socket?.user?.email, null, { wsmessage: message });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ const redisSocketEvents = ({
|
|||||||
next(new Error("Authentication error - no authorization token."));
|
next(new Error("Authentication error - no authorization token."));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Uncaught connection error:::", error);
|
//console.log("Uncaught connection error:::", error);
|
||||||
logger.log("websocket-connection-error", "error", null, null, {
|
logger.log("websocket-connection-error", "error", null, null, {
|
||||||
...error
|
...error
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ io.use(function (socket, next) {
|
|||||||
next(new Error("Authentication error - no authorization token."));
|
next(new Error("Authentication error - no authorization token."));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Uncaught connection error:::", error);
|
//console.log("Uncaught connection error:::", error);
|
||||||
logger.log("websocket-connection-error", "error", null, null, {
|
logger.log("websocket-connection-error", "error", null, null, {
|
||||||
token: socket.handshake.auth.token,
|
token: socket.handshake.auth.token,
|
||||||
...error
|
...error
|
||||||
@@ -122,7 +122,7 @@ io.on("connection", (socket) => {
|
|||||||
|
|
||||||
function createLogEvent(socket, level, message) {
|
function createLogEvent(socket, level, message) {
|
||||||
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
|
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", {
|
socket.emit("log-event", {
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
level,
|
level,
|
||||||
@@ -148,7 +148,7 @@ function createLogEvent(socket, level, message) {
|
|||||||
|
|
||||||
function createJsonEvent(socket, level, message, json) {
|
function createJsonEvent(socket, level, message, json) {
|
||||||
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
|
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", {
|
socket.emit("log-event", {
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
level,
|
level,
|
||||||
|
|||||||
Reference in New Issue
Block a user