react-native-srschat 0.1.68 ā 0.1.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/commonjs/components/email.js +57 -53
- package/lib/commonjs/components/email.js.map +1 -1
- package/lib/commonjs/components/header.js +10 -6
- package/lib/commonjs/components/header.js.map +1 -1
- package/lib/commonjs/components/productCard.js +46 -3
- package/lib/commonjs/components/productCard.js.map +1 -1
- package/lib/commonjs/components/welcomeButton.js +23 -4
- package/lib/commonjs/components/welcomeButton.js.map +1 -1
- package/lib/commonjs/contexts/AppContext.js +37 -11
- package/lib/commonjs/contexts/AppContext.js.map +1 -1
- package/lib/commonjs/layout/disclaimer.js +13 -8
- package/lib/commonjs/layout/disclaimer.js.map +1 -1
- package/lib/commonjs/layout/welcome.js +13 -5
- package/lib/commonjs/layout/welcome.js.map +1 -1
- package/lib/commonjs/layout/window.js +11 -4
- package/lib/commonjs/layout/window.js.map +1 -1
- package/lib/commonjs/utils/audioRecorder.js +11 -5
- package/lib/commonjs/utils/audioRecorder.js.map +1 -1
- package/lib/commonjs/utils/cloudinary.js +12 -4
- package/lib/commonjs/utils/cloudinary.js.map +1 -1
- package/lib/module/components/email.js +57 -53
- package/lib/module/components/email.js.map +1 -1
- package/lib/module/components/header.js +10 -6
- package/lib/module/components/header.js.map +1 -1
- package/lib/module/components/productCard.js +46 -3
- package/lib/module/components/productCard.js.map +1 -1
- package/lib/module/components/welcomeButton.js +23 -4
- package/lib/module/components/welcomeButton.js.map +1 -1
- package/lib/module/contexts/AppContext.js +37 -11
- package/lib/module/contexts/AppContext.js.map +1 -1
- package/lib/module/layout/disclaimer.js +13 -8
- package/lib/module/layout/disclaimer.js.map +1 -1
- package/lib/module/layout/welcome.js +13 -5
- package/lib/module/layout/welcome.js.map +1 -1
- package/lib/module/layout/window.js +11 -4
- package/lib/module/layout/window.js.map +1 -1
- package/lib/module/utils/audioRecorder.js +11 -5
- package/lib/module/utils/audioRecorder.js.map +1 -1
- package/lib/module/utils/cloudinary.js +10 -3
- package/lib/module/utils/cloudinary.js.map +1 -1
- package/lib/typescript/components/email.d.ts.map +1 -1
- package/lib/typescript/components/header.d.ts.map +1 -1
- package/lib/typescript/components/productCard.d.ts +1 -1
- package/lib/typescript/components/productCard.d.ts.map +1 -1
- package/lib/typescript/components/welcomeButton.d.ts.map +1 -1
- package/lib/typescript/contexts/AppContext.d.ts.map +1 -1
- package/lib/typescript/layout/welcome.d.ts.map +1 -1
- package/lib/typescript/layout/window.d.ts.map +1 -1
- package/lib/typescript/utils/audioRecorder.d.ts.map +1 -1
- package/lib/typescript/utils/cloudinary.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/email.js +61 -54
- package/src/components/header.js +6 -9
- package/src/components/productCard.js +45 -3
- package/src/components/welcomeButton.js +14 -4
- package/src/contexts/AppContext.js +42 -13
- package/src/layout/disclaimer.js +6 -8
- package/src/layout/welcome.js +5 -8
- package/src/layout/window.js +8 -5
- package/src/utils/audioRecorder.js +18 -13
- package/src/utils/cloudinary.js +6 -3
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
2
|
import { Cloudinary } from '@cloudinary/url-gen';
|
|
3
3
|
import { Platform, View } from 'react-native';
|
|
4
4
|
import { AdvancedImage } from 'cloudinary-react-native';
|
|
5
5
|
import { scale } from '@cloudinary/url-gen/actions/resize';
|
|
6
|
+
import { AppContext } from '../contexts/AppContext';
|
|
6
7
|
const CloudinaryImage = ({
|
|
7
8
|
cldImg,
|
|
8
9
|
imageStyle,
|
|
9
10
|
accessibilityLabel,
|
|
10
11
|
testID
|
|
11
12
|
}) => {
|
|
13
|
+
const {
|
|
14
|
+
brandCloudName
|
|
15
|
+
} = useContext(AppContext);
|
|
12
16
|
const cld = new Cloudinary({
|
|
13
17
|
cloud: {
|
|
14
|
-
cloudName: 'mktg'
|
|
18
|
+
cloudName: brandCloudName || 'mktg'
|
|
15
19
|
}
|
|
16
20
|
});
|
|
17
21
|
const myImage = Platform.OS === 'ios' ? cld.image(`${cldImg}`) : cld.image(`${cldImg}`).format('png');
|
|
@@ -32,9 +36,12 @@ export const CloudinaryBannerImage = ({
|
|
|
32
36
|
width = 345,
|
|
33
37
|
height = 100
|
|
34
38
|
}) => {
|
|
39
|
+
const {
|
|
40
|
+
brandCloudName
|
|
41
|
+
} = useContext(AppContext);
|
|
35
42
|
const cld = new Cloudinary({
|
|
36
43
|
cloud: {
|
|
37
|
-
cloudName: 'mktg'
|
|
44
|
+
cloudName: brandCloudName || 'mktg'
|
|
38
45
|
}
|
|
39
46
|
});
|
|
40
47
|
const myImage = cld.image(`${cldImg}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","Cloudinary","Platform","View","AdvancedImage","scale","CloudinaryImage","cldImg","imageStyle","accessibilityLabel","testID","cld","cloud","cloudName","myImage","OS","image","format","createElement","style","resizeMode","CloudinaryBannerImage","width","height","resize","parseInt"],"sourceRoot":"../../../src","sources":["utils/cloudinary.js"],"mappings":"AAAA,OAAOA,KAAK,
|
|
1
|
+
{"version":3,"names":["React","useContext","Cloudinary","Platform","View","AdvancedImage","scale","AppContext","CloudinaryImage","cldImg","imageStyle","accessibilityLabel","testID","brandCloudName","cld","cloud","cloudName","myImage","OS","image","format","createElement","style","resizeMode","CloudinaryBannerImage","width","height","resize","parseInt"],"sourceRoot":"../../../src","sources":["utils/cloudinary.js"],"mappings":"AAAA,OAAOA,KAAK,IAAIC,UAAU,QAAQ,OAAO;AACzC,SAASC,UAAU,QAAQ,qBAAqB;AAChD,SAASC,QAAQ,EAAEC,IAAI,QAAQ,cAAc;AAC7C,SAASC,aAAa,QAAQ,yBAAyB;AACvD,SAASC,KAAK,QAAQ,oCAAoC;AAC1D,SAASC,UAAU,QAAQ,wBAAwB;AAEnD,MAAMC,eAAe,GAAGA,CAAC;EAAEC,MAAM;EAAEC,UAAU;EAAEC,kBAAkB;EAAEC;AAAO,CAAC,KAAK;EAC9E,MAAM;IAAEC;EAAe,CAAC,GAAGZ,UAAU,CAACM,UAAU,CAAC;EACjD,MAAMO,GAAG,GAAG,IAAIZ,UAAU,CAAC;IACzBa,KAAK,EAAE;MACLC,SAAS,EAAEH,cAAc,IAAI;IAC/B;EACF,CAAC,CAAC;EAEF,MAAMI,OAAO,GAAGd,QAAQ,CAACe,EAAE,KAAK,KAAK,GAAGJ,GAAG,CAACK,KAAK,CAAC,GAAGV,MAAM,EAAE,CAAC,GAAGK,GAAG,CAACK,KAAK,CAAC,GAAGV,MAAM,EAAE,CAAC,CAACW,MAAM,CAAC,KAAK,CAAC;EACrG,oBACEpB,KAAA,CAAAqB,aAAA,CAACjB,IAAI,qBACHJ,KAAA,CAAAqB,aAAA,CAAChB,aAAa;IACZI,MAAM,EAAEQ,OAAQ;IAChBN,kBAAkB,EAAEA,kBAAmB;IACvCC,MAAM,EAAEA,MAAO;IACfU,KAAK,EAAE,CAAC;MAAEC,UAAU,EAAE;IAAU,CAAC,EAAEb,UAAU;EAAE,CAChD,CACG,CAAC;AAEX,CAAC;AAED,OAAO,MAAMc,qBAAqB,GAAGA,CAAC;EACpCf,MAAM;EACNC,UAAU;EACVC,kBAAkB;EAClBC,MAAM;EACNa,KAAK,GAAG,GAAG;EACXC,MAAM,GAAG;AACX,CAAC,KAAK;EACJ,MAAM;IAAEb;EAAe,CAAC,GAAGZ,UAAU,CAACM,UAAU,CAAC;EACjD,MAAMO,GAAG,GAAG,IAAIZ,UAAU,CAAC;IACzBa,KAAK,EAAE;MACLC,SAAS,EAAEH,cAAc,IAAI;IAC/B;EACF,CAAC,CAAC;EACF,MAAMI,OAAO,GAAGH,GAAG,CAACK,KAAK,CAAC,GAAGV,MAAM,EAAE,CAAC;EACtCQ,OAAO,CAACU,MAAM,CACZrB,KAAK,CAAC,CAAC,CACJmB,KAAK,CAACG,QAAQ,CAACH,KAAK,GAAG,CAAC,CAAC,CAAC,CAC1BC,MAAM,CAACE,QAAQ,CAACF,MAAM,GAAG,CAAC,CAAC,CAChC,CAAC;EACD,oBACE1B,KAAA,CAAAqB,aAAA,CAACjB,IAAI,qBACHJ,KAAA,CAAAqB,aAAA,CAAChB,aAAa;IACZI,MAAM,EAAEQ,OAAQ;IAChBN,kBAAkB,EAAEA,kBAAmB;IACvCC,MAAM,EAAEA,MAAO;IACfU,KAAK,EAAE,CAAC;MAAEC,UAAU,EAAE,SAAS;MAAEE,KAAK,EAAEA,KAAK;MAAEC,MAAM,EAAEA;IAAO,CAAC,EAAEhB,UAAU;EAAE,CAC9E,CACG,CAAC;AAEX,CAAC;AAED,eAAeF,eAAe","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../../src/components/email.js"],"names":[],"mappings":"AAiBO;;
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../../src/components/email.js"],"names":[],"mappings":"AAiBO;;sBAqVN;;kBAtW8D,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../../src/components/header.js"],"names":[],"mappings":"AAOO,
|
|
1
|
+
{"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../../src/components/header.js"],"names":[],"mappings":"AAOO,4CAyDN;kBAhEiC,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"productCard.d.ts","sourceRoot":"","sources":["../../../src/components/productCard.js"],"names":[],"mappings":"AAOO;;;;
|
|
1
|
+
{"version":3,"file":"productCard.d.ts","sourceRoot":"","sources":["../../../src/components/productCard.js"],"names":[],"mappings":"AAOO;;;;6BAwgBN;kBA/gBsD,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"welcomeButton.d.ts","sourceRoot":"","sources":["../../../src/components/welcomeButton.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"welcomeButton.d.ts","sourceRoot":"","sources":["../../../src/components/welcomeButton.js"],"names":[],"mappings":";AAqBA,sDAkBC;kBAtCiC,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/AppContext.js"],"names":[],"mappings":"AAMA,4CAA0C;AAEnC;;;;;;
|
|
1
|
+
{"version":3,"file":"AppContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/AppContext.js"],"names":[],"mappings":"AAMA,4CAA0C;AAEnC;;;;;;sBA2ZN;kBAna6E,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../../src/layout/welcome.js"],"names":[],"mappings":"AASO;;
|
|
1
|
+
{"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../../src/layout/welcome.js"],"names":[],"mappings":"AASO;;sBAgDN;;kBAxDiC,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../../src/layout/window.js"],"names":[],"mappings":"AAeO;;
|
|
1
|
+
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../../src/layout/window.js"],"names":[],"mappings":"AAeO;;sBA0LN;kBAzM8D,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audioRecorder.d.ts","sourceRoot":"","sources":["../../../src/utils/audioRecorder.js"],"names":[],"mappings":"AAqBA,4EAGC;AAGD,
|
|
1
|
+
{"version":3,"file":"audioRecorder.d.ts","sourceRoot":"","sources":["../../../src/utils/audioRecorder.js"],"names":[],"mappings":"AAqBA,4EAGC;AAGD,mFAmHC;AAiFD,mDAwCC;AAED,+CAkDC;AAED,iDASC;AAED,2DA2BC;AAqDD,iDAMC;AAED,gCAgBC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudinary.d.ts","sourceRoot":"","sources":["../../../src/utils/cloudinary.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloudinary.d.ts","sourceRoot":"","sources":["../../../src/utils/cloudinary.js"],"names":[],"mappings":"AA4BO;;;;;;;sBA8BN;;kBA1DiC,OAAO;AAOzC;;;;;sBAmBC"}
|
package/package.json
CHANGED
package/src/components/email.js
CHANGED
|
@@ -16,11 +16,12 @@ import { Header } from "./header";
|
|
|
16
16
|
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
|
17
17
|
|
|
18
18
|
export const EmailForm = ({ panHandlers }) => {
|
|
19
|
-
const { data, BASE_URL, setShowModal, formatChatHistory, conversationStartTime, sessionId } = useContext(AppContext);
|
|
19
|
+
const { data, BASE_URL, setShowModal, formatChatHistory, conversationStartTime, sessionId, theme } = useContext(AppContext);
|
|
20
|
+
const API_PREFIX = "https://";
|
|
20
21
|
const [subject, setSubject] = useState("");
|
|
21
22
|
const [message, setMessage] = useState("");
|
|
22
|
-
const [userEmail, setUserEmail] = useState(data?.user_email || "");
|
|
23
|
-
const [branchEmail, setBranchEmail] = useState((data?.branch_email) || "");
|
|
23
|
+
const [userEmail, setUserEmail] = useState(data?.user_email || ""); // Sender's email
|
|
24
|
+
const [branchEmail, setBranchEmail] = useState((data?.branch_email) || ""); // Recipient's email
|
|
24
25
|
const [isLoading, setIsLoading] = useState(false);
|
|
25
26
|
const [error, setError] = useState("");
|
|
26
27
|
const [success, setSuccess] = useState(false);
|
|
@@ -152,81 +153,89 @@ export const EmailForm = ({ panHandlers }) => {
|
|
|
152
153
|
const handleComposeEmail = async () => {
|
|
153
154
|
setIsComposing(true);
|
|
154
155
|
setError("");
|
|
155
|
-
setSuccess(false);
|
|
156
156
|
|
|
157
157
|
try {
|
|
158
|
-
const chatHistory = formatChatHistory();
|
|
159
|
-
|
|
158
|
+
const chatHistory = formatChatHistory().map(({ variant_type, ...rest }) => rest);
|
|
159
|
+
|
|
160
|
+
const payload = {
|
|
161
|
+
chat_history: chatHistory,
|
|
162
|
+
conversation_start_time: conversationStartTime || null,
|
|
163
|
+
customer_name: data?.customer_name || null,
|
|
164
|
+
user_id: data?.user_id || null,
|
|
165
|
+
session_id: data?.session_id || data?.session || sessionId || null,
|
|
166
|
+
user_email: data?.user_email || "",
|
|
167
|
+
branch_email: data?.branch_email || ""
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
console.log("Email composition request payload:", payload);
|
|
171
|
+
|
|
172
|
+
const response = await axios.post(`${API_PREFIX}${BASE_URL}/compose-email`, payload);
|
|
173
|
+
|
|
174
|
+
const textToType = response.data.message;
|
|
175
|
+
let currentText = "";
|
|
176
|
+
const typingSpeed = 10;
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < textToType.length; i++) {
|
|
179
|
+
await new Promise(resolve => setTimeout(resolve, typingSpeed));
|
|
180
|
+
currentText += textToType[i];
|
|
181
|
+
setMessage(currentText);
|
|
182
|
+
}
|
|
160
183
|
|
|
161
|
-
const response = await axios.post(`https://${BASE_URL}/compose-email`, payload);
|
|
162
|
-
// Use customer name from data if available, otherwise leave blank
|
|
163
|
-
|
|
164
|
-
setMessage(response.data.message);
|
|
165
184
|
setSubject(response.data.subject || "");
|
|
185
|
+
setUserEmail(response.data.user_email || userEmail || "");
|
|
186
|
+
// Don't overwrite branch email if response doesn't have it
|
|
187
|
+
if (response.data.branch_email) {
|
|
188
|
+
setBranchEmail(response.data.branch_email);
|
|
189
|
+
}
|
|
190
|
+
|
|
166
191
|
} catch (error) {
|
|
167
|
-
setError(
|
|
168
|
-
|
|
192
|
+
setError(
|
|
193
|
+
error.response?.data?.message ||
|
|
194
|
+
error.message ||
|
|
195
|
+
"Failed to compose email"
|
|
196
|
+
);
|
|
169
197
|
} finally {
|
|
170
198
|
setIsComposing(false);
|
|
171
199
|
}
|
|
172
200
|
};
|
|
173
201
|
|
|
174
202
|
const handleSubmit = async () => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
setError(emailError);
|
|
178
|
-
setSuccess(false);
|
|
203
|
+
if (!userEmail || !subject || !message) {
|
|
204
|
+
setError("Please fill in all fields");
|
|
179
205
|
return;
|
|
180
206
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
207
|
+
|
|
208
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(userEmail)) {
|
|
209
|
+
setError("Please enter a valid email address");
|
|
184
210
|
return;
|
|
185
211
|
}
|
|
186
212
|
|
|
187
213
|
setIsLoading(true);
|
|
188
|
-
setError("");
|
|
189
|
-
setSuccess(false);
|
|
190
214
|
|
|
191
215
|
try {
|
|
192
|
-
const chatHistory = formatChatHistory();
|
|
193
|
-
|
|
194
|
-
|
|
216
|
+
const chatHistory = formatChatHistory().map(({ variant_type, ...rest }) => rest);
|
|
217
|
+
console.log("Sending email with chat history:", chatHistory);
|
|
218
|
+
|
|
219
|
+
await axios.post(`https://${BASE_URL}/send-email`, {
|
|
195
220
|
user_email: userEmail,
|
|
196
221
|
subject,
|
|
197
222
|
message,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
await axios.post(`https://${BASE_URL}/send-email`, payload);
|
|
204
|
-
|
|
223
|
+
chat_history: chatHistory,
|
|
224
|
+
conversation_start_time: conversationStartTime,
|
|
225
|
+
customer_name: data?.customer_name,
|
|
226
|
+
branch_email: branchEmail || data?.branch_email || ""
|
|
227
|
+
});
|
|
205
228
|
setSuccess(true);
|
|
206
|
-
setError("");
|
|
207
229
|
setTimeout(() => {
|
|
208
230
|
setSuccess(false);
|
|
209
231
|
setShowModal("ChatWindow");
|
|
210
232
|
}, 2000);
|
|
211
233
|
} catch (error) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
requestPayload: {
|
|
218
|
-
user_email: userEmail,
|
|
219
|
-
subject,
|
|
220
|
-
message_length: message?.length,
|
|
221
|
-
branch_email: branchEmail,
|
|
222
|
-
customer_name: data?.customer_name,
|
|
223
|
-
conversation_start_time: conversationStartTime,
|
|
224
|
-
session_id: data?.session_id || data?.session || sessionId,
|
|
225
|
-
chat_history_length: chatHistory?.length
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
setError(error.response?.data?.message || "Failed to send email");
|
|
229
|
-
setSuccess(false);
|
|
234
|
+
setError(
|
|
235
|
+
error.response?.data?.message ||
|
|
236
|
+
error.message ||
|
|
237
|
+
"Failed to send email",
|
|
238
|
+
);
|
|
230
239
|
} finally {
|
|
231
240
|
setIsLoading(false);
|
|
232
241
|
}
|
|
@@ -257,7 +266,7 @@ export const EmailForm = ({ panHandlers }) => {
|
|
|
257
266
|
Press "Draft My Email" to generate an email based on your chat conversation. You can review and modify before sending.
|
|
258
267
|
</Text>
|
|
259
268
|
|
|
260
|
-
<TouchableOpacity style={styles.composeButton} onPress={handleComposeEmail} disabled={isComposing}>
|
|
269
|
+
<TouchableOpacity style={[styles.composeButton, { backgroundColor: theme.primaryColor }]} onPress={handleComposeEmail} disabled={isComposing}>
|
|
261
270
|
{isComposing ? (
|
|
262
271
|
<ActivityIndicator size="small" color="#FFFFFF" />
|
|
263
272
|
) : (
|
|
@@ -341,7 +350,7 @@ export const EmailForm = ({ panHandlers }) => {
|
|
|
341
350
|
{error ? <Text style={styles.errorText}>{error}</Text> : null}
|
|
342
351
|
{success ? <Text style={styles.successText}>Email sent successfully!</Text> : null}
|
|
343
352
|
|
|
344
|
-
<TouchableOpacity style={styles.sendButton} onPress={handleSubmit} disabled={isLoading}>
|
|
353
|
+
<TouchableOpacity style={[styles.sendButton, { backgroundColor: theme.primaryColor }]} onPress={handleSubmit} disabled={isLoading}>
|
|
345
354
|
{isLoading ? <ActivityIndicator size="small" color="#FFFFFF" /> : <Text style={styles.buttonText}>Send Email</Text>}
|
|
346
355
|
</TouchableOpacity>
|
|
347
356
|
</KeyboardAwareScrollView>
|
|
@@ -405,14 +414,12 @@ const styles = StyleSheet.create({
|
|
|
405
414
|
color: "#000",
|
|
406
415
|
},
|
|
407
416
|
composeButton: {
|
|
408
|
-
backgroundColor: "#367CB6",
|
|
409
417
|
borderRadius: 5,
|
|
410
418
|
padding: 12,
|
|
411
419
|
alignItems: "center",
|
|
412
420
|
marginBottom: 20,
|
|
413
421
|
},
|
|
414
422
|
sendButton: {
|
|
415
|
-
backgroundColor: "#367CB6",
|
|
416
423
|
borderRadius: 5,
|
|
417
424
|
padding: 12,
|
|
418
425
|
alignItems: "center",
|
package/src/components/header.js
CHANGED
|
@@ -6,7 +6,7 @@ import CloudinaryImage from '../utils/cloudinary';
|
|
|
6
6
|
// import { PoseidonLogo } from './PoseidonLogo';
|
|
7
7
|
|
|
8
8
|
export const Header = () => {
|
|
9
|
-
const { showModal, setShowModal, setMessages, defaultMessage, handleClearState, uiConfig } = useContext(AppContext);
|
|
9
|
+
const { showModal, setShowModal, setMessages, defaultMessage, handleClearState, uiConfig, theme, brandLogo } = useContext(AppContext);
|
|
10
10
|
|
|
11
11
|
const handleClick = () => {
|
|
12
12
|
if ((uiConfig.showIcon ?? true) !== true) {
|
|
@@ -21,7 +21,7 @@ export const Header = () => {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
|
-
<View style={styles.header}>
|
|
24
|
+
<View style={[styles.header, { backgroundColor: theme.primaryColor }]}>
|
|
25
25
|
{/* Logo on the left */}
|
|
26
26
|
<View style={styles.section}>
|
|
27
27
|
{/* <PoseidonLogo width={150} height={35} color="white" /> */}
|
|
@@ -31,10 +31,7 @@ export const Header = () => {
|
|
|
31
31
|
width={150}
|
|
32
32
|
height={35}
|
|
33
33
|
/> */}
|
|
34
|
-
<CloudinaryImage
|
|
35
|
-
cldImg="logos/HLSG_Logo_Lockup_Color"
|
|
36
|
-
imageStyle={{ width: 150, height: 35 }}
|
|
37
|
-
/>
|
|
34
|
+
<CloudinaryImage cldImg={brandLogo} imageStyle={{ width: 150, height: 35 }} />
|
|
38
35
|
</View>
|
|
39
36
|
|
|
40
37
|
{/* Title in the center */}
|
|
@@ -52,15 +49,15 @@ export const Header = () => {
|
|
|
52
49
|
{showModal !== "Form" && showModal !== "Email" &&
|
|
53
50
|
<>
|
|
54
51
|
<TouchableOpacity onPress={() => setShowModal("Email")}>
|
|
55
|
-
<Ionicons name="mail" size={24} color=
|
|
52
|
+
<Ionicons name="mail" size={24} color={theme.textColorSecondary} />
|
|
56
53
|
</TouchableOpacity>
|
|
57
54
|
<TouchableOpacity onPress={() => handleClearState()}>
|
|
58
|
-
<Ionicons name="trash" size={22} color=
|
|
55
|
+
<Ionicons name="trash" size={22} color={theme.textColorSecondary} />
|
|
59
56
|
</TouchableOpacity>
|
|
60
57
|
</>
|
|
61
58
|
}
|
|
62
59
|
<TouchableOpacity onPress={() => handleClick()}>
|
|
63
|
-
<Ionicons name="close" size={26} color=
|
|
60
|
+
<Ionicons name="close" size={26} color={theme.textColorSecondary} />
|
|
64
61
|
</TouchableOpacity>
|
|
65
62
|
</View>
|
|
66
63
|
</View>
|
|
@@ -9,6 +9,48 @@ export const ProductCard = ({ prod, onFocusQuantityInput, messageId }) => {
|
|
|
9
9
|
const { onProductCardClick, onAddToCartClick, sessionId, data, ADD_TO_CART_URL } = useContext(AppContext);
|
|
10
10
|
const [keyboardVisible, setKeyboardVisible] = useState(false);
|
|
11
11
|
|
|
12
|
+
// Validation: Check for incomplete product information
|
|
13
|
+
const hasValidProductDetails = () => {
|
|
14
|
+
if (!prod || !prod.product_details) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const details = prod.product_details;
|
|
19
|
+
if (!details.product_name || !details.part_number || !details.image_url) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const hasValidInventoryInfo = () => {
|
|
27
|
+
if (!prod.inventory_info || !prod.inventory_info.info_by_uom) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const uomData = prod.inventory_info.info_by_uom;
|
|
32
|
+
const uomKeys = Object.keys(uomData);
|
|
33
|
+
|
|
34
|
+
if (uomKeys.length === 0) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check if at least one UOM has valid pricing
|
|
39
|
+
const hasValidPricing = uomKeys.some(uom => {
|
|
40
|
+
const uomInfo = uomData[uom];
|
|
41
|
+
return uomInfo &&
|
|
42
|
+
typeof uomInfo.gross_price === 'number' &&
|
|
43
|
+
uomInfo.gross_price > 0;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return hasValidPricing;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Return null if product lacks essential information
|
|
50
|
+
if (!hasValidProductDetails() || !hasValidInventoryInfo()) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
12
54
|
const [selectedUom, setSelectedUom] = useState(() => {
|
|
13
55
|
if (prod.inventory_info?.info_by_uom) {
|
|
14
56
|
const availableUoms = Object.keys(prod.inventory_info.info_by_uom);
|
|
@@ -360,13 +402,13 @@ export const ProductCard = ({ prod, onFocusQuantityInput, messageId }) => {
|
|
|
360
402
|
<View style={styles.priceContainer}>
|
|
361
403
|
{isOnSale ? (
|
|
362
404
|
<>
|
|
363
|
-
<Text style={styles.originalPrice} allowFontScaling={false}>${Number(grossPrice).
|
|
364
|
-
<Text style={styles.salePrice} allowFontScaling={false}>${Number(netPrice).
|
|
405
|
+
<Text style={styles.originalPrice} allowFontScaling={false}>${Number(grossPrice).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</Text>
|
|
406
|
+
<Text style={styles.salePrice} allowFontScaling={false}>${Number(netPrice).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</Text>
|
|
365
407
|
<Text style={styles.perUnit}>/ {selectedUom}</Text>
|
|
366
408
|
</>
|
|
367
409
|
) : (
|
|
368
410
|
<>
|
|
369
|
-
<Text style={styles.price} allowFontScaling={false}>${Number(grossPrice).
|
|
411
|
+
<Text style={styles.price} allowFontScaling={false}>${Number(grossPrice).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</Text>
|
|
370
412
|
<Text style={styles.perUnit}>/ {selectedUom}</Text>
|
|
371
413
|
</>
|
|
372
414
|
)}
|
|
@@ -3,7 +3,7 @@ import React, { useContext } from 'react';
|
|
|
3
3
|
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
|
|
4
4
|
import { AppContext } from '../contexts/AppContext';
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const landscapeQuestions = [
|
|
7
7
|
{ text: 'What are the hours of my current branch?' },
|
|
8
8
|
{ text: 'Do you have part # RBLESPLXME2 in stock?' },
|
|
9
9
|
{ text: 'How do I install the seal on B82456?' },
|
|
@@ -11,18 +11,28 @@ const suggestedQuestions = [
|
|
|
11
11
|
{ text: '¿Puedes ayudarme en español?' }
|
|
12
12
|
];
|
|
13
13
|
|
|
14
|
+
const poolQuestions = [
|
|
15
|
+
{ text: 'What are the hours of my current branch?' },
|
|
16
|
+
{ text: 'Do you have part # JNDDEV48 in stock?' },
|
|
17
|
+
{ text: 'Do you carry Hayward super pumps?' },
|
|
18
|
+
{ text: 'Which grid assembly goes with the Hayward DE4820 filter?' },
|
|
19
|
+
{ text: '¿Puedes ayudarme en español?' }
|
|
20
|
+
];
|
|
21
|
+
|
|
14
22
|
const ButtonComponent = () => {
|
|
15
|
-
const { handleButtonClick } = useContext(AppContext);
|
|
23
|
+
const { handleButtonClick, theme, data } = useContext(AppContext);
|
|
24
|
+
const isPool = data?.brand_version === 'pool';
|
|
25
|
+
const suggestedQuestions = isPool ? poolQuestions : landscapeQuestions;
|
|
16
26
|
|
|
17
27
|
return (
|
|
18
28
|
<View style={styles.buttonContainer}>
|
|
19
29
|
{suggestedQuestions.map((item, index) => (
|
|
20
30
|
<TouchableOpacity
|
|
21
31
|
key={index}
|
|
22
|
-
style={styles.button}
|
|
32
|
+
style={[styles.button, { borderColor: theme.primaryColor }]}
|
|
23
33
|
onPress={() => handleButtonClick(item.text)}
|
|
24
34
|
>
|
|
25
|
-
<Text style={styles.buttonText}>{item.text}</Text>
|
|
35
|
+
<Text style={[styles.buttonText, { color: theme.primaryColor }]}>{item.text}</Text>
|
|
26
36
|
</TouchableOpacity>
|
|
27
37
|
))}
|
|
28
38
|
</View>
|
|
@@ -7,25 +7,54 @@ import { loadChat, updateChat, defaultState } from '../utils/storage';
|
|
|
7
7
|
export const AppContext = createContext();
|
|
8
8
|
|
|
9
9
|
export const AppProvider = ({ data, onProductCardClick, onAddToCartClick, uiConfig = {}, children }) => {
|
|
10
|
+
// Brand-driven configuration
|
|
11
|
+
const version = (data?.brand_version === 'pool' || data?.brand_version === 'landscape') ? data.brand_version : 'landscape';
|
|
12
|
+
const API_PREFIX = "https://";
|
|
13
|
+
|
|
14
|
+
const BRAND_CONFIG = (() => {
|
|
15
|
+
const isStage = data?.env === 'stage';
|
|
16
|
+
return {
|
|
17
|
+
pool: {
|
|
18
|
+
primaryColor: '#004687',
|
|
19
|
+
userMessageColor: '#003764',
|
|
20
|
+
cloudName: 'heritageplus',
|
|
21
|
+
// Pool assets live under mobileapp/ in heritageplus cloud
|
|
22
|
+
logoId: 'mobileapp/logos/HPSG_HPlus_Logo_White',
|
|
23
|
+
baseHost: isStage
|
|
24
|
+
? 'pool-stage-external-agent-adk-586731320826.us-east1.run.app'
|
|
25
|
+
: 'pool-prod-external-agent-adk-586731320826.us-east1.run.app',
|
|
26
|
+
loggingHost: 'srs-external-agent-logging-586731320826.us-central1.run.app',
|
|
27
|
+
},
|
|
28
|
+
landscape: {
|
|
29
|
+
primaryColor: '#437D3D',
|
|
30
|
+
userMessageColor: '#437D3D',
|
|
31
|
+
cloudName: 'mktg',
|
|
32
|
+
logoId: 'logos/HLSG_Logo_Lockup_Color',
|
|
33
|
+
baseHost: isStage
|
|
34
|
+
? 'landscape-stage-external-agent-adk-586731320826.us-east1.run.app'
|
|
35
|
+
: 'landscape-prod-external-agent-adk-586731320826.us-east1.run.app',
|
|
36
|
+
loggingHost: 'srs-external-agent-landscape-logging-586731320826.us-central1.run.app',
|
|
37
|
+
},
|
|
38
|
+
}[version];
|
|
39
|
+
})();
|
|
40
|
+
|
|
41
|
+
const BASE_URL = BRAND_CONFIG.baseHost;
|
|
42
|
+
const LOGGING_URL = BRAND_CONFIG.loggingHost;
|
|
43
|
+
const TRACK_CLICK_URL = `${API_PREFIX}${LOGGING_URL}/track-click`;
|
|
44
|
+
const ADD_TO_CART_URL = `${API_PREFIX}${LOGGING_URL}/add-to-cart`;
|
|
45
|
+
|
|
10
46
|
const theme = {
|
|
11
|
-
userMessage:
|
|
12
|
-
botMessage:
|
|
47
|
+
userMessage: BRAND_CONFIG.userMessageColor,
|
|
48
|
+
botMessage: BRAND_CONFIG.userMessageColor,
|
|
13
49
|
backgroundColor: '#f6f6f6',
|
|
14
50
|
textColor: '#161616',
|
|
15
51
|
textColorSecondary: '#FFFFFF',
|
|
16
52
|
inlineButtonColor: '#dbd4c8',
|
|
17
|
-
primaryColor:
|
|
53
|
+
primaryColor: BRAND_CONFIG.primaryColor,
|
|
18
54
|
};
|
|
19
55
|
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
// Backend URLs
|
|
24
|
-
const BASE_URL = data.env === "stage"
|
|
25
|
-
? "landscape-stage-external-agent-adk-586731320826.us-east1.run.app"
|
|
26
|
-
: "landscape-prod-external-agent-adk-586731320826.us-east1.run.app";
|
|
27
|
-
const LOGGING_URL = "srs-external-agent-landscape-logging-586731320826.us-central1.run.app";
|
|
28
|
-
const API_PREFIX = "https://";
|
|
56
|
+
const brandLogo = BRAND_CONFIG.logoId;
|
|
57
|
+
const brandCloudName = BRAND_CONFIG.cloudName;
|
|
29
58
|
|
|
30
59
|
// Default Messages
|
|
31
60
|
const defaultMessage = [
|
|
@@ -382,7 +411,7 @@ export const AppProvider = ({ data, onProductCardClick, onAddToCartClick, uiConf
|
|
|
382
411
|
startStreaming, setStartStreaming, maintenance, setMaintenance, feedback, setFeedback, handleFeedback, feedbackOpen, setFeedbackOpen,
|
|
383
412
|
writeFeedback, setWriteFeedback, writeAnswer, setWriteAnswer, BASE_URL, lastMessageId, setLastMessageId,
|
|
384
413
|
onProductCardClick, onAddToCartClick: handleAddToCartWithMessage, data, sessionId, setSessionId, handleWrittenFeedback, switchFeedbackOpen, confirmDisclaimer,
|
|
385
|
-
formatChatHistory, uiConfig, handleVoiceSend, TRACK_CLICK_URL, ADD_TO_CART_URL, isListening, setIsListening
|
|
414
|
+
formatChatHistory, uiConfig, handleVoiceSend, TRACK_CLICK_URL, ADD_TO_CART_URL, isListening, setIsListening, brandLogo, brandCloudName
|
|
386
415
|
}}
|
|
387
416
|
>
|
|
388
417
|
{children}
|
package/src/layout/disclaimer.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Header } from '../components/header';
|
|
|
6
6
|
import { Testing } from "../components/testing";
|
|
7
7
|
|
|
8
8
|
export const Disclaimer = ({ panHandlers }) => {
|
|
9
|
-
const { setShowModal, confirmDisclaimer, uiConfig, onAddToCartClick, onProductCardClick } = useContext(AppContext);
|
|
9
|
+
const { setShowModal, confirmDisclaimer, uiConfig, onAddToCartClick, onProductCardClick, theme } = useContext(AppContext);
|
|
10
10
|
|
|
11
11
|
const sections = [
|
|
12
12
|
{
|
|
@@ -47,7 +47,7 @@ export const Disclaimer = ({ panHandlers }) => {
|
|
|
47
47
|
</Text>
|
|
48
48
|
|
|
49
49
|
{sections.map((section, index) => (
|
|
50
|
-
<Paragraph key={index} title={section.title} details={section.details} icon={section.icon} />
|
|
50
|
+
<Paragraph key={index} title={section.title} details={section.details} icon={section.icon} theme={theme} />
|
|
51
51
|
))}
|
|
52
52
|
|
|
53
53
|
<Text style={styles.disclaimerText}>
|
|
@@ -61,7 +61,7 @@ export const Disclaimer = ({ panHandlers }) => {
|
|
|
61
61
|
<Testing onProductCardClick={onProductCardClick} onAddToCartClick={onAddToCartClick} />
|
|
62
62
|
}
|
|
63
63
|
<View style={styles.confirmArea}>
|
|
64
|
-
<TouchableOpacity style={styles.button} onPress={confirmDisclaimer}>
|
|
64
|
+
<TouchableOpacity style={[styles.button, { backgroundColor: theme.primaryColor }]} onPress={confirmDisclaimer}>
|
|
65
65
|
<Text style={styles.buttonText}>Confirm</Text>
|
|
66
66
|
</TouchableOpacity>
|
|
67
67
|
<Text style={styles.confirmText}>
|
|
@@ -72,11 +72,11 @@ export const Disclaimer = ({ panHandlers }) => {
|
|
|
72
72
|
);
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
-
const Paragraph = ({ title, details, icon }) => {
|
|
75
|
+
const Paragraph = ({ title, details, icon, theme }) => {
|
|
76
76
|
return (
|
|
77
77
|
<View style={styles.sectionContainer}>
|
|
78
|
-
<View style={styles.iconContainer}>
|
|
79
|
-
<Feather name={icon} size={18} color=
|
|
78
|
+
<View style={[styles.iconContainer, { backgroundColor: `${theme.primaryColor}20` }]}>
|
|
79
|
+
<Feather name={icon} size={18} color={theme?.primaryColor || '#1976d2'} />
|
|
80
80
|
</View>
|
|
81
81
|
<View style={styles.textContainer}>
|
|
82
82
|
<Text style={styles.sectionTitle}>{title}</Text>
|
|
@@ -135,7 +135,6 @@ const styles = StyleSheet.create({
|
|
|
135
135
|
elevation: 1,
|
|
136
136
|
},
|
|
137
137
|
iconContainer: {
|
|
138
|
-
backgroundColor: "#bbdefb",
|
|
139
138
|
borderRadius: 10,
|
|
140
139
|
padding: 10,
|
|
141
140
|
alignItems: "center",
|
|
@@ -165,7 +164,6 @@ const styles = StyleSheet.create({
|
|
|
165
164
|
paddingBottom: 25,
|
|
166
165
|
},
|
|
167
166
|
button: {
|
|
168
|
-
backgroundColor: "#367CB6",
|
|
169
167
|
width: "100%",
|
|
170
168
|
paddingVertical: 12,
|
|
171
169
|
borderRadius: 5,
|
package/src/layout/welcome.js
CHANGED
|
@@ -8,7 +8,7 @@ import ButtonComponent from '../components/welcomeButton';
|
|
|
8
8
|
import CloudinaryImage from '../utils/cloudinary';
|
|
9
9
|
|
|
10
10
|
export const Welcome = ({ panHandlers }) => {
|
|
11
|
-
const { setShowModal, uiConfig, onProductCardClick, onAddToCartClick, data } = useContext(AppContext);
|
|
11
|
+
const { setShowModal, uiConfig, onProductCardClick, onAddToCartClick, data, theme, brandLogo } = useContext(AppContext);
|
|
12
12
|
|
|
13
13
|
const handleClick = () => {
|
|
14
14
|
if ((uiConfig.showIcon ?? true) !== true) {
|
|
@@ -24,23 +24,20 @@ export const Welcome = ({ panHandlers }) => {
|
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<View style={styles.container}>
|
|
27
|
-
<View style={styles.parentContainer}>
|
|
27
|
+
<View style={[styles.parentContainer, { backgroundColor: theme.primaryColor }]}>
|
|
28
28
|
{/* Top section */}
|
|
29
|
-
<View style={styles.topContainer}>
|
|
29
|
+
<View style={[styles.topContainer, { backgroundColor: theme.primaryColor }]}>
|
|
30
30
|
<View style={styles.topHeader}>
|
|
31
31
|
{/* Logo container with absolute positioning */}
|
|
32
32
|
<View style={styles.logoContainer}>
|
|
33
|
-
<CloudinaryImage
|
|
34
|
-
cldImg="logos/HLSG_Logo_Lockup_Color"
|
|
35
|
-
imageStyle={{ width: 150, height: 35 }}
|
|
36
|
-
/>
|
|
33
|
+
<CloudinaryImage cldImg={brandLogo} imageStyle={{ width: 150, height: 35 }} />
|
|
37
34
|
</View>
|
|
38
35
|
<TouchableOpacity onPress={handleClick} style={styles.collapseButton}>
|
|
39
36
|
<Ionicons name="close" size={26} color="white" />
|
|
40
37
|
</TouchableOpacity>
|
|
41
38
|
</View>
|
|
42
39
|
|
|
43
|
-
<View style={styles.blueContainer}>
|
|
40
|
+
<View style={[styles.blueContainer, { backgroundColor: theme.primaryColor }]}>
|
|
44
41
|
<Text style={styles.welcomeHeader}>Hi {data?.customer_name || ""} š</Text>
|
|
45
42
|
<Text style={styles.welcomeBody2}>
|
|
46
43
|
Iām your Heritage+ AI Agent. I can help you during your online visit with Product and Account information.
|