feature/IO-3182-Phone-Number-Consent - Checkpoint
This commit is contained in:
154
client/package-lock.json
generated
154
client/package-lock.json
generated
@@ -20,8 +20,8 @@
|
|||||||
"@firebase/messaging": "^0.12.21",
|
"@firebase/messaging": "^0.12.21",
|
||||||
"@jsreport/browser-client": "^3.1.0",
|
"@jsreport/browser-client": "^3.1.0",
|
||||||
"@reduxjs/toolkit": "^2.8.2",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"@sentry/cli": "^2.45.0",
|
"@sentry/cli": "^2.46.0",
|
||||||
"@sentry/react": "^9.22.0",
|
"@sentry/react": "^9.23.0",
|
||||||
"@sentry/vite-plugin": "^3.5.0",
|
"@sentry/vite-plugin": "^3.5.0",
|
||||||
"@splitsoftware/splitio-react": "^2.1.1",
|
"@splitsoftware/splitio-react": "^2.1.1",
|
||||||
"@tanem/react-nprogress": "^5.0.53",
|
"@tanem/react-nprogress": "^5.0.53",
|
||||||
@@ -4469,50 +4469,50 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/browser-utils": {
|
"node_modules/@sentry-internal/browser-utils": {
|
||||||
"version": "9.22.0",
|
"version": "9.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.23.0.tgz",
|
||||||
"integrity": "sha512-Ou1tBnVxFAIn8i9gvrWzRotNJQYiu3awNXpsFCw6qFwmiKAVPa6b13vCdolhXnrIiuR77jY1LQnKh9hXpoRzsg==",
|
"integrity": "sha512-hyN2Q6mh7ggw8sDVHeRyWz5LR6gjvf8zHSzQnMaF7QkeSyaeGM/SVSL4ODwqR9TRH7U2ku6nZFMbKhaBPV+Hfg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "9.22.0"
|
"@sentry/core": "9.23.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/feedback": {
|
"node_modules/@sentry-internal/feedback": {
|
||||||
"version": "9.22.0",
|
"version": "9.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.23.0.tgz",
|
||||||
"integrity": "sha512-zgMVkoC61fgi41zLcSZA59vOtKxcLrKBo1ECYhPD1hxEaneNqY5fhXDwlQBw96P5l2yqkgfX6YZtSdU4ejI9yA==",
|
"integrity": "sha512-Xf+KqV69TBiPo1gk2EsU6O/dumuTMxWOF52uVWJddQYI3pQTU5DqSeoZ5AY76bIIhV9n6AEFDGqNPXmuj4Acrw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "9.22.0"
|
"@sentry/core": "9.23.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/replay": {
|
"node_modules/@sentry-internal/replay": {
|
||||||
"version": "9.22.0",
|
"version": "9.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.23.0.tgz",
|
||||||
"integrity": "sha512-9GOycoKbrclcRXfcbNV8svbmAsOS5R4wXBQmKF4pFLkmFA/lJv9kdZSNYkRvkrxdNfbMIJXP+DV9EqTZcryXig==",
|
"integrity": "sha512-0/q15tvSboaK7/05BFQhs71bqgHKejJoDJgXmH0lySqgsRh/S18867ZxQNiuYhuVt337h07u1QaCyjnNJKHfuA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/browser-utils": "9.22.0",
|
"@sentry-internal/browser-utils": "9.23.0",
|
||||||
"@sentry/core": "9.22.0"
|
"@sentry/core": "9.23.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/replay-canvas": {
|
"node_modules/@sentry-internal/replay-canvas": {
|
||||||
"version": "9.22.0",
|
"version": "9.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.23.0.tgz",
|
||||||
"integrity": "sha512-EcG9IMSEalFe49kowBTJObWjof/iHteDwpyuAszsFDdQUYATrVUtwpwN7o52vDYWJud4arhjrQnMamIGxa79eQ==",
|
"integrity": "sha512-cYlw5svJjyPequm0PJjFGLpee86L1NieONEHlujOXkIG6IEriiorMm+8bNpGsHRuyvg41B+4P/YmcQAGtEGxXg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/replay": "9.22.0",
|
"@sentry-internal/replay": "9.23.0",
|
||||||
"@sentry/core": "9.22.0"
|
"@sentry/core": "9.23.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -4528,16 +4528,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/browser": {
|
"node_modules/@sentry/browser": {
|
||||||
"version": "9.22.0",
|
"version": "9.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.23.0.tgz",
|
||||||
"integrity": "sha512-3TeRm74dvX0JdjX0AgkQa+22iUHwHnY+Q6M05NZ+tDeCNHGK/mEBTeqquS1oQX67jWyuvYmG3VV6RJUxtG9Paw==",
|
"integrity": "sha512-QRkNxWys8e088260vByztoTsEVZ0W6v/XnZfKT6wkPPGn0aFeOrg/xjgxfI8D5huqZCxT28Cf23akOOly4FXjg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/browser-utils": "9.22.0",
|
"@sentry-internal/browser-utils": "9.23.0",
|
||||||
"@sentry-internal/feedback": "9.22.0",
|
"@sentry-internal/feedback": "9.23.0",
|
||||||
"@sentry-internal/replay": "9.22.0",
|
"@sentry-internal/replay": "9.23.0",
|
||||||
"@sentry-internal/replay-canvas": "9.22.0",
|
"@sentry-internal/replay-canvas": "9.23.0",
|
||||||
"@sentry/core": "9.22.0"
|
"@sentry/core": "9.23.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -4728,9 +4728,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli": {
|
"node_modules/@sentry/cli": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.46.0.tgz",
|
||||||
"integrity": "sha512-4sWu7zgzgHAjIxIjXUA/66qgeEf5ZOlloO+/JaGD5qXNSW0G7KMTR6iYjReNKMgdBCTH6bUUt9qiuA+Ex9Masw==",
|
"integrity": "sha512-nqoPl7UCr446QFkylrsRrUXF51x8Z9dGquyf4jaQU+OzbOJMqclnYEvU6iwbwvaw3tu/2DnoZE/Og+Nq1h63sA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4747,20 +4747,20 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@sentry/cli-darwin": "2.45.0",
|
"@sentry/cli-darwin": "2.46.0",
|
||||||
"@sentry/cli-linux-arm": "2.45.0",
|
"@sentry/cli-linux-arm": "2.46.0",
|
||||||
"@sentry/cli-linux-arm64": "2.45.0",
|
"@sentry/cli-linux-arm64": "2.46.0",
|
||||||
"@sentry/cli-linux-i686": "2.45.0",
|
"@sentry/cli-linux-i686": "2.46.0",
|
||||||
"@sentry/cli-linux-x64": "2.45.0",
|
"@sentry/cli-linux-x64": "2.46.0",
|
||||||
"@sentry/cli-win32-arm64": "2.45.0",
|
"@sentry/cli-win32-arm64": "2.46.0",
|
||||||
"@sentry/cli-win32-i686": "2.45.0",
|
"@sentry/cli-win32-i686": "2.46.0",
|
||||||
"@sentry/cli-win32-x64": "2.45.0"
|
"@sentry/cli-win32-x64": "2.46.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli-darwin": {
|
"node_modules/@sentry/cli-darwin": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.46.0.tgz",
|
||||||
"integrity": "sha512-p4Uxfv/L2fQdP3/wYnKVVz9gzZJf/1Xp9D+6raax/3Bu5y87yHYUqcdt98y/VAXQD4ofp2QgmhGUVPofvQNZmg==",
|
"integrity": "sha512-5Ll+e5KAdIk9OYiZO8aifMBRNWmNyPjSqdjaHlBC1Qfh7pE3b1zyzoHlsUazG0bv0sNrSGea8e7kF5wIO1hvyg==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -4771,9 +4771,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli-linux-arm": {
|
"node_modules/@sentry/cli-linux-arm": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.46.0.tgz",
|
||||||
"integrity": "sha512-6sEskFLlFKJ+e0MOYgIclBTUX5jYMyYhHIxXahEkI/4vx6JO0uvpyRAkUJRpJkRh/lPog0FM+tbP3so+VxB2qQ==",
|
"integrity": "sha512-WRrLNq/TEX/TNJkGqq6Ad0tGyapd5dwlxtsPbVBrIdryuL1mA7VCBoaHBr3kcwJLsgBHFH0lmkMee2ubNZZdkg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -4781,16 +4781,17 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux",
|
"linux",
|
||||||
"freebsd"
|
"freebsd",
|
||||||
|
"android"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli-linux-arm64": {
|
"node_modules/@sentry/cli-linux-arm64": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.46.0.tgz",
|
||||||
"integrity": "sha512-gUcLoEjzg7AIc4QQGEZwRHri+EHf3Gcms9zAR1VHiNF3/C/jL4WeDPJF2YiWAQt6EtH84tHiyhw1Ab/R8XFClg==",
|
"integrity": "sha512-OEJN8yAjI9y5B4telyqzu27Hi3+S4T8VxZCqJz1+z2Mp0Q/MZ622AahVPpcrVq/5bxrnlZR16+lKh8L1QwNFPg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -4798,16 +4799,17 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux",
|
"linux",
|
||||||
"freebsd"
|
"freebsd",
|
||||||
|
"android"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli-linux-i686": {
|
"node_modules/@sentry/cli-linux-i686": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.46.0.tgz",
|
||||||
"integrity": "sha512-VmmOaEAzSW23YdGNdy/+oQjCNAMY+HmOGA77A25/ep/9AV7PQB6FI7xO5Y1PVvlkxZFJ23e373njSsEeg4uDZw==",
|
"integrity": "sha512-xko3/BVa4LX8EmRxVOCipV+PwfcK5Xs8lP6lgF+7NeuAHMNL4DqF6iV9rrN8gkGUHCUI9RXSve37uuZnFy55+Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x86",
|
"x86",
|
||||||
"ia32"
|
"ia32"
|
||||||
@@ -4816,16 +4818,17 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux",
|
"linux",
|
||||||
"freebsd"
|
"freebsd",
|
||||||
|
"android"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli-linux-x64": {
|
"node_modules/@sentry/cli-linux-x64": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.46.0.tgz",
|
||||||
"integrity": "sha512-a0Oj68mrb25a0WjX/ShZ6AAd4PPiuLcgyzQr7bl2+DvYxIOajwkGbR+CZFEhOVZcfhTnixKy/qIXEzApEPHPQg==",
|
"integrity": "sha512-hJ1g5UEboYcOuRia96LxjJ0jhnmk8EWLDvlGnXLnYHkwy3ree/L7sNgdp/QsY8Z4j2PGO5f22Va+UDhSjhzlfQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -4833,16 +4836,17 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux",
|
"linux",
|
||||||
"freebsd"
|
"freebsd",
|
||||||
|
"android"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli-win32-arm64": {
|
"node_modules/@sentry/cli-win32-arm64": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.46.0.tgz",
|
||||||
"integrity": "sha512-vn+CwS4p+52pQSLNPoi20ZOrQmv01ZgAmuMnjkh1oUZfTyBAwWLrAh6Cy4cztcN8DfL5dOWKQBo8DBKURE4ttg==",
|
"integrity": "sha512-mN7cpPoCv2VExFRGHt+IoK11yx4pM4ADZQGEso5BAUZ5duViXB2WrAXCLd8DrwMnP0OE978a7N8OtzsFqjkbNA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -4856,9 +4860,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli-win32-i686": {
|
"node_modules/@sentry/cli-win32-i686": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.46.0.tgz",
|
||||||
"integrity": "sha512-8mMoDdlwxtcdNIMtteMK7dbi7054jak8wKSHJ5yzMw8UmWxC5thc/gXBc1uPduiaI56VjoJV+phWHBKCD+6I4w==",
|
"integrity": "sha512-6F73AUE3lm71BISUO19OmlnkFD5WVe4/wA1YivtLZTc1RU3eUYJLYxhDfaH3P77+ycDppQ2yCgemLRaA4A8mNQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x86",
|
"x86",
|
||||||
"ia32"
|
"ia32"
|
||||||
@@ -4873,9 +4877,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/cli-win32-x64": {
|
"node_modules/@sentry/cli-win32-x64": {
|
||||||
"version": "2.45.0",
|
"version": "2.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.46.0.tgz",
|
||||||
"integrity": "sha512-ZvK9cIqFaq7vZ0jkHJ/xh5au6902Dr+AUxSk6L6vCL7JCe2p93KGL/4d8VFB5PD/P7Y9b+105G/e0QIFKzpeOw==",
|
"integrity": "sha512-yuGVcfepnNL84LGA0GjHzdMIcOzMe0bjPhq/rwPsPN+zu11N+nPR2wV2Bum4U0eQdqYH3iAlMdL5/BEQfuLJww==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -4910,22 +4914,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/core": {
|
"node_modules/@sentry/core": {
|
||||||
"version": "9.22.0",
|
"version": "9.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.23.0.tgz",
|
||||||
"integrity": "sha512-ixvtKmPF42Y6ckGUbFlB54OWI75H2gO5UYHojO6eXFpS7xO3ZGgV/QH6wb40mWK+0w5XZ0233FuU9VpsuE6mKA==",
|
"integrity": "sha512-9846pn/BvASGgl7WsnKY4xry98WreP9ToeLfCQTQOf+pNfD/qNPn1/0xPInGni3LVMAXRtfHHMPm2Ghz255N7A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/react": {
|
"node_modules/@sentry/react": {
|
||||||
"version": "9.22.0",
|
"version": "9.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.23.0.tgz",
|
||||||
"integrity": "sha512-mI43NnioBYdG5TiXqRlhV1feZs9bnrrl+k5HOHBK7VQtymaXO0fkcsRLZTkdSgLRLMJGasZuvVhq2xK+18QyWQ==",
|
"integrity": "sha512-2J/oOx8jd7Jr2koYIe5IcJyStHBXpjkQnxawo54Zyyvzc96MftyM2Dv5TeYdz7fChU1NIXw7BVbEpkQ9XEQlqg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/browser": "9.22.0",
|
"@sentry/browser": "9.23.0",
|
||||||
"@sentry/core": "9.22.0",
|
"@sentry/core": "9.23.0",
|
||||||
"hoist-non-react-statics": "^3.3.2"
|
"hoist-non-react-statics": "^3.3.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
"@firebase/messaging": "^0.12.21",
|
"@firebase/messaging": "^0.12.21",
|
||||||
"@jsreport/browser-client": "^3.1.0",
|
"@jsreport/browser-client": "^3.1.0",
|
||||||
"@reduxjs/toolkit": "^2.8.2",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"@sentry/cli": "^2.45.0",
|
"@sentry/cli": "^2.46.0",
|
||||||
"@sentry/react": "^9.22.0",
|
"@sentry/react": "^9.23.0",
|
||||||
"@sentry/vite-plugin": "^3.5.0",
|
"@sentry/vite-plugin": "^3.5.0",
|
||||||
"@splitsoftware/splitio-react": "^2.1.1",
|
"@splitsoftware/splitio-react": "^2.1.1",
|
||||||
"@tanem/react-nprogress": "^5.0.53",
|
"@tanem/react-nprogress": "^5.0.53",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Badge, Card, List, Space, Tag } from "antd";
|
import { Badge, Card, List, Space, Tag, Tooltip } from "antd";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Virtuoso } from "react-virtuoso";
|
import { Virtuoso } from "react-virtuoso";
|
||||||
@@ -9,6 +9,7 @@ import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
|||||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { ExclamationCircleOutlined } from "@ant-design/icons";
|
||||||
import "./chat-conversation-list.styles.scss";
|
import "./chat-conversation-list.styles.scss";
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { GET_PHONE_NUMBER_OPT_OUTS } from "../../graphql/phone-number-opt-out.queries.js";
|
import { GET_PHONE_NUMBER_OPT_OUTS } from "../../graphql/phone-number-opt-out.queries.js";
|
||||||
@@ -88,7 +89,13 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
|
|||||||
const cardExtra = (
|
const cardExtra = (
|
||||||
<>
|
<>
|
||||||
<Badge count={item.messages_aggregate.aggregate.count} />
|
<Badge count={item.messages_aggregate.aggregate.count} />
|
||||||
{hasOptOutEntry && <Tag color="red">{t("messaging.labels.no_consent")}</Tag>}
|
{hasOptOutEntry && (
|
||||||
|
<Tooltip title={t("consent.text_body")}>
|
||||||
|
<Tag color="red" icon={<ExclamationCircleOutlined />}>
|
||||||
|
{t("messaging.labels.no_consent")}
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
|||||||
userid
|
userid
|
||||||
created_at
|
created_at
|
||||||
read
|
read
|
||||||
|
is_system
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
data: message
|
data: message
|
||||||
|
|||||||
@@ -4,13 +4,16 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.archive-button {
|
.archive-button {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-title {
|
.chat-title {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messages {
|
.messages {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -37,11 +40,13 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.chat-send-message-button{
|
|
||||||
|
.chat-send-message-button {
|
||||||
margin: 0.3rem;
|
margin: 0.3rem;
|
||||||
padding-left: 0.5rem;
|
padding-left: 0.5rem;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-icon {
|
.message-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0.1rem;
|
bottom: 0.1rem;
|
||||||
@@ -125,6 +130,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.system {
|
||||||
|
align-items: center;
|
||||||
|
margin: 0.5rem 10%;
|
||||||
|
|
||||||
|
.message {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
text-align: center;
|
||||||
|
font-style: italic;
|
||||||
|
color: #555;
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-date {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #888;
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.virtuoso-container {
|
.virtuoso-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
@@ -7,12 +7,24 @@ import { DateTimeFormatter } from "../../utils/DateFormatter";
|
|||||||
|
|
||||||
export const renderMessage = (messages, index) => {
|
export const renderMessage = (messages, index) => {
|
||||||
const message = messages[index];
|
const message = messages[index];
|
||||||
|
const isSystem = message.is_system;
|
||||||
|
|
||||||
|
// Determine message class
|
||||||
|
const messageClass = isSystem ? "system messages" : message.isoutbound ? "mine messages" : "yours messages";
|
||||||
|
|
||||||
|
// Tooltip content based on message type
|
||||||
|
const tooltipTitle = isSystem ? (
|
||||||
|
i18n.t("consent.text_body")
|
||||||
|
) : (
|
||||||
|
<DateTimeFormatter>{message.created_at}</DateTimeFormatter>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={index} className={`${message.isoutbound ? "mine messages" : "yours messages"}`}>
|
<div key={index} className={messageClass}>
|
||||||
<div className="message msgmargin">
|
<div className="message msgmargin">
|
||||||
<Tooltip title={DateTimeFormatter({ children: message.created_at })}>
|
<Tooltip title={tooltipTitle}>
|
||||||
<div>
|
<div>
|
||||||
|
{isSystem && <span className="system-label">System</span>}
|
||||||
{/* Render images if available */}
|
{/* Render images if available */}
|
||||||
{message.image && message.image_path?.length > 0 && (
|
{message.image && message.image_path?.length > 0 && (
|
||||||
<div className="message-images">
|
<div className="message-images">
|
||||||
@@ -26,23 +38,31 @@ export const renderMessage = (messages, index) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Render text if available */}
|
{/* Render text if available */}
|
||||||
{message.text && <div>{message.text}</div>}
|
{message.text && <div className="message-text">{message.text}</div>}
|
||||||
|
{/* Render date for system messages */}
|
||||||
|
{isSystem && (
|
||||||
|
<div className="system-date">
|
||||||
|
<DateTimeFormatter>{message.created_at}</DateTimeFormatter>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{/* Message status icons */}
|
{/* Message status icons for non-system messages */}
|
||||||
{message.status &&
|
{!isSystem &&
|
||||||
|
message.status &&
|
||||||
(message.status === "sent" || message.status === "delivered" || message.status === "failed") && (
|
(message.status === "sent" || message.status === "delivered" || message.status === "failed") && (
|
||||||
<div className="message-status">
|
<div className="message-status">
|
||||||
<Icon
|
<Icon
|
||||||
component={message.status === "sent" ? MdDone : message.status === "delivered" ? MdDoneAll : MdClose}
|
component={message.status === "sent" ? MdDone : message.status === "delivered" ? MdDoneAll : MdClose}
|
||||||
className="message-icon"
|
className="message-icon"
|
||||||
|
style={message.status === "failed" ? { color: "#ff0000" } : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Outbound message metadata */}
|
{/* Outbound message metadata for non-system messages */}
|
||||||
{message.isoutbound && (
|
{!isSystem && message.isoutbound && (
|
||||||
<div style={{ fontSize: 10 }}>
|
<div style={{ fontSize: 10 }}>
|
||||||
{i18n.t("messaging.labels.sentby", {
|
{i18n.t("messaging.labels.sentby", {
|
||||||
by: message.userid,
|
by: message.userid,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LoadingOutlined, SendOutlined } from "@ant-design/icons";
|
import { ExclamationCircleOutlined, LoadingOutlined, SendOutlined } from "@ant-design/icons";
|
||||||
import { Alert, Input, Space, Spin } from "antd";
|
import { Alert, Input, Space, Spin, Tooltip } from "antd";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -69,7 +69,16 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSendi
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="middle">
|
<Space direction="vertical" style={{ width: "100%" }} size="middle">
|
||||||
{isOptedOut && <Alert message={t("messaging.errors.no_consent")} type="error" />}
|
{isOptedOut && (
|
||||||
|
<Tooltip title={t("consent.text_body")}>
|
||||||
|
<Alert
|
||||||
|
showIcon={true}
|
||||||
|
icon={<ExclamationCircleOutlined />}
|
||||||
|
message={t("messaging.errors.no_consent")}
|
||||||
|
type="error"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
<div className="imex-flex-row" style={{ width: "100%" }}>
|
<div className="imex-flex-row" style={{ width: "100%" }}>
|
||||||
{!isOptedOut && (
|
{!isOptedOut && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Input, Space, Table, Typography } from "antd";
|
import { Input, Table, Typography } from "antd";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { useMemo, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import { GET_PHONE_NUMBER_OPT_OUTS, SEARCH_OWNERS_BY_PHONE_NUMBERS } from "../../graphql/phone-number-opt-out.queries";
|
import { GET_PHONE_NUMBER_OPT_OUTS } from "../../graphql/phone-number-opt-out.queries";
|
||||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
|
||||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
const { Paragraph } = Typography; // Destructure Paragraph from Typography
|
const { Paragraph } = Typography;
|
||||||
|
|
||||||
|
// Commented out Associated Owners section for now
|
||||||
|
//import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
|
//import { Link } from "react-router-dom";
|
||||||
|
//import { useMemo, useState } from "react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -30,7 +33,8 @@ function PhoneNumberConsentList({ bodyshop, currentUser }) {
|
|||||||
fetchPolicy: "network-only"
|
fetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prepare phone numbers for owner query
|
// Commented out Associated Owners section for now
|
||||||
|
/*// Prepare phone numbers for owner query
|
||||||
const phoneNumbers = useMemo(() => {
|
const phoneNumbers = useMemo(() => {
|
||||||
return optOutData?.phone_number_opt_out?.map((item) => item.phone_number) || [];
|
return optOutData?.phone_number_opt_out?.map((item) => item.phone_number) || [];
|
||||||
}, [optOutData?.phone_number_opt_out]);
|
}, [optOutData?.phone_number_opt_out]);
|
||||||
@@ -74,16 +78,17 @@ function PhoneNumberConsentList({ bodyshop, currentUser }) {
|
|||||||
: t("consent.phone_2")
|
: t("consent.phone_2")
|
||||||
: null
|
: null
|
||||||
}));
|
}));
|
||||||
};
|
};*/
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("consent.phone_number"),
|
title: t("consent.phone_number"),
|
||||||
dataIndex: "phone_number",
|
dataIndex: "phone_number",
|
||||||
render: (text) => <PhoneNumberFormatter>{text}</PhoneNumberFormatter>,
|
render: (text) => <ChatOpenButton phone={text} />,
|
||||||
sorter: (a, b) => a.phone_number.localeCompare(b.phone_number)
|
sorter: (a, b) => a.phone_number.localeCompare(b.phone_number)
|
||||||
},
|
},
|
||||||
{
|
// Commented out Associated Owners section for now
|
||||||
|
/*{
|
||||||
title: t("consent.associated_owners"),
|
title: t("consent.associated_owners"),
|
||||||
dataIndex: "phone_number",
|
dataIndex: "phone_number",
|
||||||
render: (phoneNumber) => {
|
render: (phoneNumber) => {
|
||||||
@@ -109,7 +114,7 @@ function PhoneNumberConsentList({ bodyshop, currentUser }) {
|
|||||||
const bName = bOwners[0] ? `${bOwners[0].ownr_fn} ${bOwners[0].ownr_ln}` : "";
|
const bName = bOwners[0] ? `${bOwners[0].ownr_fn} ${bOwners[0].ownr_ln}` : "";
|
||||||
return aName.localeCompare(bName);
|
return aName.localeCompare(bName);
|
||||||
}
|
}
|
||||||
},
|
},*/
|
||||||
{
|
{
|
||||||
title: t("consent.created_at"),
|
title: t("consent.created_at"),
|
||||||
dataIndex: "created_at",
|
dataIndex: "created_at",
|
||||||
@@ -130,7 +135,7 @@ function PhoneNumberConsentList({ bodyshop, currentUser }) {
|
|||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={optOutData?.phone_number_opt_out}
|
dataSource={optOutData?.phone_number_opt_out}
|
||||||
loading={optOutLoading || ownersLoading}
|
loading={optOutLoading /* || ownersLoading*/}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
style={{ marginTop: 16 }}
|
style={{ marginTop: 16 }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export const CONVERSATION_SUBSCRIPTION_BY_PK = gql`
|
|||||||
id
|
id
|
||||||
status
|
status
|
||||||
text
|
text
|
||||||
|
is_system
|
||||||
isoutbound
|
isoutbound
|
||||||
image
|
image
|
||||||
image_path
|
image_path
|
||||||
@@ -77,6 +78,7 @@ export const GET_CONVERSATION_DETAILS = gql`
|
|||||||
id
|
id
|
||||||
status
|
status
|
||||||
text
|
text
|
||||||
|
is_system
|
||||||
isoutbound
|
isoutbound
|
||||||
image
|
image
|
||||||
image_path
|
image_path
|
||||||
|
|||||||
@@ -4742,6 +4742,7 @@
|
|||||||
- id
|
- id
|
||||||
- image
|
- image
|
||||||
- image_path
|
- image_path
|
||||||
|
- is_system
|
||||||
- isoutbound
|
- isoutbound
|
||||||
- msid
|
- msid
|
||||||
- read
|
- read
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."messages" add column "is_system" boolean
|
||||||
|
-- null default 'false';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."messages" add column "is_system" boolean
|
||||||
|
null default 'false';
|
||||||
@@ -15,7 +15,13 @@ const InstanceManager = require("../utils/instanceMgr").default;
|
|||||||
// Note: When we handle different languages, we might need to adjust these keywords accordingly.
|
// Note: When we handle different languages, we might need to adjust these keywords accordingly.
|
||||||
const optInKeywords = ["START", "YES", "UNSTOP"];
|
const optInKeywords = ["START", "YES", "UNSTOP"];
|
||||||
const optOutKeywords = ["STOP", "STOPALL", "UNSUBSCRIBE", "CANCEL", "END", "QUIT", "REVOKE", "OPTOUT"];
|
const optOutKeywords = ["STOP", "STOPALL", "UNSUBSCRIBE", "CANCEL", "END", "QUIT", "REVOKE", "OPTOUT"];
|
||||||
|
|
||||||
|
// System Message text, will also need to be localized if we support multiple languages
|
||||||
|
const systemMessageOptions = {
|
||||||
|
optIn: "Customer has Opted-in",
|
||||||
|
optOut: "Customer has Opted-out"
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive SMS messages from Twilio and process them
|
* Receive SMS messages from Twilio and process them
|
||||||
* @param req
|
* @param req
|
||||||
@@ -61,7 +67,38 @@ const receive = async (req, res) => {
|
|||||||
const normalizedPhone = phone(req.body.From).phoneNumber.replace(/^\+1/, ""); // Normalize phone number (remove +1 for CA numbers)
|
const normalizedPhone = phone(req.body.From).phoneNumber.replace(/^\+1/, ""); // Normalize phone number (remove +1 for CA numbers)
|
||||||
const messageText = (req.body.Body || "").trim().toUpperCase();
|
const messageText = (req.body.Body || "").trim().toUpperCase();
|
||||||
|
|
||||||
// Step 2: Check for opt-in or opt-out keywords
|
// Step 2: Process conversation
|
||||||
|
const sortedConversations = bodyshop.conversations.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
|
||||||
|
const existingConversation = sortedConversations.length
|
||||||
|
? sortedConversations[sortedConversations.length - 1]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
let conversationid;
|
||||||
|
|
||||||
|
if (existingConversation) {
|
||||||
|
conversationid = existingConversation.id;
|
||||||
|
if (existingConversation.archived) {
|
||||||
|
await client.request(UNARCHIVE_CONVERSATION, {
|
||||||
|
id: conversationid,
|
||||||
|
archived: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const newConversationResponse = await client.request(CREATE_CONVERSATION, {
|
||||||
|
conversation: {
|
||||||
|
bodyshopid: bodyshop.id,
|
||||||
|
phone_num: phone(req.body.From).phoneNumber,
|
||||||
|
archived: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const createdConversation = newConversationResponse.insert_conversations.returning[0];
|
||||||
|
conversationid = createdConversation.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Handle opt-in or opt-out keywords
|
||||||
|
let systemMessageText = "";
|
||||||
|
let socketEventType = "";
|
||||||
|
|
||||||
if (optInKeywords.includes(messageText) || optOutKeywords.includes(messageText)) {
|
if (optInKeywords.includes(messageText) || optOutKeywords.includes(messageText)) {
|
||||||
// Check if the phone number is in phone_number_opt_out
|
// Check if the phone number is in phone_number_opt_out
|
||||||
const optOutCheck = await client.request(CHECK_PHONE_NUMBER_OPT_OUT, {
|
const optOutCheck = await client.request(CHECK_PHONE_NUMBER_OPT_OUT, {
|
||||||
@@ -69,6 +106,7 @@ const receive = async (req, res) => {
|
|||||||
phone_number: normalizedPhone
|
phone_number: normalizedPhone
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Opt In
|
||||||
if (optInKeywords.includes(messageText)) {
|
if (optInKeywords.includes(messageText)) {
|
||||||
// Handle opt-in
|
// Handle opt-in
|
||||||
if (optOutCheck.phone_number_opt_out.length > 0) {
|
if (optOutCheck.phone_number_opt_out.length > 0) {
|
||||||
@@ -85,14 +123,12 @@ const receive = async (req, res) => {
|
|||||||
affected_rows: deleteResponse.delete_phone_number_opt_out.affected_rows
|
affected_rows: deleteResponse.delete_phone_number_opt_out.affected_rows
|
||||||
});
|
});
|
||||||
|
|
||||||
// Emit WebSocket event to notify clients
|
systemMessageText = systemMessageOptions.optIn;
|
||||||
const broadcastRoom = getBodyshopRoom(bodyshop.id);
|
socketEventType = "phone-number-opted-in";
|
||||||
ioRedis.to(broadcastRoom).emit("phone-number-opted-in", {
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
phone_number: normalizedPhone
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else if (optOutKeywords.includes(messageText)) {
|
}
|
||||||
|
// Opt Out
|
||||||
|
else if (optOutKeywords.includes(messageText)) {
|
||||||
// Handle opt-out
|
// Handle opt-out
|
||||||
if (optOutCheck.phone_number_opt_out.length === 0) {
|
if (optOutCheck.phone_number_opt_out.length === 0) {
|
||||||
// Phone number is not opted out; insert a new record
|
// Phone number is not opted out; insert a new record
|
||||||
@@ -115,59 +151,78 @@ const receive = async (req, res) => {
|
|||||||
affected_rows: insertResponse.insert_phone_number_opt_out.affected_rows
|
affected_rows: insertResponse.insert_phone_number_opt_out.affected_rows
|
||||||
});
|
});
|
||||||
|
|
||||||
// Emit WebSocket event to notify clients
|
systemMessageText = systemMessageOptions.optOut;
|
||||||
const broadcastRoom = getBodyshopRoom(bodyshop.id);
|
socketEventType = "phone-number-opted-out";
|
||||||
ioRedis.to(broadcastRoom).emit("phone-number-opted-out", {
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
phone_number: normalizedPhone
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond immediately without processing as a regular message
|
// Insert system message if an opt-in or opt-out action was taken
|
||||||
res.status(200).send("");
|
if (systemMessageText) {
|
||||||
return;
|
const systemMessage = {
|
||||||
|
msid: `SYS_${req.body.SmsMessageSid}_${Date.now()}`, // Unique ID for system message
|
||||||
|
text: systemMessageText,
|
||||||
|
conversationid,
|
||||||
|
isoutbound: false,
|
||||||
|
userid: null,
|
||||||
|
image: false,
|
||||||
|
image_path: null,
|
||||||
|
is_system: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const systemMessageResponse = await client.request(INSERT_MESSAGE, {
|
||||||
|
msg: systemMessage,
|
||||||
|
conversationid
|
||||||
|
});
|
||||||
|
|
||||||
|
const insertedSystemMessage = systemMessageResponse.insert_messages.returning[0];
|
||||||
|
|
||||||
|
// Emit WebSocket events for system message
|
||||||
|
const broadcastRoom = getBodyshopRoom(bodyshop.id);
|
||||||
|
const conversationRoom = getBodyshopConversationRoom({
|
||||||
|
bodyshopId: bodyshop.id,
|
||||||
|
conversationId: conversationid
|
||||||
|
});
|
||||||
|
|
||||||
|
const systemPayload = {
|
||||||
|
isoutbound: false,
|
||||||
|
conversationId: conversationid,
|
||||||
|
updated_at: insertedSystemMessage.updated_at,
|
||||||
|
msid: insertedSystemMessage.msid,
|
||||||
|
existingConversation: !!existingConversation,
|
||||||
|
newConversation: !existingConversation ? insertedSystemMessage.conversation : null
|
||||||
|
};
|
||||||
|
|
||||||
|
ioRedis.to(broadcastRoom).emit("new-message-summary", {
|
||||||
|
...systemPayload,
|
||||||
|
summary: true
|
||||||
|
});
|
||||||
|
|
||||||
|
ioRedis.to(conversationRoom).emit("new-message-detailed", {
|
||||||
|
newMessage: insertedSystemMessage,
|
||||||
|
...systemPayload,
|
||||||
|
summary: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emit opt-in or opt-out event
|
||||||
|
ioRedis.to(broadcastRoom).emit(socketEventType, {
|
||||||
|
bodyshopid: bodyshop.id,
|
||||||
|
phone_number: normalizedPhone
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Process conversation
|
// Step 4: Insert the original message
|
||||||
const sortedConversations = bodyshop.conversations.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
|
const newMessage = {
|
||||||
const existingConversation = sortedConversations.length
|
|
||||||
? sortedConversations[sortedConversations.length - 1]
|
|
||||||
: null;
|
|
||||||
|
|
||||||
let conversationid;
|
|
||||||
let newMessage = {
|
|
||||||
msid: req.body.SmsMessageSid,
|
msid: req.body.SmsMessageSid,
|
||||||
text: req.body.Body,
|
text: req.body.Body,
|
||||||
image: !!req.body.MediaUrl0,
|
image: !!req.body.MediaUrl0,
|
||||||
image_path: generateMediaArray(req.body, logger),
|
image_path: generateMediaArray(req.body, logger),
|
||||||
isoutbound: false,
|
isoutbound: false,
|
||||||
userid: null
|
userid: null,
|
||||||
|
conversationid,
|
||||||
|
is_system: false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (existingConversation) {
|
|
||||||
conversationid = existingConversation.id;
|
|
||||||
if (existingConversation.archived) {
|
|
||||||
await client.request(UNARCHIVE_CONVERSATION, {
|
|
||||||
id: conversationid,
|
|
||||||
archived: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const newConversationResponse = await client.request(CREATE_CONVERSATION, {
|
|
||||||
conversation: {
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
phone_num: phone(req.body.From).phoneNumber,
|
|
||||||
archived: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const createdConversation = newConversationResponse.insert_conversations.returning[0];
|
|
||||||
conversationid = createdConversation.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
newMessage.conversationid = conversationid;
|
|
||||||
|
|
||||||
// Step 4: Insert the message
|
|
||||||
const insertresp = await client.request(INSERT_MESSAGE, {
|
const insertresp = await client.request(INSERT_MESSAGE, {
|
||||||
msg: newMessage,
|
msg: newMessage,
|
||||||
conversationid
|
conversationid
|
||||||
@@ -180,7 +235,7 @@ const receive = async (req, res) => {
|
|||||||
throw new Error("Conversation data is missing from the response.");
|
throw new Error("Conversation data is missing from the response.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Notify clients
|
// Step 5: Notify clients for original message
|
||||||
const conversationRoom = getBodyshopConversationRoom({
|
const conversationRoom = getBodyshopConversationRoom({
|
||||||
bodyshopId: conversation.bodyshop.id,
|
bodyshopId: conversation.bodyshop.id,
|
||||||
conversationId: conversation.id
|
conversationId: conversation.id
|
||||||
@@ -190,7 +245,7 @@ const receive = async (req, res) => {
|
|||||||
isoutbound: false,
|
isoutbound: false,
|
||||||
conversationId: conversation.id,
|
conversationId: conversation.id,
|
||||||
updated_at: message.updated_at,
|
updated_at: message.updated_at,
|
||||||
msid: message.sid
|
msid: message.msid
|
||||||
};
|
};
|
||||||
|
|
||||||
const broadcastRoom = getBodyshopRoom(conversation.bodyshop.id);
|
const broadcastRoom = getBodyshopRoom(conversation.bodyshop.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user