Compare commits

..

1 Commits

Author SHA1 Message Date
Allan Carr
98bff6d8f6 IO-2824 Dev Server Instance Switch
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-20 10:56:07 -07:00
607 changed files with 34974 additions and 48038 deletions

View File

@@ -5,7 +5,6 @@ orbs:
aws-s3: circleci/aws-s3@4.0.0
aws-cli: circleci/aws-cli@4.0
eb: circleci/aws-elastic-beanstalk@2.0.1
jira: circleci/jira@2.1.0
jobs:
imex-api-deploy:
docker:
@@ -19,12 +18,6 @@ jobs:
eb status --verbose
eb deploy
eb status
- jira/notify:
environment: Production (ImEX) - API
environment_type: production
job_type: deployment
pipeline_id: << pipeline.id >>
pipeline_number: << pipeline.number >>
imex-hasura-migrate:
docker:
@@ -40,16 +33,11 @@ jobs:
- run:
name: Execute migration
command: |
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
npm install hasura-cli -g
hasura migrate apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
- jira/notify:
environment: Production (ImEX) - Hasura
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
imex-app-build:
docker:
- image: cimg/node:18.18.2
@@ -74,7 +62,6 @@ jobs:
to: "s3://imex-online-production/"
arguments: "--exclude '*.map'"
imex-app-beta-build:
docker:
- image: cimg/node:18.18.2
@@ -99,12 +86,6 @@ jobs:
from: dist
to: "s3://imex-online-beta/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Production (ImEX) - Front End
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
rome-api-deploy:
docker:
@@ -118,12 +99,7 @@ jobs:
eb status --verbose
eb deploy
eb status
- jira/notify:
environment: Production (Rome) - API
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
rome-hasura-migrate:
docker:
- image: cimg/node:18.18.2
@@ -138,16 +114,11 @@ jobs:
- run:
name: Execute migration
command: |
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
npm install hasura-cli -g
hasura migrate apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
- jira/notify:
environment: Production (Rome) - Hasura
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
rome-app-build:
docker:
- image: cimg/node:18.18.2
@@ -172,12 +143,6 @@ jobs:
from: dist
to: "s3://rome-online-production/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Production (Rome) - Front End
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
promanager-app-build:
docker:
@@ -203,12 +168,6 @@ jobs:
from: dist
to: "s3://promanager-production/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Production (ProManager) - Front End
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
test-rome-hasura-migrate:
docker:
@@ -224,18 +183,10 @@ jobs:
- run:
name: Execute migration
command: |
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
npm install hasura-cli -g
hasura migrate apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
sleep 5
hasura metadata apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
sleep 10
hasura metadata reload --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
- jira/notify:
environment: Test (Rome) - Hasura
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
test-rome-app-build:
docker:
@@ -261,12 +212,6 @@ jobs:
from: dist
to: "s3://rome-online-test/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Test (Rome) - Front End
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
test-promanager-app-build:
docker:
@@ -292,12 +237,6 @@ jobs:
from: dist
to: "s3://promanager-testing/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Test (ProManager) - Front End
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
test-hasura-migrate:
docker:
@@ -313,18 +252,10 @@ jobs:
- run:
name: Execute migration
command: |
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
npm install hasura-cli -g
hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
sleep 15
hasura metadata apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
sleep 30
hasura metadata reload --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
- jira/notify:
environment: Test (ImEX) - Hasura
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
imex-test-app-build:
docker:
@@ -371,12 +302,7 @@ jobs:
from: dist
to: "s3://imex-online-test-beta/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Test (ImEX) - Front End
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
admin-app-build:
docker:
@@ -427,7 +353,7 @@ workflows:
secret: ${HASURA_PROD_SECRET}
filters:
branches:
only: master-AIO
only: master
- rome-api-deploy:
filters:
branches:
@@ -437,7 +363,7 @@ workflows:
branches:
only: master-AIO
- rome-hasura-migrate:
secret: ${HASURA_ROME_PROD_SECRET}
secret: ${HASURA_PROD_SECRET}
filters:
branches:
only: master-AIO

View File

@@ -1,24 +0,0 @@
# Directories to exclude
.circleci
.idea
.platform
.vscode
_reference
client
redis/dockerdata
hasura
node_modules
# Files to exclude
.ebignore
.editorconfig
.eslintrc.json
.gitignore
.prettierrc.js
Dockerfile
README.MD
bodyshop_translations.babel
docker-compose.yml
ecosystem.config.js
# Optional: Exclude logs and temporary files
*.log

View File

View File

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

15
.vscode/launch.json vendored
View File

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

View File

@@ -1,47 +0,0 @@
# Use Amazon Linux 2023 as the base image
FROM amazonlinux:2023
# Install Git and Node.js (Amazon Linux 2023 uses the DNF package manager)
RUN dnf install -y git \
&& curl -sL https://rpm.nodesource.com/setup_20.x | bash - \
&& dnf install -y nodejs \
&& dnf clean all
# Install dependencies required by node-canvas
RUN dnf install -y \
gcc \
gcc-c++ \
cairo-devel \
pango-devel \
libjpeg-turbo-devel \
giflib-devel \
libpng-devel \
make \
python3 \
python3-pip \
&& dnf clean all
# Set the working directory
WORKDIR /app
# This is because our test route uses a git commit hash
RUN git config --global --add safe.directory /app
# Copy package.json and package-lock.json
COPY package.json ./
# Install Nodemon
RUN npm install -g nodemon
# Install dependencies
RUN npm i --no-package-lock
# Copy the rest of your application code
COPY . .
# Expose the port your app runs on (adjust if necessary)
EXPOSE 4000 9229
# Start the application
CMD ["nodemon", "--legacy-watch", "--inspect=0.0.0.0:9229", "server.js"]

View File

@@ -2,7 +2,7 @@ NGROK TEsting:
./ngrok.exe http http://localhost:4000 -host-header="localhost:4000"
Finding deadfiles - run from client directory
npx deadfile ./src/index.jsx --exclude build templates
npx deadfile ./src/index.js --exclude build templates
#Crushing all hasura migrations by creating a new initialization from the server.
hasura migrate create "Init" --from-server --endpoint https://db.imex.online/ --admin-secret 'Production-ImEXOnline!@#'
@@ -11,4 +11,4 @@ Production-ImEXOnline!@#'
hasura migrate status --endpoint https://db.imex.online/ --admin-secret 'Production-ImEXOnline!@#'
Generate the license file:
$ generate-license-file --input package.json --output third-party-licenses.txt --overwrite
$ generate-license-file --input package.json --output third-party-licenses.txt --overwrite

View File

@@ -1,64 +0,0 @@
# Setting up External Networking and Static IP for WSL2 using Hyper-V
This guide will walk you through the steps to configure your WSL2 (Windows Subsystem for Linux) instance to use an external Hyper-V virtual switch, enabling it to connect directly to your local network. Additionally, you'll learn how to assign a static IP address to your WSL2 instance.
## Prerequisites
1. **Windows 11**
2. **Docker Desktop For Windows (Latest Version)
# 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
- Making changes to the server files will restart the `node-app`
# Local Stack
- LocalStack Front end (Optional) - https://apps.microsoft.com/detail/9ntrnft9zws2?hl=en-us&gl=US
- http://localhost:4566/_aws/ses will allow you to see emails sent
# Docker Commands
## General `docker-compose` Commands:
1. Bring up the services, force a rebuild of all services, and do not use the cache: `docker-compose up --build --no-cache`
2. Start Containers in Detached Mode: This will run the containers in the background (detached mode): `docker-compose up -d`
3. Stop and Remove Containers: Stops and removes the containers gracefully: `docker-compose down`
4. Stop containers without removing them: `docker-compose stop`
5. Remove Containers, Volumes, and Networks: `docker-compose down --volumes`
6. Force rebuild of containers: `docker-compose build --no-cache`
7. View running Containers: `docker-compose ps`
8. View a specific containers logs: `docker-compose logs <container-name>`
9. Scale services (multiple instances of a service): `docker-compose up --scale <container-name>=<instances number> -d`
10. Watch a specific containers logs in realtime with timestamps: `docker-compose logs -f --timestamps <container-name>`
## Volume Management Commands
1. List Docker volumes: `docker volume ls`
2. Remove Unused volumes `docker volume prune`
3. Remove specific volumes `docker volume rm <volume-name>`
4. Inspect a volume: `docker volume inspect <volume-name>`
## Container Image Management Commands:
1. List running containers: `docker ps`
2. List all containers: `docker os -a`
3. Remove Stopped containers: `docker container prune`
4. Remove a specific container: `docker container rm <container-name>`
5. Remove a specific image: `docker rmi <image-name>:<version>`
6. Remove all unused images: `docker image prune -a`
## Network Management Commands:
1. List networks: `docker network ls`
2. Inspect a specific network: `docker network inspect <network-name>`
3. Remove a specific network: `docker network rm <network-name>`
4. Remove unused networks: `docker network prune`
## Debugging and maintenance:
1. Enter a Running container: `docker exec -it <container name> /bin/bash` (could also be `/bin/sh` or for example `redis-cli` on a redis node)
2. View container resource usage: `docker stats`
3. Check Disk space used by Docker: `docker system df`
4. Remove all unused Data (Nuclear option): `docker system prune`
## Specific examples
1. To simulate a Clean state, one should run `docker system prune` followed by `docker volume prune -a`
2. You can run `docker-compose up` without the `-d` option, and you will get what is identical to the experience you were used to, this includes being able to control-c and bring the entire stack down

View File

@@ -1,41 +0,0 @@
# Production Board Notes:
## General Notes
- You can single click the lane footer to collapse/un-collapse the lane
- You can double click the lane header to collapse/un-collapse the lane
- If you need to scroll horizontally, you can hold shift and use the mouse scroll wheel, or press the mouse scroll wheel while scrolling
## Board Settings
#### Layout
- Board Orientation (Vertical or Horizontal)
- This determines the orientation of the card layout on the board.
- Horizontal is the default setting, and how the prior board was set up.
- Vertical is the new setting and allows lanes to be displayed vertically, with a grid of cards
- Card Size (Small, Medium, Large)
- This determines the size of the cards on the board.
- Small is the default setting, and how the prior board was set up.
- Medium and Large are new settings and allow for larger cards to be displayed on the board.
- Compact Cards (Tall or Wide)
- Formally called 'Compact'
- When on, data is displayed on the card vertically
- when turned off, some fields may share horizontal space, tightening the card layout
- Colored Cards (On or Off)
- When on, cards are colored based on the Status color
- Kiosk Mode (On or Off)
- This should be turned on if the shop is using it on a tablet (Ipad)
#### Information
These allow users to turn fields on or off, turning them all off will show the card in the most minimal form
### Statistics
- The statistics section allows users to see accumulations of both jobs on the board, and jobs in production.
- you can click a statistic to turn it on and off, and drag and drop the statistics to rearrange them
### Filters
- Allows you to set, and persist filters for estimators and insurance companies

View File

@@ -1 +0,0 @@
node_modules

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,59 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IMEX IO Extractor</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
textarea {
width: 100%;
height: 200px;
}
.output-box {
margin-top: 20px;
padding: 10px;
border: 1px solid #ccc;
background-color: #f9f9f9;
min-height: 40px;
}
.copy-button {
margin-top: 10px;
}
</style>
</head>
<body>
<h1>IMEX IO Extractor</h1>
<textarea id="inputText" placeholder="Paste your text here..."></textarea>
<br>
<button onclick="extractIO()">Extract</button>
<div class="output-box" id="outputBox" contenteditable="true"></div>
<button class="copy-button" onclick="copyToClipboard()">Copy to Clipboard</button>
<script>
function extractIO() {
const inputText = document.getElementById('inputText').value;
const ioNumbers = [...new Set(inputText.match(/IO-\d{4}/g))] // Extract unique IO-#### matches
.map(io => ({ io, num: parseInt(io.split('-')[1]) })) // Extract number part for sorting
.sort((a, b) => a.num - b.num) // Sort by the number
.map(item => item.io); // Extract sorted IO-####
document.getElementById('outputBox').innerText = ioNumbers.join(', '); // Display horizontally
}
function copyToClipboard() {
const outputBox = document.getElementById('outputBox');
const range = document.createRange();
range.selectNodeContents(outputBox);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
document.execCommand('copy');
}
</script>
</body>
</html>

View File

@@ -4,7 +4,7 @@ Clone Repository for:
{
"name": "node-webhook-scripts",
"version": "1.0.0",
"main": "index.jsx",
"main": "index.js",
"dependencies": {
"express": "^4.16.4"
},

View File

@@ -11,7 +11,7 @@ module.exports = {
{
name: "Bitbucket Webhook",
script: "./webhook/index.jsx",
script: "./webhook/index.js",
env: {
NODE_ENV: "production"
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDWzCCAkOgAwIBAgIUD/QBSAXy/AlJ/cS4DaPWJLpChxgwDQYJKoZIhvcNAQEL
BQAwPTELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMSEwHwYDVQQKDBhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQwOTA5MTU0MjA1WhcNMjUwOTA5MTU0MjA1
WjA9MQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xITAfBgNVBAoMGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AKSd0l7NJCNBwvtPU+dVPQkteg0AfC3sGqRnZMQteCRVa2oIgC4NoF3A9BK/yHbF
ZF25OnXTck5vzc8yb3v73ndfTD9ASKNoiaZE84/GFBsxqlKR8cs0qVwzuAsdijMv
vlMPNlMRyE1Rb7nR6HXGkPXNyxgMko03NXPkvIje9zRudm0Lf8L4q/hPyPkS7Mrm
/uQfAAJe+xFcupkEX2XY7r0x1C+z6E8lA1UcuhK3SHdW7CWYqp1vU5/dnnUiXwCa
GiC6Y1bCJB0pDAVISzy3JUDdINZdiqGR+y8ho3pstChf2mp/76s3N9eG9KA/qaFK
BrGk2PvCoZ8/Aj1aMsRYFHECAwEAAaNTMFEwHQYDVR0OBBYEFDLJ2fbWP4VUJgOp
PSs+NGHcVgRmMB8GA1UdIwQYMBaAFDLJ2fbWP4VUJgOpPSs+NGHcVgRmMA8GA1Ud
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABfv5ut/y03atq0NMB0jeDY4
AvW4ukk0k1svyqxFZCw9o7m2lHb/IjmVrZG1Sj4JWrrSv0s02ccb26/t6vazNa5L
Powe3eyfHgfjTZJmgs8hyeMwKS0wWk/SPuu9JDhIJakiquqD+UVBGkHpP+XYvhDv
vhS2XRlW+aEjpUmr1oCyyrc6WbzrYRNadqEsn/AxwcMyUbht3Ugjkg+OpidcTIQp
5lv63waKo6I1vQofzBQ3L7JYsKo8kC0vAP7wkLxvzBii335uZJzzpFYFVOyVNezi
dJdazPbRYbXz4LjltdEn/SNfRuKX8ZRiN2OSo7OfSrZaMTS87SfCSFJGgQM8Yrk=
-----END CERTIFICATE-----

View File

@@ -1,27 +0,0 @@
-----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-----

View File

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

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCkndJezSQjQcL7
T1PnVT0JLXoNAHwt7BqkZ2TELXgkVWtqCIAuDaBdwPQSv8h2xWRduTp103JOb83P
Mm97+953X0w/QEijaImmRPOPxhQbMapSkfHLNKlcM7gLHYozL75TDzZTEchNUW+5
0eh1xpD1zcsYDJKNNzVz5LyI3vc0bnZtC3/C+Kv4T8j5EuzK5v7kHwACXvsRXLqZ
BF9l2O69MdQvs+hPJQNVHLoSt0h3VuwlmKqdb1Of3Z51Il8AmhogumNWwiQdKQwF
SEs8tyVA3SDWXYqhkfsvIaN6bLQoX9pqf++rNzfXhvSgP6mhSgaxpNj7wqGfPwI9
WjLEWBRxAgMBAAECggEAUNpHYlLFxh9dokujPUMreF+Cy/IKDBAkQc2au5RNpyLh
YDIOqw/8TTAhcTgLQPLQygvZP9f8E7RsVLFD+pSJ/v2qmIJ9au1Edor1Sg+S/oxV
SLrwFMunx2aLpcH7iAqSI3+cQg7A3+D4zD7iOz6tIl3Su9wo+v073tFhHKTOrEwv
Qgr9Jf3viIiKV1ym+uQEVQndocfsj46FnFpXTQ2qs7kAF6FgAOLDGfQQwzkiqEBD
NsqsDmbYIx6foZL+DEz1ZVO2M5B+xxpbNK82KwuQilVpimW8ui4LZHCe+RIFzt9+
BK6KGlLpSEwTFliivI3nahy18JzskZsfyah0CPZlQQKBgQDVv+A0qIPGvOP3Sx+9
HyeQCV23SkvvSvw8p8pMB0gvwv63YdJ7N/rJzBGS6YUHFWWZZgEeTgkJ6VJvoe0r
8JL1el9uSUa7f0eayjmFBOGuzpktNVdIn2Tg7A9MWA4JqPNNC69RMOh86ewGD4J3
a8Hz2a1bHxAmy/AZt2ukypY6eQKBgQDFJ7kqeOPkRBz9WbALRgVIXo8YWf5di0sQ
r0HC03GAISHQ725A2IFBPHJWeqj0jaMiIZD0y+Obgp7KAskrJaLfsd7Ug775kFfw
oUI9UAl6kRuPKvm3BaVAm46SQm+56VsgxTi73YN0NUp75THHZgAJjepF9zSpVJxq
VY9DjEGruQKBgQCQCpGIatcCol/tUg69X7VFd0pULhkl1J5OMbQ9r9qRdRI5eg5h
QsQaIQ7mtb8TmvOwf/DY/zVQHI+U8sXlCmW+TwzoQTENQSR7xzMj1LpRFqBaustr
AR72A537kItFLzll/i3SxOam5uxK2UDOQSuerF4KPdCglGXkrpo3nt3F4QKBgQCa
RArPAOjQo7tLQfJN3+wiRFsTYtd1uphx5bA/EdOtvj8HjVFnzADXWsTchf3N3UXY
XwtdgGwIMpys1KEz8a8P+c2x26SDAj7NOmDqOMYx8Xju/WGHpBM6Cn30U6e4gK+d
ZLSPyzQgqdIuP5hDvbwpvbGiLVw3Ys1BJtGCuSxpgQJ/eHnRiuSi5Zq5jGg+GpA+
FEEc9NCy772rR+4uzEOqyIwqewffqzSuVWuIsY/8MP3fh+NDxl/mU6cB5QVeD54Z
JZUKwmpM26muiM6WvVnM4ExPdSGA2+l4pZjby/KKd6F/w0tgZ1jb9Pb2/0vN3qVA
Y4U4XNTMt2fxUACqiL4SHA==
-----END PRIVATE KEY-----

View File

@@ -1,5 +1,5 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GA_CODE=231099835
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
@@ -8,7 +8,7 @@ VITE_APP_CLOUDINARY_API_KEY=957865933348715
VITE_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4'
VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
VITE_APP_AXIOS_BASE_API_URL=/api/
VITE_APP_AXIOS_BASE_API_URL=http://localhost:4000
VITE_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_INSTANCE=IMEX

View File

@@ -1,6 +1,7 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GA_CODE=231099835
# VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
@@ -8,7 +9,7 @@ VITE_APP_CLOUDINARY_API_KEY=957865933348715
VITE_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4'
VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
VITE_APP_AXIOS_BASE_API_URL=/api/
VITE_APP_AXIOS_BASE_API_URL=http://localhost:4000
VITE_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_INSTANCE=PROMANAGER

View File

@@ -1,5 +1,5 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GA_CODE=231099835
# VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
@@ -9,7 +9,7 @@ VITE_APP_CLOUDINARY_API_KEY=957865933348715
VITE_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
VITE_APP_AXIOS_BASE_API_URL=/api/
VITE_APP_AXIOS_BASE_API_URL=http://localhost:4000
VITE_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_COUNTRY=USA

1
client/.gitignore vendored
View File

@@ -1,4 +1,3 @@
# Sentry Config File
.sentryclirc
/dev-dist

53
client/craco.config.js Normal file
View File

@@ -0,0 +1,53 @@
// craco.config.js
const TerserPlugin = require("terser-webpack-plugin");
const CracoLessPlugin = require("craco-less");
const { convertLegacyToken } = require("@ant-design/compatible/lib");
const { theme } = require("antd/lib");
const { defaultAlgorithm, defaultSeed } = theme;
const mapToken = defaultAlgorithm(defaultSeed);
const v4Token = convertLegacyToken(mapToken);
// TODO, At the moment we are using less in the Dashboard. Once we remove this we can remove the less processor entirely.
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { ...v4Token },
javascriptEnabled: true
}
}
}
}
],
webpack: {
configure: (webpackConfig) => {
return {
...webpackConfig,
// Required for Dev Server
devServer: {
...webpackConfig.devServer,
allowedHosts: "all"
},
optimization: {
...webpackConfig.optimization,
// Workaround for CircleCI bug caused by the number of CPUs shown
// https://github.com/facebook/create-react-app/issues/8320
minimizer: webpackConfig.optimization.minimizer.map((item) => {
if (item instanceof TerserPlugin) {
item.options.parallel = 2;
}
return item;
})
}
};
}
},
devtool: "source-map"
};

View File

@@ -12,6 +12,6 @@ module.exports = defineConfig({
setupNodeEvents(on, config) {
return require("./cypress/plugins/index.js")(on, config);
},
baseUrl: "https://localhost:3000"
baseUrl: "http://localhost:3000"
}
});

View File

@@ -1,6 +1,6 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.jsx can be used to load plugins
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.

View File

@@ -1,5 +1,5 @@
// ***********************************************************
// This example support/index.jsx is processed and
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and

View File

@@ -1,126 +1,174 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<% if (env.VITE_APP_INSTANCE === 'IMEX') { %>
<link rel="icon" href="/favicon.png"/>
<% } %> <% if (env.VITE_APP_INSTANCE === 'ROME') { %>
<link rel="icon" href="/ro-favicon.png"/>
<% } %> <% if (env.VITE_APP_INSTANCE === 'PROMANAGER') { %>
<link rel="icon" href="/pm/pm-favicon.ico"/>
<% } %>
<head>
<meta charset="utf-8" />
<% if (env.VITE_APP_INSTANCE === 'IMEX') { %>
<link rel="icon" href="/favicon.png" />
<% } %> <% if (env.VITE_APP_INSTANCE === 'ROME') { %>
<link rel="icon" href="/ro-favicon.png" />
<% } %> <% if (env.VITE_APP_INSTANCE === 'PROMANAGER') { %>
<link rel="icon" href="/pm/pm-favicon.ico" />
<% } %>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#1690ff"/>
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<!-- TODO:AIo Update the individual logos for each.-->
<link rel="apple-touch-icon" href="/logo192.png"/>
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#1690ff" />
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<!-- TODO:AIo Update the individual logos for each.-->
<link rel="apple-touch-icon" href="public/logo192.png" />
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<% if (env.VITE_APP_INSTANCE === 'IMEX') { %>
<meta name="description" content="ImEX Online"/>
<title>ImEX Online</title>
<script type="text/javascript">
window.$crisp = [];
window.CRISP_WEBSITE_ID = '36724f62-2eb0-4b29-9cdd-9905fb99913e';
(function () {
d = document;
s = d.createElement('script');
s.src = 'https://client.crisp.chat/l.js';
s.async = 1;
d.getElementsByTagName('head')[0].appendChild(s);
})();
</script>
<% } %> <% if (env.VITE_APP_INSTANCE === 'ROME') { %>
<meta name="description" content="Rome Online"/>
<title>Rome Online</title>
<script type="text/javascript" id="zsiqchat">
var $zoho = $zoho || {};
$zoho.salesiq = $zoho.salesiq || {
widgetcode: "siq01bb8ac617280bdacddfeb528f07734dadc64ef3f05efef9f769c1ec171af666",
values: {},
ready: function () {
}
};
var d = document;
s = d.createElement("script");
s.type = "text/javascript";
s.id = "zsiqscript";
s.defer = true;
s.src = "https://salesiq.zohopublic.com/widget";
t = d.getElementsByTagName("script")[0];
t.parentNode.insertBefore(s, t);
</script>
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<% if (env.VITE_APP_INSTANCE === 'IMEX') { %>
<meta name="description" content="ImEX Online" />
<title>ImEX Online</title>
<script type="text/javascript">
window.$crisp = [];
window.CRISP_WEBSITE_ID = '36724f62-2eb0-4b29-9cdd-9905fb99913e';
(function () {
d = document;
s = d.createElement('script');
s.src = 'https://client.crisp.chat/l.js';
s.async = 1;
d.getElementsByTagName('head')[0].appendChild(s);
})();
</script>
<% } %> <% if (env.VITE_APP_INSTANCE === 'ROME') { %>
<meta name="description" content="Rome Online" />
<title>Rome Online</title>
<% } %> <% if (env.VITE_APP_INSTANCE === 'PROMANAGER') { %>
<title>ProManager</title>
<meta name="description" content="ProManager"/>
<!--Use the below code snippet to provide real time updates to the live chat plugin without the need of copying and paste each time to your website when changes are made via PBX-->
<% } %>
<script>
!(function () {
'use strict';
var e = [
'debug',
'destroy',
'do',
'help',
'identify',
'is',
'off',
'on',
'ready',
'render',
'reset',
'safe',
'set',
];
if (window.noticeable) console.warn('Noticeable SDK code snippet loaded more than once');
else {
var n = (window.noticeable = window.noticeable || []);
<call-us-selector phonesystem-url=https://rometech.east.3cx.us:5001 party="LiveChat528346"></call-us-selector>
function t(e) {
return function () {
var t = Array.prototype.slice.call(arguments);
return t.unshift(e), n.push(t), n;
};
}
<!--Incase you don't want real time updates to the live chat plugin when options are changed, use the below code snippet. Please note that each time you change the settings you will need to copy and paste the snippet code to your website-->
<!--<call-us
phonesystem-url=https://rometech.east.3cx.us:5001
style="position:fixed;font-size:16px;line-height:17px;z-index: 99999;right: 20px; bottom: 20px;"
id="wp-live-chat-by-3CX"
minimized="true"
animation-style="noanimation"
party="LiveChat528346"
minimized-style="bubbleright"
allow-call="true"
allow-video="false"
allow-soundnotifications="true"
enable-mute="true"
enable-onmobile="true"
offline-enabled="true"
enable="true"
ignore-queueownership="false"
authentication="both"
show-operator-actual-name="true"
aknowledge-received="true"
gdpr-enabled="false"
message-userinfo-format="name"
message-dateformat="both"
lang="browser"
button-icon-type="default"
greeting-visibility="none"
greeting-offline-visibility="none"
chat-delay="2000"
enable-direct-call="true"
enable-ga="false"
></call-us>-->
<script defer src=https://downloads-global.3cx.com/downloads/livechatandtalk/v1/callus.js id="tcx-callus-js" charset="utf-8"></script>
!(function () {
for (var o = 0; o < e.length; o++) {
var r = e[o];
n[r] = t(r);
<% } %> <% if (env.VITE_APP_INSTANCE === 'PROMANAGER') { %>
<title>ProManager</title>
<meta name="description" content="ProManager" />
<% } %>
<script>
!(function () {
'use strict';
var e = [
'debug',
'destroy',
'do',
'help',
'identify',
'is',
'off',
'on',
'ready',
'render',
'reset',
'safe',
'set',
];
if (window.noticeable) console.warn('Noticeable SDK code snippet loaded more than once');
else {
var n = (window.noticeable = window.noticeable || []);
function t(e) {
return function () {
var t = Array.prototype.slice.call(arguments);
return t.unshift(e), n.push(t), n;
};
}
})(),
(function () {
var e = document.createElement('script');
(e.async = !0), (e.src = 'https://sdk.noticeable.io/l.js');
var n = document.head;
n.insertBefore(e, n.firstChild);
})();
}
})();
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
!(function () {
for (var o = 0; o < e.length; o++) {
var r = e[o];
n[r] = t(r);
}
})(),
(function () {
var e = document.createElement('script');
(e.async = !0), (e.src = 'https://sdk.noticeable.io/l.js');
var n = document.head;
n.insertBefore(e, n.firstChild);
})();
}
})();
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="src/index.jsx"></script>
</body>
<script type="module" src="src/index.jsx"></script>
</body>
</html>

22892
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,98 +2,93 @@
"name": "bodyshop",
"version": "0.2.1",
"engines": {
"node": ">=18.18.2"
"node": "18.18.2"
},
"type": "module",
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@ant-design/pro-layout": "^7.19.12",
"@apollo/client": "^3.11.8",
"@emotion/is-prop-valid": "^1.3.1",
"@fingerprintjs/fingerprintjs": "^4.5.0",
"@ant-design/compatible": "^5.1.2",
"@ant-design/pro-layout": "^7.17.16",
"@apollo/client": "^3.8.10",
"@asseinfo/react-kanban": "^2.2.0",
"@fingerprintjs/fingerprintjs": "^4.2.2",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.2.7",
"@sentry/cli": "^2.36.2",
"@sentry/react": "^7.114.0",
"@splitsoftware/splitio-react": "^1.13.0",
"@reduxjs/toolkit": "^2.2.1",
"@sentry/cli": "^2.28.6",
"@sentry/react": "^7.104.0",
"@splitsoftware/splitio-react": "^1.11.0",
"@tanem/react-nprogress": "^5.0.51",
"@vitejs/plugin-react": "^4.3.1",
"antd": "^5.20.1",
"@vitejs/plugin-react": "^4.2.1",
"antd": "^5.15.3",
"apollo-link-logger": "^2.0.1",
"apollo-link-sentry": "^3.3.0",
"autosize": "^6.0.1",
"axios": "^1.7.7",
"classnames": "^2.5.1",
"css-box-model": "^1.2.1",
"dayjs": "^1.11.13",
"axios": "^1.6.7",
"dayjs": "^1.11.10",
"dayjs-business-days2": "^1.2.2",
"dinero.js": "^1.9.1",
"dotenv": "^16.4.5",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^10.13.2",
"graphql": "^16.9.0",
"i18next": "^23.15.1",
"i18next-browser-languagedetector": "^8.0.0",
"immutability-helper": "^3.1.1",
"libphonenumber-js": "^1.11.9",
"logrocket": "^8.1.2",
"markerjs2": "^2.32.2",
"memoize-one": "^6.0.0",
"normalize-url": "^8.0.1",
"object-hash": "^3.0.0",
"firebase": "^10.8.1",
"graphql": "^16.6.0",
"i18next": "^23.10.0",
"i18next-browser-languagedetector": "^7.0.2",
"libphonenumber-js": "^1.10.57",
"logrocket": "^8.0.1",
"markerjs2": "^2.32.0",
"normalize-url": "^8.0.0",
"prop-types": "^15.8.1",
"query-string": "^9.1.0",
"raf-schd": "^4.0.3",
"react": "^18.3.1",
"react-big-calendar": "^1.14.1",
"query-string": "^9.0.0",
"react": "^18.2.0",
"react-big-calendar": "^1.11.0",
"react-color": "^2.19.3",
"react-cookie": "^7.2.0",
"react-dom": "^18.3.1",
"react-cookie": "^7.1.0",
"react-dom": "^18.2.0",
"react-drag-listview": "^2.0.0",
"react-grid-gallery": "^1.0.1",
"react-grid-gallery": "^1.0.0",
"react-grid-layout": "1.3.4",
"react-i18next": "^14.1.3",
"react-icons": "^5.3.0",
"react-i18next": "^14.0.5",
"react-icons": "^5.0.1",
"react-image-lightbox": "^5.1.4",
"react-joyride": "^2.7.4",
"react-markdown": "^9.0.1",
"react-number-format": "^5.4.2",
"react-popopo": "^2.1.9",
"react-product-fruits": "^2.2.61",
"react-redux": "^9.1.2",
"react-number-format": "^5.3.3",
"react-product-fruits": "^2.2.6",
"react-redux": "^9.1.0",
"react-resizable": "^3.0.5",
"react-router-dom": "^6.26.2",
"react-router-dom": "^6.22.2",
"react-scripts": "^5.0.1",
"react-sticky": "^6.0.3",
"react-virtualized": "^9.22.5",
"react-virtuoso": "^4.10.4",
"recharts": "^2.12.7",
"recharts": "^2.12.2",
"redux": "^5.0.1",
"redux-actions": "^3.0.3",
"redux-persist": "^6.0.0",
"redux-saga": "^1.3.0",
"redux-state-sync": "^3.1.4",
"reselect": "^5.1.1",
"sass": "^1.79.3",
"socket.io-client": "^4.8.0",
"styled-components": "^6.1.13",
"reselect": "^5.1.0",
"sass": "^1.71.1",
"socket.io-client": "^4.7.4",
"styled-components": "^6.1.8",
"subscriptions-transport-ws": "^0.11.0",
"use-memo-one": "^1.1.3",
"userpilot": "^1.3.6",
"terser-webpack-plugin": "^5.3.10",
"userpilot": "^1.3.1",
"vite-plugin-ejs": "^1.7.0",
"web-vitals": "^3.5.2"
"web-vitals": "^3.5.2",
"workbox-core": "^7.0.0",
"workbox-expiration": "^7.0.0",
"workbox-navigation-preload": "^7.0.0",
"workbox-precaching": "^7.0.0",
"workbox-routing": "^7.0.0",
"workbox-strategies": "^7.0.0"
},
"scripts": {
"postinstall": "echo 'when updating react-big-calendar, remember to check to localizer in the calendar wrapper'",
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "vite",
"build": "dotenvx run --env-file=.env.development.imex -- vite build",
"build": "vite build",
"start:imex": "dotenvx run --env-file=.env.development.imex -- vite",
"start:rome": "dotenvx run --env-file=.env.development.rome -- vite",
"start:promanager": "dotenvx run --env-file=.env.development.promanager -- vite",
"preview:imex": "dotenvx run --env-file=.env.development.imex -- vite preview",
"preview:rome": "dotenvx run --env-file=.env.development.rome -- vite preview",
"preview:promanager": "dotenvx run --env-file=.env.development.promanager -- vite preview",
"build:test:imex": "env-cmd -f .env.test.imex npm run build",
"build:test:rome": "env-cmd -f .env.test.rome npm run build",
"build:test:promanager": "env-cmd -f .env.test.promanager npm run build",
@@ -133,31 +128,32 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-react": "^7.24.7",
"@dotenvx/dotenvx": "^1.14.1",
"@emotion/babel-plugin": "^11.12.0",
"@emotion/react": "^11.13.3",
"@sentry/webpack-plugin": "^2.22.4",
"@testing-library/cypress": "^10.0.2",
"browserslist": "^4.23.3",
"@babel/preset-react": "^7.23.3",
"@dotenvx/dotenvx": "^0.15.4",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/react": "^11.11.3",
"@sentry/webpack-plugin": "^2.14.2",
"@swc/core": "^1.3.107",
"@swc/plugin-styled-components": "^1.5.108",
"@testing-library/cypress": "^10.0.1",
"browserslist": "^4.22.3",
"browserslist-to-esbuild": "^2.1.1",
"chalk": "^5.3.0",
"cross-env": "^7.0.3",
"cypress": "^13.14.2",
"cypress": "^13.6.6",
"eslint": "^8.57.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-cypress": "^2.15.1",
"memfs": "^4.12.0",
"memfs": "^4.6.0",
"os-browserify": "^0.3.0",
"react-error-overlay": "6.0.11",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.3",
"vite": "^5.4.7",
"vite": "^5.0.11",
"vite-plugin-babel": "^1.2.0",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-pwa": "^0.20.5",
"vite-plugin-style-import": "^2.0.0",
"workbox-window": "^7.1.0"
"vite-plugin-legacy": "^2.1.0",
"vite-plugin-node-polyfills": "^0.19.0",
"vite-plugin-pwa": "^0.19.0",
"vite-plugin-style-import": "^2.0.0"
}
}

View File

@@ -16422,7 +16422,7 @@ For when you don't want to write the same thing over and over to cache a method
$ npm install --save-dev stubs
```
```js
var mylib = require('./lib/index.jsx')
var mylib = require('./lib/index.js')
var stubs = require('stubs')
// make it a noop

View File

@@ -16567,7 +16567,7 @@ even more slower.
## Benchmarks
```bash
$ node benchmarks/index.jsx
$ node benchmarks/index.js
Benchmarking: sign
elliptic#sign x 262 ops/sec ±0.51% (177 runs sampled)
eccjs#sign x 55.91 ops/sec ±0.90% (144 runs sampled)

View File

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

View File

@@ -7,7 +7,9 @@ import { connect } from "react-redux";
import { Route, Routes } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
import ErrorBoundary from "../components/error-boundary/error-boundary.component"; // Component Imports
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
//Component Imports
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
import LandingPage from "../pages/landing/landing.page";
@@ -18,24 +20,23 @@ import { checkUserSession } from "../redux/user/user.actions";
import { selectBodyshop, selectCurrentEula, selectCurrentUser } from "../redux/user/user.selectors";
import PrivateRoute from "../components/PrivateRoute";
import "./App.styles.scss";
import handleBeta from "../utils/betaHandler";
import Eula from "../components/eula/eula.component";
import InstanceRenderMgr from "../utils/instanceRenderMgr";
import ProductFruitsWrapper from "./ProductFruitsWrapper.jsx";
import { SocketProvider } from "../contexts/SocketIO/socketContext.jsx";
import { ProductFruits } from "react-product-fruits";
const ResetPassword = lazy(() => import("../pages/reset-password/reset-password.component"));
const ManagePage = lazy(() => import("../pages/manage/manage.page.container"));
const SignInPage = lazy(() => import("../pages/sign-in/sign-in.page"));
const CsiPage = lazy(() => import("../pages/csi/csi.container.page"));
const MobilePaymentContainer = lazy(() => import("../pages/mobile-payment/mobile-payment.container"));
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
online: selectOnline,
bodyshop: selectBodyshop,
currentEula: selectCurrentEula
});
const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession()),
setOnline: (isOnline) => dispatch(setOnline(isOnline))
@@ -59,11 +60,11 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
// Associate event listeners, memoize to prevent multiple listeners being added
useEffect(() => {
const offlineListener = () => {
const offlineListener = (e) => {
setOnline(false);
};
const onlineListener = () => {
const onlineListener = (e) => {
setOnline(true);
};
@@ -97,7 +98,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
InstanceRenderMgr({
imex: "gvfvfw/bodyshopapp",
rome: "rome-online/rome-online",
promanager: "" // TODO: AIO Add in log rocket for promanager instances.
promanager: "" //TODO:AIO Add in log rocket for promanager instances.
})
);
}
@@ -108,20 +109,26 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
return <LoadingSpinner message={t("general.labels.loggingin")} />;
}
if (!online) {
handleBeta();
if (!online)
return (
<Result
status="warning"
title={t("general.labels.nointernet")}
subTitle={t("general.labels.nointernet_sub")}
extra={
<Button type="primary" onClick={() => window.location.reload()}>
<Button
type="primary"
onClick={() => {
window.location.reload();
}}
>
{t("general.actions.refresh")}
</Button>
}
/>
);
}
if (currentEula && !currentUser.eulaIsAccepted) {
return <Eula />;
@@ -140,13 +147,18 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
/>
}
>
<ProductFruitsWrapper
currentUser={currentUser}
<ProductFruits
workspaceCode={InstanceRenderMgr({
imex: null,
rome: "9BkbEseqNqxw8jUH",
promanager: "aoJoEifvezYI0Z0P"
})}
debug
language="en"
user={{
email: currentUser.email,
username: currentUser.email
}}
/>
<Routes>
@@ -202,9 +214,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
path="/manage/*"
element={
<ErrorBoundary>
<SocketProvider bodyshop={bodyshop}>
<PrivateRoute isAuthorized={currentUser.authorized} />
</SocketProvider>
<PrivateRoute isAuthorized={currentUser.authorized} />
</ErrorBoundary>
}
>
@@ -214,9 +224,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
path="/tech/*"
element={
<ErrorBoundary>
<SocketProvider bodyshop={bodyshop}>
<PrivateRoute isAuthorized={currentUser.authorized} />
</SocketProvider>
<PrivateRoute isAuthorized={currentUser.authorized} />
</ErrorBoundary>
}
>

View File

@@ -161,15 +161,3 @@
.rowWithColor > td {
background-color: var(--bgColor) !important;
}
.muted-button {
color: lightgray;
border: none;
background: none;
cursor: pointer;
font-size: 16px; /* Adjust as needed */
}
.muted-button:hover {
color: darkgrey;
}

View File

@@ -1,32 +0,0 @@
import React from "react";
import { ProductFruits } from "react-product-fruits";
import PropTypes from "prop-types";
const ProductFruitsWrapper = React.memo(({ currentUser, workspaceCode }) => {
return (
workspaceCode &&
currentUser?.authorized === true &&
currentUser?.email && (
<ProductFruits
lifeCycle="unmount"
workspaceCode={workspaceCode}
debug
language="en"
user={{
email: currentUser.email,
username: currentUser.email
}}
/>
)
);
});
export default ProductFruitsWrapper;
ProductFruitsWrapper.propTypes = {
currentUser: PropTypes.shape({
authorized: PropTypes.bool,
email: PropTypes.string
}),
workspaceCode: PropTypes.string
};

View File

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

View File

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

View File

@@ -1,19 +1,18 @@
import { Button, Card, Input, Space, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { exportPageLimit } from "../../utils/config";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
import JobMarkSelectedExported from "../jobs-mark-selected-exported/jobs-mark-selected-exported";
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 OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
@@ -171,22 +170,13 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
extra={
<Space wrap>
{!bodyshop.cdk_dealerid && !bodyshop.pbs_serialnumber && (
<>
<JobMarkSelectedExported
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
refetch={refetch}
/>
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
refetch={refetch}
/>
</>
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
refetch={refetch}
/>
)}
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && <QboAuthorizeComponent />}
<Input.Search
@@ -201,7 +191,7 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
<Table
loading={loading}
dataSource={dataSource}
pagination={{ position: "top", pageSize: exportPageLimit }}
pagination={{ position: "top" }}
columns={columns}
rowKey="id"
onChange={handleTableChange}

View File

@@ -1,6 +1,6 @@
import { PageHeader } from "@ant-design/pro-layout";
import { useMutation, useQuery } from "@apollo/client";
import { Button, Divider, Form, Popconfirm, Space } from "antd";
import dayjs from "../../utils/day";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -13,7 +13,6 @@ import { insertAuditTrail } from "../../redux/application/application.actions";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import dayjs from "../../utils/day";
import AlertComponent from "../alert/alert.component";
import BillFormContainer from "../bill-form/bill-form.container";
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
@@ -23,6 +22,7 @@ import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-galler
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import BillDetailEditReturn from "./bill-detail-edit-return.component";
import { PageHeader } from "@ant-design/pro-layout";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -98,7 +98,7 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
});
billlines.forEach((billline) => {
const { deductedfromlbr, inventories, jobline, original_actual_price, create_ppc, ...il } = billline;
const { deductedfromlbr, inventories, jobline, ...il } = billline;
delete il.__typename;
if (il.id) {
@@ -153,7 +153,6 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
const exported = data && data.bills_by_pk && data.bills_by_pk.exported;
const isinhouse = data && data.bills_by_pk && data.bills_by_pk.isinhouse;
return (
<>
@@ -189,7 +188,7 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
}
/>
<Form form={form} onFinish={handleFinish} initialValues={transformData(data)} layout="vertical">
<BillFormContainer form={form} billEdit disabled={exported} disableInHouse={isinhouse} />
<BillFormContainer form={form} billEdit disabled={exported} />
<Divider orientation="left">{t("general.labels.media")}</Divider>
{bodyshop.uselocalmediaserver ? (
<JobsDocumentsLocalGallery

View File

@@ -14,6 +14,7 @@ import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import AlertComponent from "../alert/alert.component";
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobSearchSelect from "../job-search-select/job-search-select.component";
@@ -21,7 +22,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import BillFormLines from "./bill-form.lines.component";
import { CalculateBillTotal } from "./bill-form.totals.utility";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -41,8 +41,7 @@ export function BillFormComponent({
job,
loadOutstandingReturns,
loadInventory,
preferredMake,
disableInHouse
preferredMake
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -178,7 +177,7 @@ export function BillFormComponent({
]}
>
<VendorSearchSelect
disabled={disabled || disableInHouse}
disabled={disabled}
options={vendorAutoCompleteOptions}
preferredMake={preferredMake}
onSelect={handleVendorSelect}
@@ -244,7 +243,7 @@ export function BillFormComponent({
})
]}
>
<Input disabled={disabled || disableInvNumber || disableInHouse} />
<Input disabled={disabled || disableInvNumber} />
</Form.Item>
<Form.Item
label={t("bills.fields.date")}
@@ -276,7 +275,7 @@ export function BillFormComponent({
})
]}
>
<DateTimePicker isDateOnly disabled={disabled} />
<FormDatePicker disabled={disabled} />
</Form.Item>
<Form.Item
label={t("bills.fields.is_credit_memo")}

View File

@@ -16,7 +16,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
export function BillFormContainer({ bodyshop, form, billEdit, disabled, disableInvNumber, disableInHouse }) {
export function BillFormContainer({ bodyshop, form, billEdit, disabled, disableInvNumber }) {
const {
treatments: { Simple_Inventory }
} = useSplitTreatments({
@@ -47,7 +47,6 @@ export function BillFormContainer({ bodyshop, form, billEdit, disabled, disableI
job={lineData ? lineData.jobs_by_pk : null}
responsibilityCenters={bodyshop.md_responsibility_centers || null}
disableInvNumber={disableInvNumber}
disableInHouse={disableInHouse}
loadOutstandingReturns={loadOutstandingReturns}
loadInventory={loadInventory}
preferredMake={lineData ? lineData.jobs_by_pk.v_make_desc : null}

View File

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

View File

@@ -3,6 +3,8 @@ import React, { forwardRef } from "react";
import { useTranslation } from "react-i18next";
import InstanceRenderMgr from "../../utils/instanceRenderMgr";
//To be used as a form element only.
const { Option } = Select;
const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps }, ref) => {
const { t } = useTranslation();
@@ -11,7 +13,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
disabled={disabled}
ref={ref}
showSearch
popupMatchSelectWidth={true}
popupMatchSelectWidth={false}
optionLabelProp={"name"}
// optionFilterProp="line_desc"
filterOption={(inputValue, option) => {
@@ -43,7 +45,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
item.oem_partno ? ` - ${item.oem_partno}` : ""
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim(),
label: (
<div style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
<>
<span>
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
@@ -57,7 +59,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
<span style={{ float: "right", paddingleft: "1rem" }}>
{item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``}
</span>
</div>
</>
)
}))
]}

View File

@@ -1,6 +1,6 @@
import { DeleteFilled, CopyFilled } from "@ant-design/icons";
import { DeleteFilled } from "@ant-design/icons";
import { useLazyQuery, useMutation } from "@apollo/client";
import { Button, Card, Col, Form, Input, Row, Space, Spin, Statistic, message, notification } from "antd";
import { Button, Card, Col, Form, Input, Row, Space, Spin, Statistic, notification } from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -14,12 +14,10 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
import { getCurrentUser } from "../../firebase/firebase.utils";
const mapStateToProps = createStructuredSelector({
cardPaymentModal: selectCardPayment,
bodyshop: selectBodyshop,
currentUser: getCurrentUser
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
@@ -27,17 +25,11 @@ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment"))
});
const CardPaymentModalComponent = ({
bodyshop,
currentUser,
cardPaymentModal,
toggleModalVisible,
insertAuditTrail
}) => {
const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisible, insertAuditTrail }) => {
const { context, actions } = cardPaymentModal;
const [form] = Form.useForm();
const [paymentLink, setPaymentLink] = useState();
const [loading, setLoading] = useState(false);
// const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
@@ -45,7 +37,7 @@ const CardPaymentModalComponent = ({
const [, { data, refetch, queryLoading }] = useLazyQuery(QUERY_RO_AND_OWNER_BY_JOB_PKS, {
variables: { jobids: [context.jobid] },
skip: !context?.jobid
skip: true
});
//Initialize the intellipay window.
@@ -59,7 +51,8 @@ const CardPaymentModalComponent = ({
//2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
//Add a slight delay to allow the refetch to properly get the data.
setTimeout(() => {
if (actions && actions.refetch && typeof actions.refetch === "function") actions.refetch();
if (actions && actions.refetch && typeof actions.refetch === "function")
actions.refetch();
setLoading(false);
toggleModalVisible();
}, 750);
@@ -93,6 +86,7 @@ const CardPaymentModalComponent = ({
});
};
const handleIntelliPayCharge = async () => {
setLoading(true);
//Validate
@@ -107,7 +101,7 @@ const CardPaymentModalComponent = ({
const response = await axios.post("/intellipay/lightbox_credentials", {
bodyshop,
refresh: !!window.intellipay,
paymentSplitMeta: form.getFieldsValue()
paymentSplitMeta: form.getFieldsValue(),
});
if (window.intellipay) {
@@ -132,42 +126,6 @@ const CardPaymentModalComponent = ({
}
};
const handleIntelliPayChargeShortLink = async () => {
setLoading(true);
//Validate
try {
await form.validateFields();
} catch (error) {
setLoading(false);
return;
}
try {
const { payments } = form.getFieldsValue();
const response = await axios.post("/intellipay/generate_payment_url", {
bodyshop,
amount: payments?.reduce((acc, val) => {
return acc + (val?.amount || 0);
}, 0),
account: payments && data && data.jobs.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null,
comment: btoa(JSON.stringify({ payments, userEmail: currentUser.email })),
paymentSplitMeta: form.getFieldsValue()
});
if (response.data) {
setPaymentLink(response.data?.shorUrl);
navigator.clipboard.writeText(response.data?.shorUrl);
message.success(t("general.actions.copied"));
}
setLoading(false);
} catch (error) {
notification.open({
type: "error",
message: t("job_payments.notifications.error.openingip")
});
setLoading(false);
}
};
return (
<Card title="Card Payment">
<Spin spinning={loading}>
@@ -244,14 +202,16 @@ const CardPaymentModalComponent = ({
<Form.Item
shouldUpdate={(prevValues, curValues) =>
prevValues.payments?.map((p) => p?.jobid + p?.amount).join() !==
curValues.payments?.map((p) => p?.jobid + p?.amount).join()
prevValues.payments?.map((p) => p?.jobid).join() !== curValues.payments?.map((p) => p?.jobid).join()
}
>
{() => {
//If all of the job ids have been fileld in, then query and update the IP field.
const { payments } = form.getFieldsValue();
if (payments?.length > 0 && payments?.filter((p) => p?.jobid).length === payments?.length) {
if (
payments?.length > 0 &&
payments?.filter((p) => p?.jobid).length === payments?.length
) {
refetch({ jobids: payments.map((p) => p.jobid) });
}
return (
@@ -286,6 +246,7 @@ const CardPaymentModalComponent = ({
const totalAmountToCharge = payments?.reduce((acc, val) => {
return acc + (val?.amount || 0);
}, 0);
return (
<Space style={{ float: "right" }}>
<Statistic title="Amount To Charge" value={totalAmountToCharge} precision={2} />
@@ -312,36 +273,11 @@ const CardPaymentModalComponent = ({
>
{t("job_payments.buttons.proceedtopayment")}
</Button>
<Space direction="vertical" align="center">
<Button
type="primary"
// data-ipayname="submit"
className="ipayfield"
loading={queryLoading || loading}
disabled={!(totalAmountToCharge > 0)}
onClick={handleIntelliPayChargeShortLink}
>
{t("job_payments.buttons.create_short_link")}
</Button>
</Space>
</Space>
);
}}
</Form.Item>
</Form>
{paymentLink && (
<Space
style={{ cursor: "pointer", float: "right" }}
align="end"
onClick={() => {
navigator.clipboard.writeText(paymentLink);
message.success(t("general.actions.copied"));
}}
>
<div>{paymentLink}</div>
<CopyFilled />
</Space>
)}
</Spin>
</Card>
);

View File

@@ -45,7 +45,7 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
if (fcmToken) {
setpollInterval(0);
} else {
setpollInterval(90000);
setpollInterval(60000);
}
}, [fcmToken]);

View File

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

View File

@@ -10,12 +10,16 @@ import { DateFormatter } from "../../utils/DateFormatter";
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
import CourtesyCarReadiness from "../courtesy-car-readiness-select/courtesy-car-readiness-select.component";
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
export default function CourtesyCarCreateFormComponent({ form, saveLoading, newCC }) {
export default function CourtesyCarCreateFormComponent({
form,
saveLoading,
newCC,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -157,16 +161,16 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
<Input />
</Form.Item>
<Form.Item label={t("courtesycars.fields.purchasedate")} name="purchasedate">
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item label={t("courtesycars.fields.servicestartdate")} name="servicestartdate">
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item label={t("courtesycars.fields.serviceenddate")} name="serviceenddate">
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item label={t("courtesycars.fields.leaseenddate")} name="leaseenddate">
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
</LayoutFormRow>
@@ -224,7 +228,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
</div>
<div>
<Form.Item label={t("courtesycars.fields.nextservicedate")} name="nextservicedate">
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item shouldUpdate={(p, c) => p.nextservicedate !== c.nextservicedate}>
{() => {
@@ -256,7 +260,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
</Form.Item>
<div>
<Form.Item label={t("courtesycars.fields.registrationexpires")} name="registrationexpires">
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item shouldUpdate={(p, c) => p.registrationexpires !== c.registrationexpires}>
{() => {
@@ -289,7 +293,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
}
]}
>
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item shouldUpdate={(p, c) => p.insuranceexpires !== c.insuranceexpires}>
{() => {

View File

@@ -2,7 +2,7 @@ import { Form, InputNumber } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
export default function CourtesyCarReturnModalComponent() {
const { t } = useTranslation();
@@ -19,7 +19,7 @@ export default function CourtesyCarReturnModalComponent() {
}
]}
>
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item
label={t("contracts.fields.kmend")}

View File

@@ -9,6 +9,7 @@ import axios from "axios";
const fortyFiveDaysAgo = () => dayjs().subtract(45, "day").toLocaleString();
export default function JobLifecycleDashboardComponent({ data, bodyshop, ...cardProps }) {
console.log("🚀 ~ JobLifecycleDashboardComponent ~ bodyshop:", bodyshop);
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [lifecycleData, setLifecycleData] = useState(null);
@@ -142,7 +143,7 @@ export default function JobLifecycleDashboardComponent({ data, bodyshop, ...card
>
<div>
{lifecycleData.summations.map((key) => (
<Tag key={key.status} color={key.color} style={{ width: "13vh", padding: "4px", margin: "4px" }}>
<Tag color={key.color} style={{ width: "13vh", padding: "4px", margin: "4px" }}>
<div
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
@@ -164,7 +165,6 @@ export default function JobLifecycleDashboardComponent({ data, bodyshop, ...card
size="small"
pagination={false}
columns={columns}
rowKey={(record) => record.status}
dataSource={lifecycleData.summations.sort((a, b) => b.value - a.value).slice(0, 3)}
/>
</Card>

View File

@@ -36,6 +36,9 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
};
});
console.log("Scheduled Out Today");
console.dir(scheduledOutToday);
const tvFontSize = 18;
const tvFontWeight = "bold";
@@ -86,6 +89,8 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
sorter: (a, b) => alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
sortOrder: state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => {
console.log("Render record out today");
console.dir(record);
return record.ownerid ? (
<Link to={"/manage/owners/" + record.ownerid} onClick={(e) => e.stopPropagation()}>
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>

View File

@@ -40,11 +40,13 @@ export function DmsLogEvents({ socket, logs, bodyshop }) {
function LogLevelHierarchy(level) {
switch (level) {
case "TRACE":
return "pink";
case "DEBUG":
return "orange";
case "INFO":
return "blue";
case "WARN":
case "WARNING":
return "yellow";
case "ERROR":
return "red";

View File

@@ -14,6 +14,7 @@ import {
Typography
} from "antd";
import Dinero from "dinero.js";
import dayjs from "../../utils/day";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -21,12 +22,11 @@ import { createStructuredSelector } from "reselect";
import { determineDmsType } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import i18n from "../../translations/i18n";
import dayjs from "../../utils/day";
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -89,7 +89,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
job.area_of_damage && job.area_of_damage.impact1
? " " +
t("jobs.labels.dms.damageto", {
area_of_damage: (job.area_of_damage && job.area_of_damage.impact1.padStart(2, "0")) || "UNKNOWN"
area_of_damage: (job.area_of_damage && job.area_of_damage.impact1) || "UNKNOWN"
})
: ""
}`.slice(0, 239),
@@ -164,7 +164,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
<Input disabled />
</Form.Item>
<Form.Item name="inservicedate" label={t("jobs.fields.dms.inservicedate")}>
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
</LayoutFormRow>
<Space>

View File

@@ -4,14 +4,14 @@ import Markdown from "react-markdown";
import { createStructuredSelector } from "reselect";
import { selectCurrentEula, selectCurrentUser } from "../../redux/user/user.selectors";
import { connect } from "react-redux";
import { FormDatePicker } from "../form-date-picker/form-date-picker.component";
import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries";
import { useMutation } from "@apollo/client";
import { acceptEula } from "../../redux/user/user.actions";
import { useTranslation } from "react-i18next";
import dayjs from "../../utils/day";
import day from "../../utils/day";
import "./eula.styles.scss";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
const Eula = ({ currentEula, currentUser, acceptEula }) => {
const [formReady, setFormReady] = useState(false);
@@ -208,7 +208,7 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
{
required: true,
validator: (_, value) => {
if (dayjs(value).isSame(dayjs(), "day")) {
if (day(value).isSame(day(), "day")) {
return Promise.resolve();
}
return Promise.reject(new Error(t("eula.messages.date_accepted")));
@@ -216,7 +216,7 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
}
]}
>
<DateTimePicker isDateOnly onChange={handleChange} onlyToday aria-label={t("eula.labels.date_accepted")} />
<FormDatePicker onChange={handleChange} onlyToday aria-label={t("eula.labels.date_accepted")} />
</Form.Item>
</Col>
</Row>

View File

@@ -0,0 +1,123 @@
import { DatePicker } from "antd";
import dayjs from "../../utils/day";
import React, { useRef } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(FormDatePicker);
const dateFormat = "MM/DD/YYYY";
export function FormDatePicker({
bodyshop,
value,
onChange,
onBlur,
onlyFuture,
onlyToday,
isDateOnly = true,
...restProps
}) {
const ref = useRef();
const handleChange = (newDate) => {
if (value !== newDate && onChange) {
onChange(isDateOnly ? newDate && newDate.format("YYYY-MM-DD") : newDate);
}
};
const handleKeyDown = (e) => {
if (e.key.toLowerCase() === "t") {
if (onChange) {
onChange(isDateOnly ? dayjs().format("YYYY-MM-DD") : dayjs());
}
} else if (e.key.toLowerCase() === "enter") {
if (ref.current && ref.current.blur) ref.current.blur();
}
};
const handleBlur = (e) => {
const v = e.target.value;
if (!v) return;
const formats = [
"MMDDYY",
"MMDDYYYY",
"MM/DD/YY",
"MM/DD/YYYY",
"M/DD/YY",
"M/DD/YYYY",
"MM/D/YY",
"MM/D/YYYY",
"M/D/YY",
"M/D/YYYY",
"D/MM/YY",
"D/MM/YYYY",
"DD/M/YY",
"DD/M/YYYY",
"D/M/YY",
"D/M/YYYY"
];
let _a;
// Iterate through formats to find the correct one
for (let format of formats) {
_a = dayjs(v, format);
if (v === _a.format(format)) {
break;
}
}
if (_a.isValid() && value && value.isValid && value.isValid()) {
_a.set({
hours: value.hours(),
minutes: value.minutes(),
seconds: value.seconds(),
milliseconds: value.milliseconds()
});
}
if (_a.isValid() && onChange) {
if (onlyFuture) {
if (dayjs().subtract(1, "day").isBefore(_a)) {
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
} else {
onChange(isDateOnly ? dayjs().format("YYYY-MM-DD") : dayjs());
}
} else {
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
}
}
};
return (
<div onKeyDown={handleKeyDown}>
<DatePicker
ref={ref}
value={value ? dayjs(value) : null}
onChange={handleChange}
format={dateFormat}
onBlur={onBlur || handleBlur}
showToday={false}
disabledTime
disabledDate={(d) => {
if (onlyToday) {
return !dayjs().isSame(d, "day");
} else if (onlyFuture) {
return dayjs().subtract(1, "day").isAfter(d);
}
}}
{...restProps}
/>
</div>
);
}

View File

@@ -0,0 +1,48 @@
import { DatePicker } from "antd";
import dayjs from "../../utils/day.js";
import React, { useRef } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(FormDateTimePickerEnhanced);
const dateFormat = "MM/DD/YYYY h:mm a";
export function FormDateTimePickerEnhanced({
bodyshop,
value,
onBlur,
onlyFuture,
onlyToday,
isDateOnly = true,
...restProps
}) {
const ref = useRef();
return (
<div>
<DatePicker
ref={ref}
value={value ? dayjs(value) : null}
format={dateFormat}
onBlur={onBlur}
showToday={false}
disabledDate={(d) => {
if (onlyToday) {
return !dayjs().isSame(d, "day");
} else if (onlyFuture) {
return dayjs().subtract(1, "day").isAfter(d);
}
}}
{...restProps}
/>
</div>
);
}

View File

@@ -1,122 +1,46 @@
import { DatePicker } from "antd";
import PropTypes from "prop-types";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
import React, { forwardRef } from "react";
//import DatePicker from "react-datepicker";
//import "react-datepicker/src/stylesheets/datepicker.scss";
import { Space, TimePicker } from "antd";
import dayjs from "../../utils/day";
import { fuzzyMatchDate } from "./formats.js";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
//To be used as a form element only.
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const DateTimePicker = ({
value,
onChange,
onBlur,
id,
onlyFuture,
onlyToday,
isDateOnly = false,
bodyshop,
...restProps
}) => {
const [isManualInput, setIsManualInput] = useState(false);
const { t } = useTranslation();
const handleChange = useCallback(
(newDate) => {
if (onChange) {
onChange(bodyshop?.timezone && newDate ? dayjs(newDate).tz(bodyshop.timezone, true) : newDate);
}
setIsManualInput(false);
},
[onChange, bodyshop?.timezone]
);
const handleBlur = useCallback(
(e) => {
// Bail if this is not a manual input
if (!isManualInput) {
return;
}
// Reset manual input flag
setIsManualInput(false);
const v = e?.target?.value;
if (!v) return;
let parsedDate = isDateOnly ? fuzzyMatchDate(v)?.startOf("day") : fuzzyMatchDate(v);
if (parsedDate && onChange) {
onChange(parsedDate);
}
},
[isManualInput, isDateOnly, onChange]
);
const handleKeyDown = useCallback(
(e) => {
setIsManualInput(true);
if (e.key.toLowerCase() === "t" && onChange) {
e.preventDefault();
setIsManualInput(false);
onChange(dayjs());
} else if (e.key.toLowerCase() === "enter") {
handleBlur(e);
}
},
[onChange, handleBlur]
);
const handleDisabledDate = useCallback(
(current) => {
if (onlyToday) {
return !dayjs().isSame(current, "day");
} else if (onlyFuture) {
return dayjs().subtract(1, "day").isAfter(current);
}
return false;
},
[onlyToday, onlyFuture]
);
const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, ...restProps }, ref) => {
// const handleChange = (newDate) => {
// if (value !== newDate && onChange) {
// onChange(newDate);
// }
// };
return (
<div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}>
<DatePicker
showTime={
isDateOnly
? false
: {
format: "hh:mm a",
minuteStep: 15,
defaultValue: dayjs(dayjs(), "HH:mm:ss")
}
}
format={isDateOnly ? "MM/DD/YYYY" : "MM/DD/YYYY hh:mm a"}
<Space direction="vertical" style={{ width: "100%" }} id={id}>
<FormDatePicker
{...restProps}
{...(onlyFuture && {
disabledDate: (d) => dayjs().subtract(1, "day").isAfter(d)
})}
value={value}
onBlur={onBlur}
onChange={onChange}
onlyFuture={onlyFuture}
isDateOnly={false}
/>
<TimePicker
value={value ? dayjs(value) : null}
onChange={handleChange}
placeholder={isDateOnly ? t("general.labels.date") : t("general.labels.datetime")}
onBlur={onBlur || handleBlur}
disabledDate={handleDisabledDate}
{...(onlyFuture && {
disabledDate: (d) => dayjs().isAfter(d)
})}
onChange={onChange}
disableSeconds={true}
minuteStep={15}
onBlur={onBlur}
format="hh:mm a"
{...restProps}
/>
</div>
</Space>
);
};
DateTimePicker.propTypes = {
value: PropTypes.any,
onChange: PropTypes.func,
onBlur: PropTypes.func,
id: PropTypes.string,
onlyFuture: PropTypes.bool,
onlyToday: PropTypes.bool,
isDateOnly: PropTypes.bool
};
export default connect(mapStateToProps, null)(DateTimePicker);
export default forwardRef(DateTimePicker);

View File

@@ -1,63 +0,0 @@
import dayjs from "../../utils/day";
const dateFormats = [
"MMDDYYYY",
"MMDDYY",
"M/D/YYYY",
"MM/D/YYYY",
"M/DD/YYYY",
"MM/DD/YYYY",
"M/D/YY",
"MM/D/YY",
"M/DD/YY",
"MM/DD/YY"
];
const timeFormats = ["h:mm A", "h:mmA", "h A", "hA", "hh:mm A", "hh:mm:ss A"];
const dateTimeFormats = [
...["M/D/YYYY", "MM/D/YYYY", "M/DD/YYYY", "MM/DD/YYYY", "M/D/YY", "MM/D/YY", "M/DD/YY", "MM/DD/YY"].flatMap(
(dateFormat) => timeFormats.map((timeFormat) => `${dateFormat} ${timeFormat}`)
),
...["MMDDYYYY", "MMDDYY"].flatMap((dateFormat) => timeFormats.map((timeFormat) => `${dateFormat} ${timeFormat}`)),
"M/D/YYYY",
"MM/D/YYYY",
"M/DD/YYYY",
"MM/DD/YYYY",
"M/D/YY",
"MM/D/YY",
"M/DD/YY",
"MM/DD/YY",
"MMDDYYYY",
"MMDDYY"
];
const sanitizeInput = (input) =>
input
.trim()
.toUpperCase()
.replace(/\s*(am|pm)\s*/i, " $1")
.replaceAll(".", "/")
.replaceAll("-", "/");
export const fuzzyMatchDate = (dateString) => {
const sanitizedInput = sanitizeInput(dateString);
for (const format of dateFormats) {
const parsedDate = dayjs(sanitizedInput, format, true);
if (parsedDate.isValid()) {
return parsedDate;
}
}
for (const format of dateTimeFormats) {
const parsedDateTime = dayjs(sanitizedInput, format, true);
if (parsedDateTime.isValid()) {
return parsedDateTime; // Return the dayjs object
}
}
return null; // If no matching format is found
};

View File

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

View File

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

View File

@@ -13,6 +13,7 @@ import Icon, {
FileFilled,
HomeFilled,
ImportOutlined,
InfoCircleOutlined,
LineChartOutlined,
PaperClipOutlined,
PhoneOutlined,
@@ -26,8 +27,8 @@ import Icon, {
UserOutlined
} from "@ant-design/icons";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Layout, Menu } from "antd";
import React from "react";
import { Layout, Menu, Switch, Tooltip } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { BsKanban } from "react-icons/bs";
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
@@ -42,6 +43,7 @@ import { selectRecentItems, selectSelectedHeader } from "../../redux/application
import { setModalContext } from "../../redux/modals/modals.actions";
import { signOutStart } from "../../redux/user/user.actions";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import { checkBeta, handleBeta, setBeta } from "../../utils/betaHandler";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
@@ -113,18 +115,19 @@ function Header({
names: ["ImEXPay", "DmsAp", "Simple_Inventory"],
splitKey: bodyshop && bodyshop.imexshopid
});
const [betaSwitch, setBetaSwitch] = useState(false);
const { t } = useTranslation();
// const deleteBetaCookie = () => {
// const cookieExists = document.cookie.split("; ").some((row) => row.startsWith(`betaSwitchImex=`));
// if (cookieExists) {
// const domain = window.location.hostname.split(".").slice(-2).join(".");
// document.cookie = `betaSwitchImex=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=.${domain}`;
// }
// };
//
// deleteBetaCookie();
useEffect(() => {
const isBeta = checkBeta();
setBetaSwitch(isBeta);
}, []);
const betaSwitchChange = (checked) => {
setBeta(checked);
setBetaSwitch(checked);
handleBeta();
};
const accountingChildren = [];
@@ -692,6 +695,31 @@ function Header({
}
];
InstanceRenderManager({
executeFunction: true,
args: [],
imex: () => {
menuItems.push({
key: "beta-switch",
id: "header-beta-switch",
style: { marginLeft: "auto" },
label: (
<Tooltip
title={`A more modern ${InstanceRenderManager({
imex: t("titles.imexonline"),
rome: t("titles.romeonline"),
promanager: t("titles.promanager")
})} is ready for you to try! You can switch back at any time.`}
>
<InfoCircleOutlined />
<span style={{ marginRight: 8 }}>Try the new app</span>
<Switch checked={betaSwitch} onChange={betaSwitchChange} />
</Tooltip>
)
});
}
});
return (
<Layout.Header>
<Menu

View File

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

View File

@@ -1,5 +1,6 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Form, Input, notification, Switch } from "antd";
import dayjs from "../../../../utils/day";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -13,7 +14,6 @@ import { UPDATE_OWNER } from "../../../../graphql/owners.queries";
import { insertAuditTrail } from "../../../../redux/application/application.actions";
import { selectBodyshop, selectCurrentUser } from "../../../../redux/user/user.selectors";
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
import dayjs from "../../../../utils/day";
import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
@@ -275,19 +275,7 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
>
<DateTimePicker disabled={readOnly} />
</Form.Item>
<Form.Item
name="actual_delivery"
label={t("jobs.fields.actual_delivery")}
rules={[
{
required: bodyshop.deliverchecklist.actual_delivery
? bodyshop.deliverchecklist.actual_delivery
: false
//message: t("general.validation.required"),
}
]}
disabled={readOnly}
>
<Form.Item name="actual_delivery" label={t("jobs.fields.actual_delivery")} disabled={readOnly}>
<DateTimePicker disabled={readOnly} />
</Form.Item>
<Form.Item

View File

@@ -3,7 +3,7 @@ import React from "react";
import { useQuery } from "@apollo/client";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_PARTS_BILLS_BY_JOBID } from "../../graphql/bills.queries";
import { QUERY_BILLS_BY_JOBID } from "../../graphql/bills.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
@@ -19,7 +19,7 @@ const mapDispatchToProps = (dispatch) => ({
export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardBills);
export function JobCloseRoGuardBills({ job, jobRO, bodyshop, form, warningCallback }) {
const { loading, error, data } = useQuery(QUERY_PARTS_BILLS_BY_JOBID, {
const { loading, error, data } = useQuery(QUERY_BILLS_BY_JOBID, {
variables: { jobid: job.id },
fetchPolicy: "network-only",
nextFetchPolicy: "network-only"

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useState } from "react";
import { LockOutlined } from "@ant-design/icons";
import { Badge, Card, Col, Collapse, Form, Input, Row, Space, Tooltip } from "antd";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -12,8 +12,9 @@ import JobCloseRoGuardBills from "./job-close-ro-guard.bills";
import JobCloseRoGuardPpd from "./job-close-ro-guard.ppd";
import JobCloseRoGuardProfit from "./job-close-ro-guard.profit";
import "./job-close-ro-guard.styles.scss";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import JobCloseRoGuardSublet from "./job-close-ro-guard.sublet";
import JobCloseRoGuardTtLifecycle from "./job-close-ro-guard.tt-lifecycle";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser

View File

@@ -1,21 +1,14 @@
import React from "react";
import { useTranslation } from "react-i18next";
import Car from "../job-damage-visual/job-damage-visual.component";
import CardTemplate from "./job-detail-cards.template.component";
import Car from "../job-damage-visual/job-damage-visual.component";
export default function JobDetailCardsDamageComponent({ loading, data }) {
const { t } = useTranslation();
const { area_of_damage } = data;
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.damage")}>
{area_of_damage ? (
<Car
dmg1={area_of_damage.impact1 && area_of_damage.impact1.padStart(2, "0")}
dmg2={area_of_damage.impact2 && area_of_damage.impact2.padStart(2, "0")}
/>
) : (
t("jobs.errors.nodamage")
)}
{area_of_damage ? <Car dmg1={area_of_damage.impact1} dmg2={area_of_damage.impact2} /> : t("jobs.errors.nodamage")}
</CardTemplate>
);
}

View File

@@ -26,16 +26,6 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
const { t } = useTranslation();
const { joblines_status } = data;
const filteredJobLines = data.joblines.filter(
(j) =>
j.part_type !== null &&
j.part_type !== "PAE" &&
j.part_type !== "PAS" &&
j.part_type !== "PASL" &&
j.part_qty !== 0 &&
j.act_price !== 0
);
//TODO: Correct jobline_statuses view by including the part_qty !== 0 and act_price !== 0
const columns = [
{
title: t("joblines.fields.line_desc"),
@@ -105,7 +95,7 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
<div>
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
<PartsStatusPie joblines_status={joblines_status} />
<Table key="id" columns={columns} dataSource={filteredJobLines ? filteredJobLines : []} />
<Table key="id" columns={columns} dataSource={data ? data.joblines : []} />
</CardTemplate>
</div>
);

View File

@@ -2,30 +2,27 @@ import { useQuery } from "@apollo/client";
import { Col, Row, Skeleton, Space, Timeline, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
import { selectTechnician } from "../../redux/tech/tech.selectors.js";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component";
import BillDetailEditcontainer from "../bill-detail-edit/bill-detail-edit.container.jsx";
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
import TaskListContainer from "../task-list/task-list.container.jsx";
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
technician: selectTechnician
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander);
export function JobLinesExpander({ jobline, jobid, bodyshop, technician }) {
export function JobLinesExpander({ jobline, jobid, bodyshop }) {
const { t } = useTranslation();
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
fetchPolicy: "network-only",
@@ -50,15 +47,9 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, technician }) {
children: (
<Row wrap>
<Col span={4}>
{!technician ? (
<>
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}>
{line.parts_order.order_number}
</Link>
</>
) : (
`${line.parts_order.order_number}`
)}
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}>
{line.parts_order.order_number}
</Link>
</Col>
<Col span={4}>
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
@@ -93,17 +84,17 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, technician }) {
key: line.id,
children: (
<Row>
<Col span={8}>{line.parts_dispatch.number}</Col>
<Col span={8}>
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>{line.parts_dispatch.number}</Link>
</Col>
<Col span={8}>
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
</Col>
<Col span={8}>
{line.accepted_at ? (
<Space>
{t("parts_dispatch_lines.fields.accepted_at")}
<DateFormatter>{line.accepted_at}</DateFormatter>
</Space>
) : null}
<Space>
{t("parts_dispatch_lines.fields.accepted_at")}
<DateFormatter>{line.accepted_at}</DateFormatter>
</Space>
</Col>
</Row>
)
@@ -120,7 +111,6 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, technician }) {
<FeatureWrapper featureName="bills" noauth={() => null}>
<Col md={24} lg={8}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<BillDetailEditcontainer />
<Timeline
items={
data.billlines.length > 0
@@ -129,15 +119,9 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, technician }) {
children: (
<Row wrap>
<Col span={4}>
{!technician ? (
<>
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
{line.bill.invoice_number}
</Link>
</>
) : (
`${line.bill.invoice_number}`
)}
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
{line.bill.invoice_number}
</Link>
</Col>
<Col span={4}>
<span>

View File

@@ -3,21 +3,13 @@ import { Button, Form, notification, Popover, Tooltip } from "antd";
import axios from "axios";
import { t } from "i18next";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_LINE_PPC } from "../../graphql/jobs-lines.queries";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
const mapStateToProps = createStructuredSelector({
technician: selectTechnician
});
const mapDispatchToProps = (dispatch) => ({});
export function JobLinesPartPriceChange({ job, line, refetch, technician }) {
export default function JobLinesPartPriceChange({ job, line, refetch }) {
const [loading, setLoading] = useState(false);
const [updatePartPrice] = useMutation(UPDATE_LINE_PPC);
@@ -60,7 +52,7 @@ export function JobLinesPartPriceChange({ job, line, refetch, technician }) {
}
};
const popcontent = !technician && InstanceRenderManager({
const popcontent = InstanceRenderManager({
imex: null,
rome: (
<Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}>
@@ -103,4 +95,3 @@ export function JobLinesPartPriceChange({ job, line, refetch, technician }) {
</JobLineConvertToLabor>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesPartPriceChange);

View File

@@ -31,20 +31,19 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import _ from "lodash";
import { FaTasks } from "react-icons/fa";
import { selectBodyshop } from "../../redux/user/user.selectors";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLineBulkAssignComponent from "../job-line-bulk-assign/job-line-bulk-assign.component";
import JobLineDispatchButton from "../job-line-dispatch-button/job-line-dispatch-button.component";
import JoblineTeamAssignment from "../job-line-team-assignment/job-line-team-assignmnent.component";
import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-send-parts-price-change.component";
import PartsOrderDrawer from "../parts-order-list-table/parts-order-list-table-drawer.component";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import JobLinesExpander from "./job-lines-expander.component";
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
import { FaTasks } from "react-icons/fa";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -55,7 +54,6 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setJobLineEditContext: (context) => dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
setPartsReceiveContext: (context) => dispatch(setModalContext({ context: context, modal: "partsReceive" })),
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
});
@@ -65,7 +63,6 @@ export function JobLinesComponent({
jobRO,
technician,
setPartsOrderContext,
setPartsReceiveContext,
loading,
refetch,
jobLines,
@@ -74,11 +71,7 @@ export function JobLinesComponent({
setJobLineEditContext,
form,
setBillEnterContext,
setTaskUpsertContext,
billsQuery,
handleBillOnRowClick,
handlePartsOrderOnRowClick,
handlePartsDispatchOnRowClick
setTaskUpsertContext
}) {
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
const {
@@ -205,6 +198,7 @@ export function JobLinesComponent({
onFilter: (value, record) => value.includes(record.part_type),
render: (text, record) => (record.part_type ? t(`joblines.fields.part_types.${record.part_type}`) : null)
},
{
title: t("joblines.fields.act_price"),
dataIndex: "act_price",
@@ -219,6 +213,7 @@ export function JobLinesComponent({
dataIndex: "part_qty",
key: "part_qty"
},
// {
// title: t('joblines.fields.tax_part'),
// dataIndex: 'tax_part',
@@ -327,7 +322,7 @@ export function JobLinesComponent({
key: "actions",
render: (text, record) => (
<Space>
{(record.manual_line || jobIsPrivate) && !technician && (
{(record.manual_line || jobIsPrivate) && (
<>
<Button
disabled={jobRO}
@@ -342,6 +337,7 @@ export function JobLinesComponent({
</Button>
</>
)}
<Button
title={t("tasks.buttons.create")}
onClick={() => {
@@ -355,7 +351,7 @@ export function JobLinesComponent({
>
<FaTasks />
</Button>
{(record.manual_line || jobIsPrivate) && !technician && (
{(record.manual_line || jobIsPrivate) && (
<>
<Button
disabled={jobRO}
@@ -441,15 +437,6 @@ export function JobLinesComponent({
return (
<div>
<PartsOrderModalContainer />
{!technician && (
<PartsOrderDrawer
job={job}
billsQuery={billsQuery}
handleOnRowClick={handlePartsOrderOnRowClick}
setPartsReceiveContext={setPartsReceiveContext}
setTaskUpsertContext={setTaskUpsertContext}
/>
)}
<PageHeader
title={t("jobs.labels.estimatelines")}
extra={
@@ -566,7 +553,7 @@ export function JobLinesComponent({
>
{t("joblines.actions.new")}
</Button>
{InstanceRenderManager({ rome: <JobSendPartPriceChangeComponent job={job} disabled={technician} /> })}
{InstanceRenderManager({ rome: <JobSendPartPriceChangeComponent job={job} /> })}
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
<Input.Search
placeholder={t("general.labels.search")}

View File

@@ -1,17 +1,7 @@
import React, { useMemo, useState } from "react";
import JobLinesComponent from "./job-lines.component";
function JobLinesContainer({
job,
joblines,
billsQuery,
handleBillOnRowClick,
handlePartsOrderOnRowClick,
handlePartsDispatchOnRowClick,
refetch,
form,
...rest
}) {
function JobLinesContainer({ job, joblines, refetch, form, ...rest }) {
const [searchText, setSearchText] = useState("");
const jobLines = useMemo(() => {
@@ -32,19 +22,7 @@ function JobLinesContainer({
}, [joblines, searchText]);
return (
<div>
<JobLinesComponent
refetch={refetch}
jobLines={jobLines}
billsQuery={billsQuery}
handleBillOnRowClick={handleBillOnRowClick}
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
setSearchText={setSearchText}
job={job}
form={form}
/>
</div>
<JobLinesComponent refetch={refetch} jobLines={jobLines} setSearchText={setSearchText} job={job} form={form} />
);
}

View File

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

View File

@@ -11,18 +11,17 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
technician: selectTechnician
//currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobLineConvertToLabor);
export function JobLineConvertToLabor({ children, jobline, job, insertAuditTrail, technician, ...otherBtnProps }) {
export function JobLineConvertToLabor({ children, jobline, job, insertAuditTrail, ...otherBtnProps }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
@@ -166,7 +165,7 @@ export function JobLineConvertToLabor({ children, jobline, job, insertAuditTrail
return (
<>
{children}
{jobline.act_price !== 0 && !technician && (
{jobline.act_price !== 0 && (
<Popover disabled={jobline.convertedtolbr} content={overlay} open={visibility} placement="bottom">
<Tooltip title={t("joblines.actions.converttolabor")}>
<Button

View File

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

View File

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

View File

@@ -10,8 +10,8 @@ import {
QUERY_SCOREBOARD_ENTRY,
UPDATE_SCOREBOARD_ENTRY
} from "../../graphql/scoreboard.queries";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
export default function ScoreboardAddButton({ job, disabled, ...otherBtnProps }) {
const { t } = useTranslation();
@@ -86,7 +86,7 @@ export default function ScoreboardAddButton({ job, disabled, ...otherBtnProps })
}
]}
>
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item
label={t("scoreboard.fields.bodyhrs")}

View File

@@ -3,7 +3,7 @@ import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
export default function JobSendPartPriceChangeComponent({ job, disabled }) {
export default function JobSendPartPriceChangeComponent({ job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleClick = async () => {
@@ -24,7 +24,7 @@ export default function JobSendPartPriceChangeComponent({ job, disabled }) {
};
return (
<Button onClick={handleClick} loading={loading} disabled={disabled}>
<Button onClick={handleClick} loading={loading}>
{t("jobs.actions.sendpartspricechange")}
</Button>
);

View File

@@ -3,8 +3,8 @@ import Dinero from "dinero.js";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { alphaSort } from "../../utils/sorters";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
export default function JobTotalsTableLabor({ job }) {
const { t } = useTranslation();
@@ -56,49 +56,16 @@ export default function JobTotalsTableLabor({ job }) {
sortOrder: state.sortedInfo.columnKey === "mod_lb_hrs" && state.sortedInfo.order,
render: (text, record) => record.hours.toFixed(1)
},
...InstanceRenderManager({
imex: [
{
title: t("joblines.fields.total"),
dataIndex: "total",
key: "total",
align: "right",
sorter: (a, b) => a.total.amount - b.total.amount,
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
render: (text, record) => Dinero(record.total).toFormat()
}
],
rome: [
{
title: t("joblines.fields.amount"),
dataIndex: "base",
key: "base",
align: "right",
sorter: (a, b) => a.base.amount - b.base.amount,
sortOrder: state.sortedInfo.columnKey === "base" && state.sortedInfo.order,
render: (text, record) => Dinero(record.base).toFormat()
},
{
title: t("joblines.fields.adjustment"),
dataIndex: "adjustment",
key: "adjustment",
align: "right",
sorter: (a, b) => a.adjustment.amount - b.adjustment.amount,
sortOrder: state.sortedInfo.columnKey === "adjustment" && state.sortedInfo.order,
render: (text, record) => Dinero(record.adjustment).toFormat()
},
{
title: t("joblines.fields.total"),
dataIndex: "total",
key: "total",
align: "right",
sorter: (a, b) => a.total.amount - b.total.amount,
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
render: (text, record) => Dinero(record.total).toFormat()
}
],
promanager: "USE_ROME"
})
{
title: t("joblines.fields.total"),
dataIndex: "total",
key: "total",
align: "right",
sorter: (a, b) => a.total.amount - b.total.amount,
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
render: (text, record) => Dinero(record.total).toFormat()
}
];
const handleTableChange = (pagination, filters, sorter) => {
@@ -124,16 +91,6 @@ export default function JobTotalsTableLabor({ job }) {
<Table.Summary.Cell>
{(job.job_totals.rates.mapa.hours + job.job_totals.rates.mash.hours).toFixed(1)}
</Table.Summary.Cell>
{InstanceRenderManager({
imex: null,
rome: (
<>
<Table.Summary.Cell />
<Table.Summary.Cell />
</>
),
promanager: "USE_ROME"
})}
<Table.Summary.Cell align="right">
<strong>{Dinero(job.job_totals.rates.rates_subtotal).toFormat()}</strong>
</Table.Summary.Cell>
@@ -165,29 +122,7 @@ export default function JobTotalsTableLabor({ job }) {
<CurrencyFormatter>{job.job_totals.rates.mapa.rate}</CurrencyFormatter>
</Table.Summary.Cell>
<Table.Summary.Cell>{job.job_totals.rates.mapa.hours.toFixed(1)}</Table.Summary.Cell>
{InstanceRenderManager({
imex: (
<>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mapa.total).toFormat()}
</Table.Summary.Cell>
</>
),
rome: (
<>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mapa.base).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mapa.adjustment).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mapa.total).toFormat()}
</Table.Summary.Cell>
</>
),
promanager: "USE_ROME"
})}
<Table.Summary.Cell align="right">{Dinero(job.job_totals.rates.mapa.total).toFormat()}</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell>
@@ -216,29 +151,7 @@ export default function JobTotalsTableLabor({ job }) {
<CurrencyFormatter>{job.job_totals.rates.mash.rate}</CurrencyFormatter>
</Table.Summary.Cell>
<Table.Summary.Cell>{job.job_totals.rates.mash.hours.toFixed(1)}</Table.Summary.Cell>
{InstanceRenderManager({
imex: (
<>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mash.total).toFormat()}
</Table.Summary.Cell>
</>
),
rome: (
<>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mash.base).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mash.adjustment).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mash.total).toFormat()}
</Table.Summary.Cell>
</>
),
promanager: "USE_ROME"
})}
<Table.Summary.Cell align="right">{Dinero(job.job_totals.rates.mash.total).toFormat()}</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell>
@@ -246,16 +159,6 @@ export default function JobTotalsTableLabor({ job }) {
</Table.Summary.Cell>
<Table.Summary.Cell />
<Table.Summary.Cell />
{InstanceRenderManager({
imex: null,
rome: (
<>
<Table.Summary.Cell />
<Table.Summary.Cell />
</>
),
promanager: "USE_ROME"
})}
<Table.Summary.Cell align="right">
<strong>{Dinero(job.job_totals.rates.subtotal).toFormat()}</strong>
</Table.Summary.Cell>

View File

@@ -141,14 +141,10 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
key: t("jobs.fields.ded_amt"),
total: job.job_totals.totals.custPayable.deductible
},
...(InstanceRenderManager({
imex: [{
key: t("jobs.fields.federal_tax_payable"),
total: job.job_totals.totals.custPayable.federal_tax
}],
rome: [],
promanager: "USE_ROME"
})),
// {
// key: t("jobs.fields.federal_tax_payable"),
// total: job.job_totals.totals.custPayable.federal_tax,
// },
{
key: t("jobs.fields.other_amount_payable"),
total: job.job_totals.totals.custPayable.other_customer_amount

View File

@@ -5,6 +5,7 @@ import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
@@ -19,14 +20,7 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation, type }) =>
dispatch(
insertAuditTrail({
jobid,
operation,
type
})
)
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminDatesChange);
@@ -93,7 +87,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
<FormFieldsChanged form={form} />
<LayoutFormRow header={t("jobs.forms.estdates")}>
<Form.Item label={t("jobs.fields.date_estimated")} name="date_estimated">
<DateTimePicker format="MM/DD/YYYY" isDateOnly />
<FormDatePicker format="MM/DD/YYYY" />
</Form.Item>
<Form.Item label={t("jobs.fields.date_towin")} name="date_towin">
<DateTimePicker />

View File

@@ -3,7 +3,6 @@ import { Button, Space, notification } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { DELETE_DELIVERY_CHECKLIST, DELETE_INTAKE_CHECKLIST } from "../../graphql/jobs.queries";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
export default function JobAdminDeleteIntake({ job }) {
const { t } = useTranslation();
@@ -48,22 +47,16 @@ export default function JobAdminDeleteIntake({ job }) {
setLoading(false);
};
const InstanceRender = InstanceRenderManager({
imex: true,
rome: "USE_IMEX",
promanager: false
});
return InstanceRender ? (
return (
<>
<Space wrap>
<Button loading={loading} onClick={handleDelete} disabled={!job.intakechecklist}>
{t("jobs.labels.deleteintake")}
</Button>
<Button loading={loading} onClick={handleDeleteDelivery} disabled={!job.deliverchecklist}>
<Button loading={loading} onClick={handleDeleteDelivery} disabled={!job.deliverychecklist}>
{t("jobs.labels.deletedelivery")}
</Button>
</Space>
</>
) : null;
);
}

View File

@@ -3,6 +3,7 @@ import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Col, Row, notification } from "antd";
import Axios from "axios";
import _ from "lodash";
import dayjs from "../../utils/day";
import queryString from "query-string";
import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -23,8 +24,6 @@ import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selecto
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import confirmDialog from "../../utils/asyncConfirm";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import AlertComponent from "../alert/alert.component";
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
@@ -33,6 +32,7 @@ import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.contai
import { GetSupplementDelta } from "./jobs-available-supplement.estlines.util";
import HeaderFields from "./jobs-available-supplement.headerfields";
import JobsAvailableTableComponent from "./jobs-available-table.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -580,13 +580,12 @@ function ResolveCCCLineIssues(estData, bodyshop) {
InstanceRenderManager({
executeFunction: true,
args: [],
rome: () => {
promanager: () => {
if (line.mod_lbr_ty === "LAET" || line.mod_lbr_ty === "LAUT") {
// line.notes += ` | ET/UT Update (prev = ${line.mod_lbr_ty})`;
line.mod_lbr_ty = "LAR";
}
},
promanager: "USE_ROME"
}
});
});

View File

@@ -1,9 +1,18 @@
import { Collapse, Form, Input, InputNumber, Select, Space, Switch } from "antd";
import {
Collapse,
Form,
Input,
InputNumber,
Select,
Space,
Switch,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
@@ -20,7 +29,6 @@ import JobsDetailRatesTaxes from "../jobs-detail-rates/jobs-detail-rates.taxes.c
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -53,7 +61,10 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.regie_number")} name="regie_number">
<Form.Item
label={t("jobs.fields.regie_number")}
name="regie_number"
>
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
@@ -105,7 +116,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<FormItemEmail email={getFieldValue("ins_ea")} />
</Form.Item>
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
<DateTimePicker isDateOnly />
<FormDatePicker />
</Form.Item>
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
<Input />

Some files were not shown because too many files have changed in this diff Show More