@diffsome/react 1.3.3 → 1.3.4

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/dist/chat.css CHANGED
@@ -500,6 +500,98 @@
500
500
  }
501
501
  }
502
502
 
503
+ /* Offline Form */
504
+ .diffsome-chat-offline-body {
505
+ flex: 1;
506
+ padding: 24px 20px;
507
+ display: flex;
508
+ flex-direction: column;
509
+ overflow-y: auto;
510
+ }
511
+
512
+ .diffsome-chat-offline-form {
513
+ display: flex;
514
+ flex-direction: column;
515
+ gap: 12px;
516
+ }
517
+
518
+ .diffsome-chat-offline-msg {
519
+ color: var(--diffsome-chat-text-muted);
520
+ font-size: 14px;
521
+ line-height: 1.5;
522
+ margin-bottom: 4px;
523
+ }
524
+
525
+ .diffsome-chat-offline-input {
526
+ width: 100%;
527
+ padding: 10px 14px;
528
+ border: 1px solid var(--diffsome-chat-border);
529
+ border-radius: 8px;
530
+ font-size: 14px;
531
+ outline: none;
532
+ transition: border-color 0.2s;
533
+ background: var(--diffsome-chat-bg);
534
+ color: var(--diffsome-chat-text);
535
+ font-family: inherit;
536
+ box-sizing: border-box;
537
+ }
538
+
539
+ .diffsome-chat-offline-input:focus {
540
+ border-color: var(--diffsome-chat-primary);
541
+ }
542
+
543
+ .diffsome-chat-offline-textarea {
544
+ width: 100%;
545
+ padding: 10px 14px;
546
+ border: 1px solid var(--diffsome-chat-border);
547
+ border-radius: 8px;
548
+ font-size: 14px;
549
+ outline: none;
550
+ resize: vertical;
551
+ transition: border-color 0.2s;
552
+ background: var(--diffsome-chat-bg);
553
+ color: var(--diffsome-chat-text);
554
+ font-family: inherit;
555
+ box-sizing: border-box;
556
+ }
557
+
558
+ .diffsome-chat-offline-textarea:focus {
559
+ border-color: var(--diffsome-chat-primary);
560
+ }
561
+
562
+ .diffsome-chat-offline-btn {
563
+ width: 100%;
564
+ padding: 12px;
565
+ background: var(--diffsome-chat-primary);
566
+ color: white;
567
+ border: none;
568
+ border-radius: 8px;
569
+ font-size: 14px;
570
+ font-weight: 600;
571
+ cursor: pointer;
572
+ transition: opacity 0.2s;
573
+ font-family: inherit;
574
+ }
575
+
576
+ .diffsome-chat-offline-btn:hover:not(:disabled) {
577
+ opacity: 0.9;
578
+ }
579
+
580
+ .diffsome-chat-offline-btn:disabled {
581
+ opacity: 0.5;
582
+ cursor: not-allowed;
583
+ }
584
+
585
+ .diffsome-chat-offline-success {
586
+ display: flex;
587
+ flex-direction: column;
588
+ align-items: center;
589
+ justify-content: center;
590
+ gap: 12px;
591
+ height: 100%;
592
+ text-align: center;
593
+ }
594
+
503
595
  /* Dark mode support */
504
596
  @media (prefers-color-scheme: dark) {
505
597
  .diffsome-chat-bubble-container {
package/dist/index.js CHANGED
@@ -2901,8 +2901,22 @@ var ChatBubble = ({
2901
2901
  const [unreadCount, setUnreadCount] = (0, import_react24.useState)(0);
2902
2902
  const [initialized, setInitialized] = (0, import_react24.useState)(false);
2903
2903
  const [ended, setEnded] = (0, import_react24.useState)(false);
2904
+ const [offline, setOffline] = (0, import_react24.useState)(false);
2905
+ const [offlineMessage, setOfflineMessage] = (0, import_react24.useState)("");
2906
+ const [offlineSent, setOfflineSent] = (0, import_react24.useState)(false);
2907
+ const [offlineLoading, setOfflineLoading] = (0, import_react24.useState)(false);
2904
2908
  const seenMessageIds = (0, import_react24.useRef)(/* @__PURE__ */ new Set());
2905
2909
  const connectionRef = (0, import_react24.useRef)(null);
2910
+ (0, import_react24.useEffect)(() => {
2911
+ client.chat.checkAvailability().then((result) => {
2912
+ const data = result?.data ?? result;
2913
+ if (data?.within_operating_hours === false) {
2914
+ setOffline(true);
2915
+ setOfflineMessage(data.offline_message || "We are currently offline. Please leave a message.");
2916
+ }
2917
+ }).catch(() => {
2918
+ });
2919
+ }, [client.chat]);
2906
2920
  const setupListeners = (0, import_react24.useCallback)((conn) => {
2907
2921
  conn.onMessage((msg) => {
2908
2922
  if (seenMessageIds.current.has(msg.id)) {
@@ -3045,6 +3059,23 @@ var ChatBubble = ({
3045
3059
  seenMessageIds.current.clear();
3046
3060
  initChat();
3047
3061
  };
3062
+ const handleOfflineSubmit = async (name, email, message) => {
3063
+ setOfflineLoading(true);
3064
+ try {
3065
+ await client.chat.start({
3066
+ visitor_name: name || "Visitor",
3067
+ visitor_email: email,
3068
+ initial_message: message,
3069
+ channel: "web",
3070
+ metadata: { offline: true }
3071
+ });
3072
+ setOfflineSent(true);
3073
+ } catch (error) {
3074
+ console.error("Failed to send offline message:", error);
3075
+ } finally {
3076
+ setOfflineLoading(false);
3077
+ }
3078
+ };
3048
3079
  (0, import_react24.useEffect)(() => {
3049
3080
  return () => {
3050
3081
  connectionRef.current?.disconnect();
@@ -3057,7 +3088,17 @@ var ChatBubble = ({
3057
3088
  className: `diffsome-chat-bubble-container ${positionClass} ${className}`,
3058
3089
  style: { "--diffsome-chat-primary": primaryColor, zIndex },
3059
3090
  children: [
3060
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3091
+ isOpen && (offline ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3092
+ OfflineForm,
3093
+ {
3094
+ title,
3095
+ offlineMessage,
3096
+ onClose: handleClose,
3097
+ onSubmit: handleOfflineSubmit,
3098
+ submitted: offlineSent,
3099
+ loading: offlineLoading
3100
+ }
3101
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3061
3102
  ChatWidget,
3062
3103
  {
3063
3104
  messages,
@@ -3077,7 +3118,7 @@ var ChatBubble = ({
3077
3118
  loginUrl,
3078
3119
  loginPromptMessage
3079
3120
  }
3080
- ),
3121
+ )),
3081
3122
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
3082
3123
  "button",
3083
3124
  {
@@ -3114,6 +3155,72 @@ var ChatBubble = ({
3114
3155
  }
3115
3156
  );
3116
3157
  };
3158
+ var OfflineForm = ({
3159
+ title,
3160
+ offlineMessage,
3161
+ onClose,
3162
+ onSubmit,
3163
+ submitted,
3164
+ loading
3165
+ }) => {
3166
+ const [name, setName] = (0, import_react24.useState)("");
3167
+ const [email, setEmail] = (0, import_react24.useState)("");
3168
+ const [message, setMessage] = (0, import_react24.useState)("");
3169
+ const handleSubmit = (e) => {
3170
+ e.preventDefault();
3171
+ if (!email || !message) return;
3172
+ onSubmit(name, email, message);
3173
+ };
3174
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "diffsome-chat-widget", children: [
3175
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "diffsome-chat-header", children: [
3176
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "diffsome-chat-header-info", children: [
3177
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "diffsome-chat-header-title", children: title }),
3178
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "diffsome-chat-header-subtitle", children: "Offline" })
3179
+ ] }),
3180
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "diffsome-chat-header-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", width: "24", height: "24", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) })
3181
+ ] }),
3182
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "diffsome-chat-offline-body", children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "diffsome-chat-offline-success", children: [
3183
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "#10b981", width: "48", height: "48", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }),
3184
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { fontWeight: 600, fontSize: "16px", color: "#1f2937" }, children: "Message sent!" }),
3185
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { color: "#6b7280" }, children: "We'll get back to you soon." })
3186
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className: "diffsome-chat-offline-form", children: [
3187
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "diffsome-chat-offline-msg", children: offlineMessage }),
3188
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3189
+ "input",
3190
+ {
3191
+ type: "text",
3192
+ placeholder: "Your name",
3193
+ value: name,
3194
+ onChange: (e) => setName(e.target.value),
3195
+ className: "diffsome-chat-offline-input"
3196
+ }
3197
+ ),
3198
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3199
+ "input",
3200
+ {
3201
+ type: "email",
3202
+ placeholder: "Email *",
3203
+ value: email,
3204
+ onChange: (e) => setEmail(e.target.value),
3205
+ required: true,
3206
+ className: "diffsome-chat-offline-input"
3207
+ }
3208
+ ),
3209
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3210
+ "textarea",
3211
+ {
3212
+ placeholder: "Your message *",
3213
+ value: message,
3214
+ onChange: (e) => setMessage(e.target.value),
3215
+ required: true,
3216
+ rows: 4,
3217
+ className: "diffsome-chat-offline-textarea"
3218
+ }
3219
+ ),
3220
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "submit", disabled: loading || !email || !message, className: "diffsome-chat-offline-btn", children: loading ? "Sending..." : "Leave Message" })
3221
+ ] }) })
3222
+ ] });
3223
+ };
3117
3224
  // Annotate the CommonJS export names for ESM import in node:
3118
3225
  0 && (module.exports = {
3119
3226
  ChatBubble,
package/dist/index.mjs CHANGED
@@ -2809,8 +2809,22 @@ var ChatBubble = ({
2809
2809
  const [unreadCount, setUnreadCount] = useState22(0);
2810
2810
  const [initialized, setInitialized] = useState22(false);
2811
2811
  const [ended, setEnded] = useState22(false);
2812
+ const [offline, setOffline] = useState22(false);
2813
+ const [offlineMessage, setOfflineMessage] = useState22("");
2814
+ const [offlineSent, setOfflineSent] = useState22(false);
2815
+ const [offlineLoading, setOfflineLoading] = useState22(false);
2812
2816
  const seenMessageIds = useRef3(/* @__PURE__ */ new Set());
2813
2817
  const connectionRef = useRef3(null);
2818
+ useEffect20(() => {
2819
+ client.chat.checkAvailability().then((result) => {
2820
+ const data = result?.data ?? result;
2821
+ if (data?.within_operating_hours === false) {
2822
+ setOffline(true);
2823
+ setOfflineMessage(data.offline_message || "We are currently offline. Please leave a message.");
2824
+ }
2825
+ }).catch(() => {
2826
+ });
2827
+ }, [client.chat]);
2814
2828
  const setupListeners = useCallback22((conn) => {
2815
2829
  conn.onMessage((msg) => {
2816
2830
  if (seenMessageIds.current.has(msg.id)) {
@@ -2953,6 +2967,23 @@ var ChatBubble = ({
2953
2967
  seenMessageIds.current.clear();
2954
2968
  initChat();
2955
2969
  };
2970
+ const handleOfflineSubmit = async (name, email, message) => {
2971
+ setOfflineLoading(true);
2972
+ try {
2973
+ await client.chat.start({
2974
+ visitor_name: name || "Visitor",
2975
+ visitor_email: email,
2976
+ initial_message: message,
2977
+ channel: "web",
2978
+ metadata: { offline: true }
2979
+ });
2980
+ setOfflineSent(true);
2981
+ } catch (error) {
2982
+ console.error("Failed to send offline message:", error);
2983
+ } finally {
2984
+ setOfflineLoading(false);
2985
+ }
2986
+ };
2956
2987
  useEffect20(() => {
2957
2988
  return () => {
2958
2989
  connectionRef.current?.disconnect();
@@ -2965,7 +2996,17 @@ var ChatBubble = ({
2965
2996
  className: `diffsome-chat-bubble-container ${positionClass} ${className}`,
2966
2997
  style: { "--diffsome-chat-primary": primaryColor, zIndex },
2967
2998
  children: [
2968
- isOpen && /* @__PURE__ */ jsx6(
2999
+ isOpen && (offline ? /* @__PURE__ */ jsx6(
3000
+ OfflineForm,
3001
+ {
3002
+ title,
3003
+ offlineMessage,
3004
+ onClose: handleClose,
3005
+ onSubmit: handleOfflineSubmit,
3006
+ submitted: offlineSent,
3007
+ loading: offlineLoading
3008
+ }
3009
+ ) : /* @__PURE__ */ jsx6(
2969
3010
  ChatWidget,
2970
3011
  {
2971
3012
  messages,
@@ -2985,7 +3026,7 @@ var ChatBubble = ({
2985
3026
  loginUrl,
2986
3027
  loginPromptMessage
2987
3028
  }
2988
- ),
3029
+ )),
2989
3030
  /* @__PURE__ */ jsxs5(
2990
3031
  "button",
2991
3032
  {
@@ -3022,6 +3063,72 @@ var ChatBubble = ({
3022
3063
  }
3023
3064
  );
3024
3065
  };
3066
+ var OfflineForm = ({
3067
+ title,
3068
+ offlineMessage,
3069
+ onClose,
3070
+ onSubmit,
3071
+ submitted,
3072
+ loading
3073
+ }) => {
3074
+ const [name, setName] = useState22("");
3075
+ const [email, setEmail] = useState22("");
3076
+ const [message, setMessage] = useState22("");
3077
+ const handleSubmit = (e) => {
3078
+ e.preventDefault();
3079
+ if (!email || !message) return;
3080
+ onSubmit(name, email, message);
3081
+ };
3082
+ return /* @__PURE__ */ jsxs5("div", { className: "diffsome-chat-widget", children: [
3083
+ /* @__PURE__ */ jsxs5("div", { className: "diffsome-chat-header", children: [
3084
+ /* @__PURE__ */ jsxs5("div", { className: "diffsome-chat-header-info", children: [
3085
+ /* @__PURE__ */ jsx6("div", { className: "diffsome-chat-header-title", children: title }),
3086
+ /* @__PURE__ */ jsx6("div", { className: "diffsome-chat-header-subtitle", children: "Offline" })
3087
+ ] }),
3088
+ /* @__PURE__ */ jsx6("button", { className: "diffsome-chat-header-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx6("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", width: "24", height: "24", children: /* @__PURE__ */ jsx6("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) })
3089
+ ] }),
3090
+ /* @__PURE__ */ jsx6("div", { className: "diffsome-chat-offline-body", children: submitted ? /* @__PURE__ */ jsxs5("div", { className: "diffsome-chat-offline-success", children: [
3091
+ /* @__PURE__ */ jsx6("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "#10b981", width: "48", height: "48", children: /* @__PURE__ */ jsx6("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }),
3092
+ /* @__PURE__ */ jsx6("p", { style: { fontWeight: 600, fontSize: "16px", color: "#1f2937" }, children: "Message sent!" }),
3093
+ /* @__PURE__ */ jsx6("p", { style: { color: "#6b7280" }, children: "We'll get back to you soon." })
3094
+ ] }) : /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className: "diffsome-chat-offline-form", children: [
3095
+ /* @__PURE__ */ jsx6("p", { className: "diffsome-chat-offline-msg", children: offlineMessage }),
3096
+ /* @__PURE__ */ jsx6(
3097
+ "input",
3098
+ {
3099
+ type: "text",
3100
+ placeholder: "Your name",
3101
+ value: name,
3102
+ onChange: (e) => setName(e.target.value),
3103
+ className: "diffsome-chat-offline-input"
3104
+ }
3105
+ ),
3106
+ /* @__PURE__ */ jsx6(
3107
+ "input",
3108
+ {
3109
+ type: "email",
3110
+ placeholder: "Email *",
3111
+ value: email,
3112
+ onChange: (e) => setEmail(e.target.value),
3113
+ required: true,
3114
+ className: "diffsome-chat-offline-input"
3115
+ }
3116
+ ),
3117
+ /* @__PURE__ */ jsx6(
3118
+ "textarea",
3119
+ {
3120
+ placeholder: "Your message *",
3121
+ value: message,
3122
+ onChange: (e) => setMessage(e.target.value),
3123
+ required: true,
3124
+ rows: 4,
3125
+ className: "diffsome-chat-offline-textarea"
3126
+ }
3127
+ ),
3128
+ /* @__PURE__ */ jsx6("button", { type: "submit", disabled: loading || !email || !message, className: "diffsome-chat-offline-btn", children: loading ? "Sending..." : "Leave Message" })
3129
+ ] }) })
3130
+ ] });
3131
+ };
3025
3132
  export {
3026
3133
  ChatBubble,
3027
3134
  ChatInput,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diffsome/react",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "React hooks and providers for Diffsome SDK - Headless e-commerce & CMS",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",