Merged in release/2024-10-25 (pull request #1833)

Release/2024-10-25 into test-AIO -IO-2921, IO-2966, IO-2992, IO-2996
This commit is contained in:
Dave Richer
2024-10-23 17:25:41 +00:00
12 changed files with 534 additions and 924 deletions

0
.localstack/.gitkeep Normal file
View File

15
.vscode/launch.json vendored
View File

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

View File

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

27
certs/id_rsa Normal file
View File

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

1
certs/id_rsa.pub Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

1306
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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