Compare commits

...

18 Commits

Author SHA1 Message Date
Dave Richer
b479684fe4 feature/IO-2996-Package-Updates-Docker-Debugging - Maintenance
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-23 13:00:51 -04:00
Dave Richer
5b81912bd3 Merged in release/2024-10-18 (pull request #1828)
Release/2024-10-18 into master-AIO - IO-2971, IO-2976, IO-2977, IO-2984, IO-2985, IO-2987, IO-2988, IO-2989
2024-10-19 03:47:08 +00:00
Dave Richer
3c98a94c38 Merged in feature/IO-2976-GlobalSearch-First-link-Navigate (pull request #1826)
feature/IO-2976-GlobalSearch-First-link-Navigate - Fix global search so it goes to the first url if it is available, added an ID for rome tours.
2024-10-18 19:25:09 +00:00
Allan Carr
0ce5d9063a Merged in feature/IO-2989-jobexported-missing-translation (pull request #1823)
IO-2989 jobexported missing en_us translation

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

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

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

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

Approved-by: Dave Richer
2024-10-17 17:03:27 +00:00
Dave Richer
9ce419b949 feature/IO-2985-Production-List-View-Null-Handling - Handle Null md_production_config
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-17 11:42:03 -04:00
Allan Carr
5053816be7 IO-2984 Open in Explorer correction
encodeURL

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-16 16:38:59 -07:00
Allan Carr
30ca34ea93 IO-2971 Export Table Size limit to 10
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-10-16 13:43:25 -07:00
Dave Richer
68d1a404b3 release/2024-10-18: Fixes from Docker meeting
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-10-16 15:28:04 -04:00
23 changed files with 720 additions and 1144 deletions

0
.localstack/.gitkeep Normal file
View File

15
.vscode/launch.json vendored
View File

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

View File

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

View File

@@ -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 doesnt 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
View File

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

1
certs/id_rsa.pub Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -185,7 +185,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);

View File

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

View File

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

View File

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

View File

@@ -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,8 +76,8 @@ 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]);
useEffect(() => { useEffect(() => {
const newColumns = const newColumns =

View File

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

View File

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

View File

@@ -2845,40 +2845,52 @@
"filters_title": "", "filters_title": "",
"information": "", "information": "",
"layout": "", "layout": "",
"statistics": { "statistics": {
"jobs_in_production": "", "jobs_in_production": "",
"tasks_in_production": "", "tasks_in_production": "",
"tasks_on_board": "", "tasks_on_board": "",
"total_amount_in_production": "", "tasks_in_view": "",
"total_amount_on_board": "", "total_amount_in_production": "",
"total_hours_in_production": "", "total_amount_on_board": "",
"total_hours_on_board": "", "total_amount_in_view": "",
"total_jobs_on_board": "", "total_hours_in_production": "",
"total_lab_in_production": "", "total_hours_on_board": "",
"total_lab_on_board": "", "total_hours_in_view": "",
"total_lar_in_production": "", "total_jobs_on_board": "",
"total_lar_on_board": "" "total_jobs_in_view": "",
}, "total_lab_in_production": "",
"total_lab_on_board": "",
"total_lab_in_view": "",
"total_lar_in_production": "",
"total_lar_on_board": "",
"total_lar_in_view": ""
},
"statistics_title": "" "statistics_title": ""
}, },
"statistics": { "statistics": {
"currency_symbol": "", "currency_symbol": "",
"hours": "", "hours": "",
"jobs": "", "jobs": "",
"jobs_in_production": "", "jobs_in_production": "",
"tasks": "", "tasks": "",
"tasks_in_production": "", "tasks_in_production": "",
"tasks_on_board": "", "tasks_on_board": "",
"total_amount_in_production": "", "tasks_in_view": "",
"total_amount_on_board": "", "total_amount_in_production": "",
"total_hours_in_production": "", "total_amount_on_board": "",
"total_hours_on_board": "", "total_amount_in_view": "",
"total_jobs_on_board": "", "total_hours_in_production": "",
"total_lab_in_production": "", "total_hours_on_board": "",
"total_lab_on_board": "", "total_hours_in_view": "",
"total_lar_in_production": "", "total_jobs_on_board": "",
"total_lar_on_board": "" "total_jobs_in_view": "",
}, "total_lab_in_production": "",
"total_lab_on_board": "",
"total_lab_in_view": "",
"total_lar_in_production": "",
"total_lar_on_board": "",
"total_lar_in_view": ""
},
"successes": { "successes": {
"removed": "" "removed": ""
} }

View File

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

View File

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

View File

@@ -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
- 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,7 @@ 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
" "
# Node App: The Main IMEX API # Node App: The Main IMEX API
node-app: node-app:
@@ -134,6 +139,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

1306
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,10 +19,10 @@
"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-elasticache": "^3.675.0",
"@aws-sdk/client-secrets-manager": "^3.654.0", "@aws-sdk/client-secrets-manager": "^3.675.0",
"@aws-sdk/client-ses": "^3.654.0", "@aws-sdk/client-ses": "^3.675.0",
"@aws-sdk/credential-provider-node": "^3.654.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,35 +32,35 @@
"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", "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",