diff --git a/client/package-lock.json b/client/package-lock.json
index fd218cc50..42436e972 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -20,8 +20,8 @@
"@firebase/messaging": "^0.12.21",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.8.2",
- "@sentry/cli": "^2.45.0",
- "@sentry/react": "^9.22.0",
+ "@sentry/cli": "^2.46.0",
+ "@sentry/react": "^9.23.0",
"@sentry/vite-plugin": "^3.5.0",
"@splitsoftware/splitio-react": "^2.1.1",
"@tanem/react-nprogress": "^5.0.53",
@@ -4469,50 +4469,50 @@
"license": "MIT"
},
"node_modules/@sentry-internal/browser-utils": {
- "version": "9.22.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.22.0.tgz",
- "integrity": "sha512-Ou1tBnVxFAIn8i9gvrWzRotNJQYiu3awNXpsFCw6qFwmiKAVPa6b13vCdolhXnrIiuR77jY1LQnKh9hXpoRzsg==",
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.23.0.tgz",
+ "integrity": "sha512-hyN2Q6mh7ggw8sDVHeRyWz5LR6gjvf8zHSzQnMaF7QkeSyaeGM/SVSL4ODwqR9TRH7U2ku6nZFMbKhaBPV+Hfg==",
"license": "MIT",
"dependencies": {
- "@sentry/core": "9.22.0"
+ "@sentry/core": "9.23.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/feedback": {
- "version": "9.22.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.22.0.tgz",
- "integrity": "sha512-zgMVkoC61fgi41zLcSZA59vOtKxcLrKBo1ECYhPD1hxEaneNqY5fhXDwlQBw96P5l2yqkgfX6YZtSdU4ejI9yA==",
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.23.0.tgz",
+ "integrity": "sha512-Xf+KqV69TBiPo1gk2EsU6O/dumuTMxWOF52uVWJddQYI3pQTU5DqSeoZ5AY76bIIhV9n6AEFDGqNPXmuj4Acrw==",
"license": "MIT",
"dependencies": {
- "@sentry/core": "9.22.0"
+ "@sentry/core": "9.23.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/replay": {
- "version": "9.22.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.22.0.tgz",
- "integrity": "sha512-9GOycoKbrclcRXfcbNV8svbmAsOS5R4wXBQmKF4pFLkmFA/lJv9kdZSNYkRvkrxdNfbMIJXP+DV9EqTZcryXig==",
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.23.0.tgz",
+ "integrity": "sha512-0/q15tvSboaK7/05BFQhs71bqgHKejJoDJgXmH0lySqgsRh/S18867ZxQNiuYhuVt337h07u1QaCyjnNJKHfuA==",
"license": "MIT",
"dependencies": {
- "@sentry-internal/browser-utils": "9.22.0",
- "@sentry/core": "9.22.0"
+ "@sentry-internal/browser-utils": "9.23.0",
+ "@sentry/core": "9.23.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry-internal/replay-canvas": {
- "version": "9.22.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.22.0.tgz",
- "integrity": "sha512-EcG9IMSEalFe49kowBTJObWjof/iHteDwpyuAszsFDdQUYATrVUtwpwN7o52vDYWJud4arhjrQnMamIGxa79eQ==",
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.23.0.tgz",
+ "integrity": "sha512-cYlw5svJjyPequm0PJjFGLpee86L1NieONEHlujOXkIG6IEriiorMm+8bNpGsHRuyvg41B+4P/YmcQAGtEGxXg==",
"license": "MIT",
"dependencies": {
- "@sentry-internal/replay": "9.22.0",
- "@sentry/core": "9.22.0"
+ "@sentry-internal/replay": "9.23.0",
+ "@sentry/core": "9.23.0"
},
"engines": {
"node": ">=18"
@@ -4528,16 +4528,16 @@
}
},
"node_modules/@sentry/browser": {
- "version": "9.22.0",
- "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.22.0.tgz",
- "integrity": "sha512-3TeRm74dvX0JdjX0AgkQa+22iUHwHnY+Q6M05NZ+tDeCNHGK/mEBTeqquS1oQX67jWyuvYmG3VV6RJUxtG9Paw==",
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.23.0.tgz",
+ "integrity": "sha512-QRkNxWys8e088260vByztoTsEVZ0W6v/XnZfKT6wkPPGn0aFeOrg/xjgxfI8D5huqZCxT28Cf23akOOly4FXjg==",
"license": "MIT",
"dependencies": {
- "@sentry-internal/browser-utils": "9.22.0",
- "@sentry-internal/feedback": "9.22.0",
- "@sentry-internal/replay": "9.22.0",
- "@sentry-internal/replay-canvas": "9.22.0",
- "@sentry/core": "9.22.0"
+ "@sentry-internal/browser-utils": "9.23.0",
+ "@sentry-internal/feedback": "9.23.0",
+ "@sentry-internal/replay": "9.23.0",
+ "@sentry-internal/replay-canvas": "9.23.0",
+ "@sentry/core": "9.23.0"
},
"engines": {
"node": ">=18"
@@ -4728,9 +4728,9 @@
}
},
"node_modules/@sentry/cli": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.45.0.tgz",
- "integrity": "sha512-4sWu7zgzgHAjIxIjXUA/66qgeEf5ZOlloO+/JaGD5qXNSW0G7KMTR6iYjReNKMgdBCTH6bUUt9qiuA+Ex9Masw==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.46.0.tgz",
+ "integrity": "sha512-nqoPl7UCr446QFkylrsRrUXF51x8Z9dGquyf4jaQU+OzbOJMqclnYEvU6iwbwvaw3tu/2DnoZE/Og+Nq1h63sA==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -4747,20 +4747,20 @@
"node": ">= 10"
},
"optionalDependencies": {
- "@sentry/cli-darwin": "2.45.0",
- "@sentry/cli-linux-arm": "2.45.0",
- "@sentry/cli-linux-arm64": "2.45.0",
- "@sentry/cli-linux-i686": "2.45.0",
- "@sentry/cli-linux-x64": "2.45.0",
- "@sentry/cli-win32-arm64": "2.45.0",
- "@sentry/cli-win32-i686": "2.45.0",
- "@sentry/cli-win32-x64": "2.45.0"
+ "@sentry/cli-darwin": "2.46.0",
+ "@sentry/cli-linux-arm": "2.46.0",
+ "@sentry/cli-linux-arm64": "2.46.0",
+ "@sentry/cli-linux-i686": "2.46.0",
+ "@sentry/cli-linux-x64": "2.46.0",
+ "@sentry/cli-win32-arm64": "2.46.0",
+ "@sentry/cli-win32-i686": "2.46.0",
+ "@sentry/cli-win32-x64": "2.46.0"
}
},
"node_modules/@sentry/cli-darwin": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.45.0.tgz",
- "integrity": "sha512-p4Uxfv/L2fQdP3/wYnKVVz9gzZJf/1Xp9D+6raax/3Bu5y87yHYUqcdt98y/VAXQD4ofp2QgmhGUVPofvQNZmg==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.46.0.tgz",
+ "integrity": "sha512-5Ll+e5KAdIk9OYiZO8aifMBRNWmNyPjSqdjaHlBC1Qfh7pE3b1zyzoHlsUazG0bv0sNrSGea8e7kF5wIO1hvyg==",
"license": "BSD-3-Clause",
"optional": true,
"os": [
@@ -4771,9 +4771,9 @@
}
},
"node_modules/@sentry/cli-linux-arm": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.45.0.tgz",
- "integrity": "sha512-6sEskFLlFKJ+e0MOYgIclBTUX5jYMyYhHIxXahEkI/4vx6JO0uvpyRAkUJRpJkRh/lPog0FM+tbP3so+VxB2qQ==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.46.0.tgz",
+ "integrity": "sha512-WRrLNq/TEX/TNJkGqq6Ad0tGyapd5dwlxtsPbVBrIdryuL1mA7VCBoaHBr3kcwJLsgBHFH0lmkMee2ubNZZdkg==",
"cpu": [
"arm"
],
@@ -4781,16 +4781,17 @@
"optional": true,
"os": [
"linux",
- "freebsd"
+ "freebsd",
+ "android"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-linux-arm64": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.45.0.tgz",
- "integrity": "sha512-gUcLoEjzg7AIc4QQGEZwRHri+EHf3Gcms9zAR1VHiNF3/C/jL4WeDPJF2YiWAQt6EtH84tHiyhw1Ab/R8XFClg==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.46.0.tgz",
+ "integrity": "sha512-OEJN8yAjI9y5B4telyqzu27Hi3+S4T8VxZCqJz1+z2Mp0Q/MZ622AahVPpcrVq/5bxrnlZR16+lKh8L1QwNFPg==",
"cpu": [
"arm64"
],
@@ -4798,16 +4799,17 @@
"optional": true,
"os": [
"linux",
- "freebsd"
+ "freebsd",
+ "android"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-linux-i686": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.45.0.tgz",
- "integrity": "sha512-VmmOaEAzSW23YdGNdy/+oQjCNAMY+HmOGA77A25/ep/9AV7PQB6FI7xO5Y1PVvlkxZFJ23e373njSsEeg4uDZw==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.46.0.tgz",
+ "integrity": "sha512-xko3/BVa4LX8EmRxVOCipV+PwfcK5Xs8lP6lgF+7NeuAHMNL4DqF6iV9rrN8gkGUHCUI9RXSve37uuZnFy55+Q==",
"cpu": [
"x86",
"ia32"
@@ -4816,16 +4818,17 @@
"optional": true,
"os": [
"linux",
- "freebsd"
+ "freebsd",
+ "android"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-linux-x64": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.45.0.tgz",
- "integrity": "sha512-a0Oj68mrb25a0WjX/ShZ6AAd4PPiuLcgyzQr7bl2+DvYxIOajwkGbR+CZFEhOVZcfhTnixKy/qIXEzApEPHPQg==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.46.0.tgz",
+ "integrity": "sha512-hJ1g5UEboYcOuRia96LxjJ0jhnmk8EWLDvlGnXLnYHkwy3ree/L7sNgdp/QsY8Z4j2PGO5f22Va+UDhSjhzlfQ==",
"cpu": [
"x64"
],
@@ -4833,16 +4836,17 @@
"optional": true,
"os": [
"linux",
- "freebsd"
+ "freebsd",
+ "android"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-win32-arm64": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.45.0.tgz",
- "integrity": "sha512-vn+CwS4p+52pQSLNPoi20ZOrQmv01ZgAmuMnjkh1oUZfTyBAwWLrAh6Cy4cztcN8DfL5dOWKQBo8DBKURE4ttg==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.46.0.tgz",
+ "integrity": "sha512-mN7cpPoCv2VExFRGHt+IoK11yx4pM4ADZQGEso5BAUZ5duViXB2WrAXCLd8DrwMnP0OE978a7N8OtzsFqjkbNA==",
"cpu": [
"arm64"
],
@@ -4856,9 +4860,9 @@
}
},
"node_modules/@sentry/cli-win32-i686": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.45.0.tgz",
- "integrity": "sha512-8mMoDdlwxtcdNIMtteMK7dbi7054jak8wKSHJ5yzMw8UmWxC5thc/gXBc1uPduiaI56VjoJV+phWHBKCD+6I4w==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.46.0.tgz",
+ "integrity": "sha512-6F73AUE3lm71BISUO19OmlnkFD5WVe4/wA1YivtLZTc1RU3eUYJLYxhDfaH3P77+ycDppQ2yCgemLRaA4A8mNQ==",
"cpu": [
"x86",
"ia32"
@@ -4873,9 +4877,9 @@
}
},
"node_modules/@sentry/cli-win32-x64": {
- "version": "2.45.0",
- "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.45.0.tgz",
- "integrity": "sha512-ZvK9cIqFaq7vZ0jkHJ/xh5au6902Dr+AUxSk6L6vCL7JCe2p93KGL/4d8VFB5PD/P7Y9b+105G/e0QIFKzpeOw==",
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.46.0.tgz",
+ "integrity": "sha512-yuGVcfepnNL84LGA0GjHzdMIcOzMe0bjPhq/rwPsPN+zu11N+nPR2wV2Bum4U0eQdqYH3iAlMdL5/BEQfuLJww==",
"cpu": [
"x64"
],
@@ -4910,22 +4914,22 @@
}
},
"node_modules/@sentry/core": {
- "version": "9.22.0",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.22.0.tgz",
- "integrity": "sha512-ixvtKmPF42Y6ckGUbFlB54OWI75H2gO5UYHojO6eXFpS7xO3ZGgV/QH6wb40mWK+0w5XZ0233FuU9VpsuE6mKA==",
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.23.0.tgz",
+ "integrity": "sha512-9846pn/BvASGgl7WsnKY4xry98WreP9ToeLfCQTQOf+pNfD/qNPn1/0xPInGni3LVMAXRtfHHMPm2Ghz255N7A==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@sentry/react": {
- "version": "9.22.0",
- "resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.22.0.tgz",
- "integrity": "sha512-mI43NnioBYdG5TiXqRlhV1feZs9bnrrl+k5HOHBK7VQtymaXO0fkcsRLZTkdSgLRLMJGasZuvVhq2xK+18QyWQ==",
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.23.0.tgz",
+ "integrity": "sha512-2J/oOx8jd7Jr2koYIe5IcJyStHBXpjkQnxawo54Zyyvzc96MftyM2Dv5TeYdz7fChU1NIXw7BVbEpkQ9XEQlqg==",
"license": "MIT",
"dependencies": {
- "@sentry/browser": "9.22.0",
- "@sentry/core": "9.22.0",
+ "@sentry/browser": "9.23.0",
+ "@sentry/core": "9.23.0",
"hoist-non-react-statics": "^3.3.2"
},
"engines": {
diff --git a/client/package.json b/client/package.json
index 729aee7e2..61af4372d 100644
--- a/client/package.json
+++ b/client/package.json
@@ -19,8 +19,8 @@
"@firebase/messaging": "^0.12.21",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.8.2",
- "@sentry/cli": "^2.45.0",
- "@sentry/react": "^9.22.0",
+ "@sentry/cli": "^2.46.0",
+ "@sentry/react": "^9.23.0",
"@sentry/vite-plugin": "^3.5.0",
"@splitsoftware/splitio-react": "^2.1.1",
"@tanem/react-nprogress": "^5.0.53",
diff --git a/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx b/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx
index 8bf185756..e5fe4b9ca 100644
--- a/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx
+++ b/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx
@@ -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 { connect } from "react-redux";
import { Virtuoso } from "react-virtuoso";
@@ -9,6 +9,7 @@ import { TimeAgoFormatter } from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import _ from "lodash";
+import { ExclamationCircleOutlined } from "@ant-design/icons";
import "./chat-conversation-list.styles.scss";
import { useQuery } from "@apollo/client";
import { GET_PHONE_NUMBER_OPT_OUTS } from "../../graphql/phone-number-opt-out.queries.js";
@@ -88,7 +89,13 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
const cardExtra = (
<>
- {hasOptOutEntry && {t("messaging.labels.no_consent")}}
+ {hasOptOutEntry && (
+
+ }>
+ {t("messaging.labels.no_consent")}
+
+
+ )}
>
);
diff --git a/client/src/components/chat-conversation/chat-conversation.container.jsx b/client/src/components/chat-conversation/chat-conversation.container.jsx
index f951aabd1..e53ec8172 100644
--- a/client/src/components/chat-conversation/chat-conversation.container.jsx
+++ b/client/src/components/chat-conversation/chat-conversation.container.jsx
@@ -58,6 +58,7 @@ function ChatConversationContainer({ bodyshop, selectedConversation }) {
userid
created_at
read
+ is_system
}
`,
data: message
diff --git a/client/src/components/chat-messages-list/chat-message-list.styles.scss b/client/src/components/chat-messages-list/chat-message-list.styles.scss
index 98f5ab8a4..969de0c82 100644
--- a/client/src/components/chat-messages-list/chat-message-list.styles.scss
+++ b/client/src/components/chat-messages-list/chat-message-list.styles.scss
@@ -4,13 +4,16 @@
height: 100%;
width: 100%;
}
+
.archive-button {
height: 20px;
border-radius: 4px;
}
+
.chat-title {
margin-bottom: 5px;
}
+
.messages {
display: flex;
flex-direction: column;
@@ -37,11 +40,13 @@
gap: 8px;
}
}
-.chat-send-message-button{
+
+.chat-send-message-button {
margin: 0.3rem;
padding-left: 0.5rem;
-
+
}
+
.message-icon {
position: absolute;
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 {
flex: 1;
overflow: auto;
diff --git a/client/src/components/chat-messages-list/renderMessage.jsx b/client/src/components/chat-messages-list/renderMessage.jsx
index c572d77e3..2d1e1ce75 100644
--- a/client/src/components/chat-messages-list/renderMessage.jsx
+++ b/client/src/components/chat-messages-list/renderMessage.jsx
@@ -7,12 +7,24 @@ import { DateTimeFormatter } from "../../utils/DateFormatter";
export const renderMessage = (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")
+ ) : (
+ {message.created_at}
+ );
return (
-
+
-
+
+ {isSystem &&
System}
{/* Render images if available */}
{message.image && message.image_path?.length > 0 && (
@@ -26,23 +38,31 @@ export const renderMessage = (messages, index) => {
)}
{/* Render text if available */}
- {message.text &&
{message.text}
}
+ {message.text &&
{message.text}
}
+ {/* Render date for system messages */}
+ {isSystem && (
+
+ {message.created_at}
+
+ )}
- {/* Message status icons */}
- {message.status &&
+ {/* Message status icons for non-system messages */}
+ {!isSystem &&
+ message.status &&
(message.status === "sent" || message.status === "delivered" || message.status === "failed") && (
)}
- {/* Outbound message metadata */}
- {message.isoutbound && (
+ {/* Outbound message metadata for non-system messages */}
+ {!isSystem && message.isoutbound && (
{i18n.t("messaging.labels.sentby", {
by: message.userid,
diff --git a/client/src/components/chat-send-message/chat-send-message.component.jsx b/client/src/components/chat-send-message/chat-send-message.component.jsx
index c3e91b2b1..5b4d31d14 100644
--- a/client/src/components/chat-send-message/chat-send-message.component.jsx
+++ b/client/src/components/chat-send-message/chat-send-message.component.jsx
@@ -1,5 +1,5 @@
-import { LoadingOutlined, SendOutlined } from "@ant-design/icons";
-import { Alert, Input, Space, Spin } from "antd";
+import { ExclamationCircleOutlined, LoadingOutlined, SendOutlined } from "@ant-design/icons";
+import { Alert, Input, Space, Spin, Tooltip } from "antd";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -69,7 +69,16 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSendi
return (
- {isOptedOut && }
+ {isOptedOut && (
+
+ }
+ message={t("messaging.errors.no_consent")}
+ type="error"
+ />
+
+ )}
{!isOptedOut && (
<>
diff --git a/client/src/components/owner-detail-form/owner-detail-form.component.jsx b/client/src/components/owner-detail-form/owner-detail-form.component.jsx
index a71cbc836..99bacd566 100644
--- a/client/src/components/owner-detail-form/owner-detail-form.component.jsx
+++ b/client/src/components/owner-detail-form/owner-detail-form.component.jsx
@@ -1,14 +1,15 @@
-import { Form, Input } from "antd";
-import React from "react";
+import { Form, Input, Tooltip } from "antd";
+import { CloseCircleFilled } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
-import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
+import { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
-export default function OwnerDetailFormComponent({ form, loading }) {
+export default function OwnerDetailFormComponent({ form, loading, isPhone1OptedOut, isPhone2OptedOut }) {
const { t } = useTranslation();
const { getFieldValue } = form;
+
return (
@@ -62,19 +63,55 @@ export default function OwnerDetailFormComponent({ form, loading }) {
>
-
PhoneItemFormatterValidation(getFieldValue, "ownr_ph1")]}
- >
-
+
+
+
PhoneItemFormatterValidation(getFieldValue, "ownr_ph1")]}
+ >
+
+
+ {isPhone1OptedOut && (
+
+
+
+ )}
+
- PhoneItemFormatterValidation(getFieldValue, "ownr_ph2")]}
- >
-
+
+
+
PhoneItemFormatterValidation(getFieldValue, "ownr_ph2")]}
+ >
+
+
+ {isPhone2OptedOut && (
+
+
+
+ )}
+
diff --git a/client/src/components/owner-detail-form/owner-detail-form.container.jsx b/client/src/components/owner-detail-form/owner-detail-form.container.jsx
index fdf2df339..a92913d30 100644
--- a/client/src/components/owner-detail-form/owner-detail-form.container.jsx
+++ b/client/src/components/owner-detail-form/owner-detail-form.container.jsx
@@ -1,69 +1,113 @@
import { Button, Form, Popconfirm } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
-
-import React, { useState } from "react";
+import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
-import { useMutation } from "@apollo/client";
+import { useApolloClient, useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
+import { connect } from "react-redux";
+import { createStructuredSelector } from "reselect";
import { DELETE_OWNER, UPDATE_OWNER } from "../../graphql/owners.queries";
+import { selectBodyshop } from "../../redux/user/user.selectors"; // Adjust path
+import { phoneNumberOptOutService } from "../../utils/phoneOptOutService.js"; // Adjust path
import OwnerDetailFormComponent from "./owner-detail-form.component";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
+import { phone } from "phone"; // Import phone utility for formatting
-function OwnerDetailFormContainer({ owner, refetch }) {
+// Connect to Redux to access bodyshop
+const mapStateToProps = createStructuredSelector({
+ bodyshop: selectBodyshop
+});
+
+function OwnerDetailFormContainer({ owner, refetch, bodyshop }) {
const { t } = useTranslation();
const [form] = Form.useForm();
- const history = useNavigate();
+ const navigate = useNavigate();
const [loading, setLoading] = useState(false);
+ const [optedOutPhones, setOptedOutPhones] = useState(new Set());
const [updateOwner] = useMutation(UPDATE_OWNER);
const [deleteOwner] = useMutation(DELETE_OWNER);
const notification = useNotification();
+ const apolloClient = useApolloClient();
+
+ // Fetch opt-out status on mount
+ useEffect(() => {
+ const fetchOptOutStatus = async () => {
+ if (bodyshop?.id && (owner?.ownr_ph1 || owner?.ownr_ph2)) {
+ const phoneNumbers = [owner.ownr_ph1, owner.ownr_ph2].filter(Boolean);
+ const optOutSet = await phoneNumberOptOutService(apolloClient, bodyshop.id, phoneNumbers);
+ setOptedOutPhones(optOutSet);
+ }
+ };
+
+ fetchOptOutStatus();
+ }, [apolloClient, bodyshop?.id, owner?.ownr_ph1, owner?.ownr_ph2]);
+
+ // Reset form fields when owner changes
+ useEffect(() => {
+ form.setFieldsValue({
+ ownr_ph1: owner?.ownr_ph1,
+ ownr_ph2: owner?.ownr_ph2,
+ ...owner
+ });
+ }, [owner, form]);
const handleDelete = async () => {
setLoading(true);
- const result = await deleteOwner({
- variables: { id: owner.id }
- });
- console.log(result);
- if (result.errors) {
- notification["error"]({
+ try {
+ const result = await deleteOwner({
+ variables: { id: owner.id }
+ });
+ if (result.errors) {
+ notification.error({
+ message: t("owners.errors.deleting", {
+ error: JSON.stringify(result.errors)
+ })
+ });
+ } else {
+ notification.success({
+ message: t("owners.successes.delete")
+ });
+ navigate(`/manage/owners`);
+ }
+ } catch (error) {
+ notification.error({
message: t("owners.errors.deleting", {
- error: JSON.stringify(result.errors)
+ error: error.message
})
});
+ } finally {
setLoading(false);
- } else {
- notification["success"]({
- message: t("owners.successes.delete")
- });
- setLoading(false);
- history(`/manage/owners`);
}
};
const handleFinish = async (values) => {
setLoading(true);
- const result = await updateOwner({
- variables: { ownerId: owner.id, owner: values }
- });
-
- if (!!result.errors) {
- notification["error"]({
+ try {
+ const result = await updateOwner({
+ variables: { ownerId: owner.id, owner: values }
+ });
+ if (result.errors) {
+ notification.error({
+ message: t("owners.errors.saving", {
+ error: JSON.stringify(result.errors)
+ })
+ });
+ } else {
+ notification.success({
+ message: t("owners.successes.save")
+ });
+ if (refetch) await refetch();
+ form.resetFields();
+ }
+ } catch (error) {
+ notification.error({
message: t("owners.errors.saving", {
- error: JSON.stringify(result.errors)
+ error: error.message
})
});
+ } finally {
setLoading(false);
- return;
}
-
- notification["success"]({
- message: t("owners.successes.save")
- });
-
- if (refetch) await refetch();
- form.resetFields();
- form.resetFields();
- setLoading(false);
};
return (
@@ -72,6 +116,7 @@ function OwnerDetailFormContainer({ owner, refetch }) {
title={t("menus.header.owners")}
extra={[
,
-