@magicfeedback/popup-sdk 1.1.0 → 1.1.1
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 +0 -19
- package/dist/chunk-5VVCJBND.mjs +140 -0
- package/dist/chunk-5VVCJBND.mjs.map +1 -0
- package/dist/chunk-M6PEFLLQ.mjs +119 -0
- package/dist/chunk-M6PEFLLQ.mjs.map +1 -0
- package/dist/index.d.mts +400 -40
- package/dist/index.d.ts +400 -40
- package/dist/index.js +903 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +758 -3411
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +470 -0
- package/dist/react-native.d.ts +470 -0
- package/dist/react-native.js +3202 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +1675 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/renderPopup-7GGKR5BY.mjs +1376 -0
- package/dist/renderPopup-7GGKR5BY.mjs.map +1 -0
- package/dist/renderPopup-Y4WG3575.mjs +3303 -0
- package/dist/renderPopup-Y4WG3575.mjs.map +1 -0
- package/package.json +36 -2
|
@@ -0,0 +1,3202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/tracking/tracking-manager.ts
|
|
34
|
+
function createDefaultStorage() {
|
|
35
|
+
try {
|
|
36
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
37
|
+
const ls = window.localStorage;
|
|
38
|
+
const probe = "__deepdots_probe__";
|
|
39
|
+
ls.setItem(probe, "1");
|
|
40
|
+
ls.removeItem(probe);
|
|
41
|
+
return {
|
|
42
|
+
getItem: (k) => ls.getItem(k),
|
|
43
|
+
setItem: (k, v) => ls.setItem(k, v),
|
|
44
|
+
removeItem: (k) => ls.removeItem(k)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
return new InMemoryStorage();
|
|
50
|
+
}
|
|
51
|
+
function buildSurveyIdentity(userId, sessionId, miniService = null) {
|
|
52
|
+
const profile = userId ? [{ key: "external-user-id", value: [userId] }] : [];
|
|
53
|
+
const metadata = [];
|
|
54
|
+
if (sessionId) metadata.push({ key: "session_id", value: [sessionId] });
|
|
55
|
+
if (userId) metadata.push({ key: "user_id", value: [userId] });
|
|
56
|
+
if (miniService) metadata.push({ key: "mini_service", value: [miniService] });
|
|
57
|
+
return { profile, metadata };
|
|
58
|
+
}
|
|
59
|
+
function defaultUuid() {
|
|
60
|
+
const g = typeof globalThis !== "undefined" ? globalThis : {};
|
|
61
|
+
if (g.crypto?.randomUUID) return g.crypto.randomUUID();
|
|
62
|
+
const bytes = new Uint8Array(16);
|
|
63
|
+
if (g.crypto?.getRandomValues) {
|
|
64
|
+
g.crypto.getRandomValues(bytes);
|
|
65
|
+
} else {
|
|
66
|
+
for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
67
|
+
}
|
|
68
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
69
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
70
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
71
|
+
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
72
|
+
}
|
|
73
|
+
var InMemoryStorage, STORAGE_KEYS, TrackingManager;
|
|
74
|
+
var init_tracking_manager = __esm({
|
|
75
|
+
"src/tracking/tracking-manager.ts"() {
|
|
76
|
+
"use strict";
|
|
77
|
+
InMemoryStorage = class {
|
|
78
|
+
constructor() {
|
|
79
|
+
this.map = /* @__PURE__ */ new Map();
|
|
80
|
+
}
|
|
81
|
+
getItem(key) {
|
|
82
|
+
return this.map.has(key) ? this.map.get(key) : null;
|
|
83
|
+
}
|
|
84
|
+
setItem(key, value) {
|
|
85
|
+
this.map.set(key, value);
|
|
86
|
+
}
|
|
87
|
+
removeItem(key) {
|
|
88
|
+
this.map.delete(key);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
STORAGE_KEYS = {
|
|
92
|
+
userId: "deepdots.user_id",
|
|
93
|
+
firstSeen: "deepdots.user.first_seen",
|
|
94
|
+
contactAttributes: "deepdots.contact.attributes"
|
|
95
|
+
};
|
|
96
|
+
TrackingManager = class {
|
|
97
|
+
constructor(options) {
|
|
98
|
+
/** session_id cacheado, provisto por el backend (no se genera ni se persiste). */
|
|
99
|
+
this.sessionId = null;
|
|
100
|
+
this.userWasJustCreated = false;
|
|
101
|
+
this.storage = options.storage;
|
|
102
|
+
this.clientUserId = options.clientUserId;
|
|
103
|
+
this.now = options.now ?? (() => Date.now());
|
|
104
|
+
this.uuid = options.uuid ?? defaultUuid;
|
|
105
|
+
this.enabled = options.enabled ?? true;
|
|
106
|
+
}
|
|
107
|
+
isTrackingEnabled() {
|
|
108
|
+
return this.enabled;
|
|
109
|
+
}
|
|
110
|
+
setTrackingEnabled(enabled) {
|
|
111
|
+
this.enabled = enabled;
|
|
112
|
+
}
|
|
113
|
+
/** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */
|
|
114
|
+
getUserId() {
|
|
115
|
+
if (!this.enabled) return null;
|
|
116
|
+
if (this.clientUserId) return this.clientUserId;
|
|
117
|
+
const persisted = this.storage.getItem(STORAGE_KEYS.userId);
|
|
118
|
+
if (persisted) return persisted;
|
|
119
|
+
const id = this.uuid();
|
|
120
|
+
this.storage.setItem(STORAGE_KEYS.userId, id);
|
|
121
|
+
this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));
|
|
122
|
+
this.userWasJustCreated = true;
|
|
123
|
+
return id;
|
|
124
|
+
}
|
|
125
|
+
/** `true` solo en la primera sesión tras crear el user_id propio del SDK. */
|
|
126
|
+
isNewUser() {
|
|
127
|
+
return this.userWasJustCreated;
|
|
128
|
+
}
|
|
129
|
+
/** session_id actual provisto por el backend, o null si aún no llegó. */
|
|
130
|
+
getSessionId() {
|
|
131
|
+
if (!this.enabled) return null;
|
|
132
|
+
return this.sessionId;
|
|
133
|
+
}
|
|
134
|
+
/** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */
|
|
135
|
+
setSessionId(sessionId) {
|
|
136
|
+
if (!this.enabled) return;
|
|
137
|
+
this.sessionId = sessionId;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// src/assets/style.css
|
|
144
|
+
var style_default;
|
|
145
|
+
var init_style = __esm({
|
|
146
|
+
"src/assets/style.css"() {
|
|
147
|
+
style_default = `.magicfeedback-container {
|
|
148
|
+
width: 100%;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
.magicfeedback-success-message,
|
|
153
|
+
.magicfeedback-info-message,
|
|
154
|
+
.magicfeedback-start-message,
|
|
155
|
+
.magicfeedback-container{
|
|
156
|
+
animation-duration: 0.5s;
|
|
157
|
+
animation-name: animate-fade;
|
|
158
|
+
animation-delay: 0.25s;
|
|
159
|
+
animation-fill-mode: backwards;
|
|
160
|
+
|
|
161
|
+
font-style: normal;
|
|
162
|
+
text-align: justify;
|
|
163
|
+
font-weight: 400;
|
|
164
|
+
font-size: 1.2rem;
|
|
165
|
+
color: #475569;
|
|
166
|
+
line-height: 1.5;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@keyframes animate-fade {
|
|
170
|
+
0% { opacity: 0; }
|
|
171
|
+
100% { opacity: 1; }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.magicfeedback-form {
|
|
175
|
+
display: flex;
|
|
176
|
+
flex-direction: column;
|
|
177
|
+
justify-content: flex-start;
|
|
178
|
+
align-items: center;
|
|
179
|
+
width: 100%;
|
|
180
|
+
}
|
|
181
|
+
.magicfeedback-questions {
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
justify-content: flex-start;
|
|
185
|
+
align-items: flex-start;
|
|
186
|
+
width: 100%;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.magicfeedback-div {
|
|
190
|
+
display: flex;
|
|
191
|
+
flex-direction: column;
|
|
192
|
+
justify-content: flex-start;
|
|
193
|
+
align-items: flex-start;
|
|
194
|
+
margin-bottom: 10px;
|
|
195
|
+
width: 100%;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.magicfeedback-label {
|
|
199
|
+
margin-bottom: 25px;
|
|
200
|
+
margin-top: 20px;
|
|
201
|
+
font-weight: 400;
|
|
202
|
+
font-family: inherit;
|
|
203
|
+
font-size: 1.2rem;
|
|
204
|
+
color: #212529;
|
|
205
|
+
text-align: start;
|
|
206
|
+
word-wrap: break-word;
|
|
207
|
+
white-space: normal;
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.magicfeedback-checkbox {
|
|
212
|
+
display: flex;
|
|
213
|
+
flex-direction: column;
|
|
214
|
+
flex-wrap: wrap;
|
|
215
|
+
width: 100%;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.magicfeedback-checkbox-container {
|
|
219
|
+
display: flex;
|
|
220
|
+
flex-direction: row;
|
|
221
|
+
justify-content: flex-start;
|
|
222
|
+
align-items: center;
|
|
223
|
+
margin-bottom: 5px;
|
|
224
|
+
margin-top: 5px;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.magicfeedback-radio {
|
|
228
|
+
display: flex;
|
|
229
|
+
flex-direction: column;
|
|
230
|
+
justify-content: flex-start;
|
|
231
|
+
flex-wrap: wrap;
|
|
232
|
+
width: 100%;
|
|
233
|
+
margin-right: 5px;
|
|
234
|
+
padding: 5px;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.magicfeedback-checkbox-container,
|
|
238
|
+
.magicfeedback-radio-container {
|
|
239
|
+
display: flex;
|
|
240
|
+
flex-direction: row;
|
|
241
|
+
justify-content: flex-start;
|
|
242
|
+
align-items: center;
|
|
243
|
+
margin: 5px;
|
|
244
|
+
padding: 5px 0;
|
|
245
|
+
background-color: #fff;
|
|
246
|
+
border-radius: 5px;
|
|
247
|
+
box-shadow: 0 0 3px #ccc;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.deepdots-popup input::-webkit-outer-spin-button,
|
|
251
|
+
.deepdots-popup input::-webkit-inner-spin-button {
|
|
252
|
+
-webkit-appearance: none;
|
|
253
|
+
-moz-appearance: textfield;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.magicfeedback-checkbox input[type="checkbox"] {
|
|
257
|
+
margin-right: 5px;
|
|
258
|
+
margin-left: 5px;
|
|
259
|
+
width: 20px;
|
|
260
|
+
height: 20px;
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
.magicfeedback-checkbox label,
|
|
266
|
+
.magicfeedback-radio-container label {
|
|
267
|
+
margin-left: 5px;
|
|
268
|
+
font-weight: 400;
|
|
269
|
+
font-family: inherit;
|
|
270
|
+
font-size: 16px;
|
|
271
|
+
color: #212529;
|
|
272
|
+
text-align: start;
|
|
273
|
+
width: 100%;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.deepdots-popup p {
|
|
277
|
+
margin-bottom: 0px;
|
|
278
|
+
font-size: 12px;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.deepdots-popup h2 {
|
|
282
|
+
line-height: 1.66;
|
|
283
|
+
margin: 0;
|
|
284
|
+
padding: 0;
|
|
285
|
+
font-weight: 900;
|
|
286
|
+
color: #222;
|
|
287
|
+
font-family: 'Montserrat';
|
|
288
|
+
font-size: 18px;
|
|
289
|
+
text-transform: uppercase;
|
|
290
|
+
text-align: center;
|
|
291
|
+
margin-bottom: 40px;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.deepdots-popup h6 {
|
|
295
|
+
margin-bottom: 1rem;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.deepdots-popup input[type="date"],
|
|
299
|
+
.deepdots-popup input[type="text"],
|
|
300
|
+
.deepdots-popup input[type="number"],
|
|
301
|
+
.deepdots-popup input[type="email"],
|
|
302
|
+
.deepdots-popup input[type="password"],
|
|
303
|
+
.deepdots-popup textarea,
|
|
304
|
+
.deepdots-popup select {
|
|
305
|
+
box-sizing: border-box;
|
|
306
|
+
padding: 10px;
|
|
307
|
+
margin: 10px 0;
|
|
308
|
+
width: 100%;
|
|
309
|
+
border: 2px solid #ccc; /* Default border color - you can customize this */
|
|
310
|
+
border-radius: 5px;
|
|
311
|
+
font-size: 18px;
|
|
312
|
+
outline: none;
|
|
313
|
+
transition: border-color 0.3s ease;
|
|
314
|
+
min-height: 50px;
|
|
315
|
+
font-family: inherit;
|
|
316
|
+
-webkit-appearance: none;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.deepdots-popup select {
|
|
320
|
+
background-image: linear-gradient(45deg, transparent 50%, gray 50%),
|
|
321
|
+
linear-gradient(135deg, gray 50%, transparent 50%),
|
|
322
|
+
linear-gradient(to right, #ccc, #ccc);
|
|
323
|
+
background-position: calc(100% - 20px) calc(1em + 2px),
|
|
324
|
+
calc(100% - 15px) calc(1em + 2px),
|
|
325
|
+
calc(100% - 2.5em) 0.5em;
|
|
326
|
+
background-size: 5px 5px,
|
|
327
|
+
5px 5px,
|
|
328
|
+
1px 1.5em;
|
|
329
|
+
background-repeat: no-repeat;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.magicfeedback-boolean-container {
|
|
333
|
+
display: flex;
|
|
334
|
+
flex-direction: row;
|
|
335
|
+
justify-content: flex-start;
|
|
336
|
+
align-items: center;
|
|
337
|
+
margin: 10px 0;
|
|
338
|
+
width: 100%;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.magicfeedback-boolean-container label {
|
|
342
|
+
margin-left: 10px;
|
|
343
|
+
font-weight: 400;
|
|
344
|
+
font-size: 16px;
|
|
345
|
+
width: 100%;
|
|
346
|
+
padding-top: 5px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.magicfeedback-boolean-option {
|
|
350
|
+
margin-right: 10px;
|
|
351
|
+
margin-left: 10px;
|
|
352
|
+
width: 100%;
|
|
353
|
+
height: 38px;
|
|
354
|
+
border-radius: 8px;
|
|
355
|
+
font-size: 16px;
|
|
356
|
+
cursor: pointer;
|
|
357
|
+
border: 1px solid #ced4da !important;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.magicfeedback-boolean-option label {
|
|
361
|
+
font-weight: 400;
|
|
362
|
+
font-size: 16px;
|
|
363
|
+
color: #475569;
|
|
364
|
+
cursor: pointer;
|
|
365
|
+
width: 100%;
|
|
366
|
+
height: 100%;
|
|
367
|
+
display: flex;
|
|
368
|
+
justify-content: center;
|
|
369
|
+
align-items: center;
|
|
370
|
+
border-radius: inherit;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.magicfeedback-boolean-option input[type=radio] + label:hover {
|
|
374
|
+
color: #007bff;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.magicfeedback-boolean-option input[type=radio]:checked + label {
|
|
378
|
+
color: #007bff;
|
|
379
|
+
background-color: #cadff8;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.deepdots-popup input[type="radio"] {
|
|
383
|
+
padding: 20px;
|
|
384
|
+
margin: 10px 0 10px 15px;
|
|
385
|
+
width: 40px;
|
|
386
|
+
background-color: #fff;
|
|
387
|
+
box-shadow: none;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* Style for focused input/select */
|
|
391
|
+
.deepdots-popup input:focus, .deepdots-popup select:focus {
|
|
392
|
+
border-color: #4CAF50; /* Highlight color when focused - you can customize this */
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Style for invalid input (example: validation error) */
|
|
396
|
+
.deepdots-popup input.error, .deepdots-popup select.error {
|
|
397
|
+
border-color: #ff3333; /* Red color for error state - you can customize this */
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.magicfeedback-action-container {
|
|
401
|
+
display: flex;
|
|
402
|
+
flex-direction: row;
|
|
403
|
+
justify-content: space-between;
|
|
404
|
+
align-items: center;
|
|
405
|
+
margin-top: 10px;
|
|
406
|
+
width: 100%;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.magicfeedback-action-container .magicfeedback-submit {
|
|
410
|
+
cursor: pointer;
|
|
411
|
+
padding: 10px;
|
|
412
|
+
margin: 10px 0;
|
|
413
|
+
width: 100px;
|
|
414
|
+
border-radius: 8px;
|
|
415
|
+
font-size: 16px;
|
|
416
|
+
outline: none;
|
|
417
|
+
transition: border-color 0.3s ease;
|
|
418
|
+
background-color: #1E293B;
|
|
419
|
+
color: white;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.magicfeedback-action-container .magicfeedback-back {
|
|
423
|
+
cursor: pointer;
|
|
424
|
+
padding: 10px;
|
|
425
|
+
margin: 10px 0;
|
|
426
|
+
width: 100px;
|
|
427
|
+
border: 0;
|
|
428
|
+
border-radius: 8px;
|
|
429
|
+
font-size: 16px;
|
|
430
|
+
outline: none;
|
|
431
|
+
transition: border-color 0.3s ease;
|
|
432
|
+
background-color: transparent;
|
|
433
|
+
color: #212529;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
/* Rating styles */
|
|
439
|
+
.magicfeedback-rating {
|
|
440
|
+
width: 100%;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.magicfeedback-rating-container {
|
|
444
|
+
display: flex;
|
|
445
|
+
flex-direction: row;
|
|
446
|
+
justify-content: space-around;
|
|
447
|
+
flex-wrap: wrap;
|
|
448
|
+
align-items: center;
|
|
449
|
+
|
|
450
|
+
margin: 15px 5px;
|
|
451
|
+
padding: 0;
|
|
452
|
+
|
|
453
|
+
list-style: none;
|
|
454
|
+
font-size: 1.5rem;
|
|
455
|
+
cursor: pointer;
|
|
456
|
+
user-select: none;
|
|
457
|
+
-webkit-user-select: none;
|
|
458
|
+
-moz-user-select: none;
|
|
459
|
+
-ms-user-select: none;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.magicfeedback-rating-placeholder-value,
|
|
463
|
+
.magicfeedback-rating-placeholder{
|
|
464
|
+
text-align: center;
|
|
465
|
+
align-content: center;
|
|
466
|
+
justify-content: center;
|
|
467
|
+
text-wrap: wrap;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.magicfeedback-rating-option {
|
|
471
|
+
margin: 0 0.5rem;
|
|
472
|
+
padding: 0;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
@media (max-width: 1024px) {
|
|
476
|
+
.magicfeedback-rating-option {
|
|
477
|
+
margin: 10px 0;
|
|
478
|
+
padding: 0;
|
|
479
|
+
width: 20%;
|
|
480
|
+
display: flex;
|
|
481
|
+
justify-content: center;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.magicfeedback-rating-placeholder-value {
|
|
485
|
+
font-size: 0.75rem;
|
|
486
|
+
margin-bottom: 1px;
|
|
487
|
+
}
|
|
488
|
+
.magicfeedback-rating-placeholder{
|
|
489
|
+
justify-content: center;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.magicfeedback-rating-container {
|
|
493
|
+
flex-wrap: wrap;
|
|
494
|
+
justify-content: flex-start;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
.magicfeedback-rating-option-label-container {
|
|
500
|
+
display: flex;
|
|
501
|
+
flex-direction: column;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
.magicfeedback-rating-option-label-container label {
|
|
506
|
+
display: inline-block;
|
|
507
|
+
text-align: center;
|
|
508
|
+
margin-bottom: 0;
|
|
509
|
+
font-weight: 600;
|
|
510
|
+
font-size: 18px;
|
|
511
|
+
color: #475569;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.magicfeedback-rating-option-label-container input[type=radio] {
|
|
515
|
+
position: absolute;
|
|
516
|
+
opacity: 0;
|
|
517
|
+
width: 0;
|
|
518
|
+
height: 0;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* IMAGE STYLES */
|
|
522
|
+
.magicfeedback-rating-option-label-container input[type=radio] + img {
|
|
523
|
+
cursor: pointer;
|
|
524
|
+
height: 48px;
|
|
525
|
+
width: 48px;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/* LABEL STYLES */
|
|
529
|
+
.magicfeedback-rating-option-label-container input[type=radio] + label {
|
|
530
|
+
cursor: pointer;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/* CHECKED STYLES */
|
|
534
|
+
.magicfeedback-rating-option-label-container input[type=radio]:checked + img {
|
|
535
|
+
box-shadow: #007bff 0px 1px 4px, #007BFFA0 0px 0px 0px 2px;
|
|
536
|
+
border-radius: 50%;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.magicfeedback-rating-number {
|
|
540
|
+
width: 100%;
|
|
541
|
+
}
|
|
542
|
+
.magicfeedback-rating-container,
|
|
543
|
+
.magicfeedback-rating-number-container {
|
|
544
|
+
display: flex;
|
|
545
|
+
flex-direction: row;
|
|
546
|
+
justify-content: space-between;
|
|
547
|
+
align-items: center;
|
|
548
|
+
|
|
549
|
+
margin: 15px 0;
|
|
550
|
+
padding: 0;
|
|
551
|
+
|
|
552
|
+
list-style: none;
|
|
553
|
+
font-size: 1.5rem;
|
|
554
|
+
cursor: pointer;
|
|
555
|
+
user-select: none;
|
|
556
|
+
-webkit-user-select: none;
|
|
557
|
+
-moz-user-select: none;
|
|
558
|
+
-ms-user-select: none;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/* Cuando hay placeholders (Very bad / Very good), el placeholder ocupa
|
|
562
|
+
el 100% de la primera l\xEDnea y los boxes bajan a la siguiente */
|
|
563
|
+
.magicfeedback-rating-number-container--with-placeholder {
|
|
564
|
+
flex-wrap: wrap;
|
|
565
|
+
align-items: flex-start;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.magicfeedback-rating-number-container--with-placeholder .magicfeedback-rating-placeholder {
|
|
569
|
+
width: 100%;
|
|
570
|
+
margin-bottom: 8px;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.magicfeedback-rating-number-container--with-placeholder .magicfeedback-rating-number-option {
|
|
574
|
+
width: auto;
|
|
575
|
+
flex: 1;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.magicfeedback-rating-number-option {
|
|
579
|
+
display: flex;
|
|
580
|
+
flex-direction: row;
|
|
581
|
+
justify-content: flex-start;
|
|
582
|
+
align-items: center;
|
|
583
|
+
margin: 5px;
|
|
584
|
+
padding: 0;
|
|
585
|
+
list-style: none;
|
|
586
|
+
font-size: 1.5rem;
|
|
587
|
+
cursor: pointer;
|
|
588
|
+
user-select: none;
|
|
589
|
+
-webkit-user-select: none;
|
|
590
|
+
-moz-user-select: none;
|
|
591
|
+
-ms-user-select: none;
|
|
592
|
+
width: 100%;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.magicfeedback-rating-number-option-row{
|
|
596
|
+
max-width: 100% !important;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.magicfeedback-rating-number-top-placeholder,
|
|
600
|
+
.magicfeedback-rating-number-bottom-placeholder{
|
|
601
|
+
text-align: center !important;
|
|
602
|
+
align-content: center;
|
|
603
|
+
justify-content: center;
|
|
604
|
+
text-wrap: wrap;
|
|
605
|
+
|
|
606
|
+
font-size: 15px;
|
|
607
|
+
color: #475569;
|
|
608
|
+
|
|
609
|
+
width: 100%;
|
|
610
|
+
margin: auto;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
@media (min-width: 650px) {
|
|
614
|
+
.magicfeedback-rating-number-option-row +.magicfeedback-rating-number-container-ltr:first-child,
|
|
615
|
+
.magicfeedback-rating-number-option-row + +.magicfeedback-rating-number-container-rtl:last-child {
|
|
616
|
+
margin-left: 0;
|
|
617
|
+
width: calc(100% - 10px);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
.magicfeedback-rating-number-option-row +.magicfeedback-rating-number-container-rtl:first-child,
|
|
621
|
+
.magicfeedback-rating-number-option-row + +.magicfeedback-rating-number-container-ltr:last-child {
|
|
622
|
+
margin-right: 0;
|
|
623
|
+
width: calc(100% - 10px);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
.magicfeedback-rating-number-option-label-container {
|
|
629
|
+
display: flex;
|
|
630
|
+
flex-direction: column;
|
|
631
|
+
align-items: center;
|
|
632
|
+
justify-content: center;
|
|
633
|
+
width: 100%;
|
|
634
|
+
height: 100%;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.magicfeedback-rating-number-option-label-container label {
|
|
638
|
+
display: flex;
|
|
639
|
+
align-items: center;
|
|
640
|
+
justify-content: center;
|
|
641
|
+
white-space: nowrap;
|
|
642
|
+
overflow: hidden;
|
|
643
|
+
text-overflow: ellipsis;
|
|
644
|
+
margin: 0 10px;
|
|
645
|
+
font-weight: 600;
|
|
646
|
+
font-size: 14px;
|
|
647
|
+
color: #475569;
|
|
648
|
+
cursor: pointer;
|
|
649
|
+
width: 100%;
|
|
650
|
+
height: 100%;
|
|
651
|
+
min-height: 50px;
|
|
652
|
+
text-align: center;
|
|
653
|
+
padding: 0;
|
|
654
|
+
border-radius: 4px;
|
|
655
|
+
border: 1px solid #ced4da;
|
|
656
|
+
background-color: rgba(246, 246, 246, 0.3);
|
|
657
|
+
transition: background-color 0.15s ease-in-out;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.magicfeedback-rating-number-option-label-container input[type=radio] {
|
|
661
|
+
position: absolute;
|
|
662
|
+
opacity: 0;
|
|
663
|
+
width: 0;
|
|
664
|
+
height: 0;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.magicfeedback-rating-number-option-label-container input[type=radio] + label {
|
|
668
|
+
cursor: pointer;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
.magicfeedback-rating-number-option-label-container input[type=radio] + label:hover,
|
|
672
|
+
.magicfeedback-rating-number-option-label-container input[type=radio]:checked + label {
|
|
673
|
+
color: #007bff;
|
|
674
|
+
border: 1px solid #007bff;
|
|
675
|
+
background-color: rgba(86, 168, 253, 0.29);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.magicfeedback-rating-number-container-column{
|
|
679
|
+
align-items: flex-start !important;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
@media (max-width: 650px) {
|
|
683
|
+
.magicfeedback-rating-number-container-ltr {
|
|
684
|
+
justify-content: center;
|
|
685
|
+
flex-wrap: wrap;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.magicfeedback-rating-number-container-rtl {
|
|
689
|
+
justify-content: center;
|
|
690
|
+
flex-wrap: wrap-reverse;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.magicfeedback-rating-number-option {
|
|
694
|
+
max-width: 100%;
|
|
695
|
+
height: 55px;
|
|
696
|
+
margin: 5px 0;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.magicfeedback-rating-star {
|
|
701
|
+
width: 100%;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.magicfeedback-rating-star-container {
|
|
705
|
+
display: flex !important;
|
|
706
|
+
flex-direction: row !important;
|
|
707
|
+
justify-content: center !important;
|
|
708
|
+
align-items: center !important;
|
|
709
|
+
flex-wrap: wrap;
|
|
710
|
+
margin: 15px 5px;
|
|
711
|
+
padding: 0;
|
|
712
|
+
list-style: none;
|
|
713
|
+
font-size: 1.5rem;
|
|
714
|
+
cursor: pointer;
|
|
715
|
+
user-select: none;
|
|
716
|
+
-webkit-user-select: none;
|
|
717
|
+
-moz-user-select: none;
|
|
718
|
+
-ms-user-select: none;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
.magicfeedback-rating-star-option:not(:checked) > .rating__star {
|
|
722
|
+
float: right;
|
|
723
|
+
width: 1em;
|
|
724
|
+
overflow: hidden;
|
|
725
|
+
white-space: nowrap;
|
|
726
|
+
cursor: pointer;
|
|
727
|
+
font-size: 30px;
|
|
728
|
+
color: #ccc;
|
|
729
|
+
margin: 0 10px;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,
|
|
733
|
+
.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {
|
|
734
|
+
color: #deb217 !important;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.magicfeedback-rating-star-selected {
|
|
738
|
+
color: #ffc700 !important;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
/* Style for multiple choice image */
|
|
743
|
+
.magicfeedback-image-option-label-container:hover {
|
|
744
|
+
cursor: pointer;
|
|
745
|
+
border: 0 !important;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.magicfeedback-image-option-label-container input[type=radio]:checked + img,
|
|
749
|
+
.magicfeedback-image-option-label-container input[type=checkbox]:checked + img {
|
|
750
|
+
border: 1px solid #007bff !important;
|
|
751
|
+
box-shadow: 0 0 3px #007bff !important;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.magicfeedback-image-option-label-container input[type=radio] + img,
|
|
755
|
+
.magicfeedback-image-option-label-container input[type=checkbox] + img {
|
|
756
|
+
border: 1px solid transparent !important;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.magicfeedback-image-option-label-container{
|
|
760
|
+
margin: 10px !important;
|
|
761
|
+
height: calc(100% - 20px) !important;
|
|
762
|
+
width: calc(100% - 20px) !important;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.magicfeedback-multiple-choice-image-option {
|
|
766
|
+
display: flex;
|
|
767
|
+
height: auto !important;
|
|
768
|
+
box-shadow: 0 0 3px #ccc;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.magicfeedback-multiple-choice-image-label {
|
|
772
|
+
font-size: 1rem;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.magicfeedback-consent-container {
|
|
776
|
+
display: flex;
|
|
777
|
+
flex-direction: row;
|
|
778
|
+
justify-content: flex-start;
|
|
779
|
+
align-items: center;
|
|
780
|
+
margin: 10px 0;
|
|
781
|
+
width: 100%;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.magicfeedback-consent-container label {
|
|
785
|
+
margin-left: 10px;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.magicfeedback-start-message-container {
|
|
789
|
+
/* ... add your start message container styles here ... */
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.magicfeedback-success-message,
|
|
793
|
+
.magicfeedback-info-message,
|
|
794
|
+
.magicfeedback-start-message {
|
|
795
|
+
margin: 35px 5px;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.magicfeedback-success-message p,
|
|
799
|
+
.magicfeedback-info-message p,
|
|
800
|
+
.magicfeedback-start-message p {
|
|
801
|
+
font-style: normal;
|
|
802
|
+
text-align: start;
|
|
803
|
+
font-weight: 400;
|
|
804
|
+
font-size: 16px;
|
|
805
|
+
color: #475569;
|
|
806
|
+
line-height: 1.5;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.magicfeedback-success-message img,
|
|
810
|
+
.magicfeedback-info-message img,
|
|
811
|
+
.magicfeedback-start-message img {
|
|
812
|
+
width: 400px;
|
|
813
|
+
max-width: 80%;
|
|
814
|
+
height: auto;
|
|
815
|
+
margin: 10px auto;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.magicfeedback-counter,
|
|
819
|
+
.magicfeedback-point-system-total {
|
|
820
|
+
margin: 10px 0 0 0 !important;
|
|
821
|
+
display: flex !important;
|
|
822
|
+
justify-content: right !important;
|
|
823
|
+
align-items: center !important;
|
|
824
|
+
width: 100%;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.magicfeedback-skip-container {
|
|
828
|
+
background-color: transparent !important;
|
|
829
|
+
border: 0 !important;
|
|
830
|
+
color: #999999FF !important;
|
|
831
|
+
padding: 0 !important;
|
|
832
|
+
margin: 0 !important;
|
|
833
|
+
cursor: pointer !important;
|
|
834
|
+
font-size: 0.5em !important;
|
|
835
|
+
font-weight: 400 !important;
|
|
836
|
+
box-shadow: 0 0 0 !important;
|
|
837
|
+
|
|
838
|
+
&:hover {
|
|
839
|
+
color: #0066ff;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
.magicfeedback-skip-container input[type=checkbox] {
|
|
844
|
+
border-color: #999999FF !important;
|
|
845
|
+
border: 0 !important;
|
|
846
|
+
cursor: pointer !important;
|
|
847
|
+
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.magicfeedback-multi-question-matrix,
|
|
851
|
+
.magicfeedback-point-system,
|
|
852
|
+
.magicfeedback-priority-list {
|
|
853
|
+
width: 100% !important;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.magicfeedback-multi-question-matrix-container,
|
|
857
|
+
.magicfeedback-point-system-container,
|
|
858
|
+
.magicfeedback-priority-list-container {
|
|
859
|
+
width: 100% !important;
|
|
860
|
+
overflow-x: auto;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
.magicfeedback-point-system-list,
|
|
864
|
+
.magicfeedback-priority-list-list,
|
|
865
|
+
.magicfeedback-multi-question-matrix-table {
|
|
866
|
+
width: 100% !important;
|
|
867
|
+
padding: 0 !important;
|
|
868
|
+
overflow-x: auto;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.magicfeedback-multi-question-matrix {
|
|
872
|
+
position: relative !important;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.magicfeedback-multi-question-matrix-container {
|
|
876
|
+
overflow: auto !important;
|
|
877
|
+
padding-bottom: 10px;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.magicfeedback-multi-question-matrix-label{
|
|
881
|
+
text-align: left !important;
|
|
882
|
+
}
|
|
883
|
+
.magicfeedback-priority-list-item {
|
|
884
|
+
display: flex;
|
|
885
|
+
flex-direction: row;
|
|
886
|
+
justify-content: space-between;
|
|
887
|
+
align-items: center;
|
|
888
|
+
padding: 10px 0 10px 10px;
|
|
889
|
+
|
|
890
|
+
border: 1px solid #ced4da;
|
|
891
|
+
border-radius: 5px;
|
|
892
|
+
margin: 10px 0 !important;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.magicfeedback-point-system-item {
|
|
896
|
+
height: 45px;
|
|
897
|
+
margin: 5px 0 !important;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.magicfeedback-point-system-item label,
|
|
901
|
+
.magicfeedback-priority-list-item-label {
|
|
902
|
+
font-family: inherit;
|
|
903
|
+
font-size: 18px !important;
|
|
904
|
+
color: #212529;
|
|
905
|
+
text-align: start;
|
|
906
|
+
word-wrap: break-word;
|
|
907
|
+
white-space: normal;
|
|
908
|
+
padding: 0 10px 0 0;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.magicfeedback-point-system-input-container {
|
|
912
|
+
padding: 0 10px 0 0;
|
|
913
|
+
border-radius: 5px;
|
|
914
|
+
font-size: 14px;
|
|
915
|
+
border: 1px solid #ced4da;
|
|
916
|
+
height: 42px;
|
|
917
|
+
margin: auto 0;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
.magicfeedback-multi-question-matrix-table thead {
|
|
921
|
+
margin-bottom: 20px;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.magicfeedback-multi-question-matrix-table th{
|
|
925
|
+
text-align: center;
|
|
926
|
+
font-weight: 400 !important;
|
|
927
|
+
font-size: 1rem !important;
|
|
928
|
+
padding: 0 10px !important;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
.magicfeedback-multi-question-matrix-table th,
|
|
932
|
+
.magicfeedback-multi-question-matrix-table td label,
|
|
933
|
+
.magicfeedback-priority-list-arrow-up,
|
|
934
|
+
.magicfeedback-priority-list-arrow-down {
|
|
935
|
+
font-style: normal !important;
|
|
936
|
+
font-weight: 400 !important;
|
|
937
|
+
font-size: 1rem !important;
|
|
938
|
+
color: #475569 !important;
|
|
939
|
+
cursor: pointer !important;
|
|
940
|
+
margin: 0 !important;
|
|
941
|
+
line-height: inherit;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
.magicfeedback-multi-question-matrix-table tr {
|
|
945
|
+
align-items: center;
|
|
946
|
+
margin: 5px 0;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
.magicfeedback-multi-question-matrix-row-tr{
|
|
950
|
+
border-radius: 5px;
|
|
951
|
+
margin: 10px 0 !important;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.magicfeedback-multi-question-matrix-row-tr:nth-child(odd) {
|
|
955
|
+
background-color: #f1f6f6; /* Light gray background for odd rows */
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
.magicfeedback-multi-question-matrix-row-tr:nth-child(even) {
|
|
959
|
+
background-color: #ffffff; /* White background for even rows */
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
.magicfeedback-multi-question-matrix-row-label {
|
|
963
|
+
text-align: left !important;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
.magicfeedback-point-system-item span {
|
|
967
|
+
color: #475569 !important;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
.magicfeedback-point-system-item input {
|
|
971
|
+
padding: 0;
|
|
972
|
+
margin: 0 !important;
|
|
973
|
+
width: auto !important;
|
|
974
|
+
min-width: 100px !important;
|
|
975
|
+
height: auto !important;
|
|
976
|
+
min-height: 42px;
|
|
977
|
+
cursor: pointer;
|
|
978
|
+
font-size: 14px;
|
|
979
|
+
text-align: center !important;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
.magicfeedback-multi-question-matrix-table input {
|
|
983
|
+
margin: auto !important;
|
|
984
|
+
padding: 20px !important;
|
|
985
|
+
width: 100% !important;
|
|
986
|
+
height: 15px;
|
|
987
|
+
border: 1px solid #ced4da;
|
|
988
|
+
cursor: pointer;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
.magicfeedback-image{
|
|
992
|
+
max-height: 400px !important;
|
|
993
|
+
height: auto !important;
|
|
994
|
+
max-width: 100% !important;
|
|
995
|
+
margin: 10px auto !important;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
.magicfeedback-error{
|
|
999
|
+
margin-top: 5px;
|
|
1000
|
+
}`;
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// src/ui/renderPopup.ts
|
|
1005
|
+
var renderPopup_exports = {};
|
|
1006
|
+
__export(renderPopup_exports, {
|
|
1007
|
+
renderPopup: () => renderPopup
|
|
1008
|
+
});
|
|
1009
|
+
function ensureMagicFeedbackStyles(_popup) {
|
|
1010
|
+
const STYLE_ID = "magicfeedback-sdk-styles";
|
|
1011
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
1012
|
+
const style = document.createElement("style");
|
|
1013
|
+
style.id = STYLE_ID;
|
|
1014
|
+
style.textContent = style_default;
|
|
1015
|
+
document.head.appendChild(style);
|
|
1016
|
+
}
|
|
1017
|
+
function ensureSpinnerStyles(_popup) {
|
|
1018
|
+
if (document.getElementById("deepdots-spinner-styles")) return;
|
|
1019
|
+
const style = document.createElement("style");
|
|
1020
|
+
style.id = "deepdots-spinner-styles";
|
|
1021
|
+
style.textContent = `
|
|
1022
|
+
@keyframes ddspin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
|
1023
|
+
.mf-spinner { display:flex; justify-content:center; align-items:center; padding:8px 0; }
|
|
1024
|
+
.mf-spinner-circle { width:28px; height:28px; border:3px solid #e0e6ed; border-top-color:#1E293B; border-radius:50%; animation: ddspin 0.9s linear infinite; }
|
|
1025
|
+
`;
|
|
1026
|
+
document.head.appendChild(style);
|
|
1027
|
+
}
|
|
1028
|
+
function ensureResponsiveStyles(_popup) {
|
|
1029
|
+
if (document.getElementById("deepdots-responsive-styles")) return;
|
|
1030
|
+
const style = document.createElement("style");
|
|
1031
|
+
style.id = "deepdots-responsive-styles";
|
|
1032
|
+
style.textContent = `
|
|
1033
|
+
/* Responsive adjustments */
|
|
1034
|
+
@media (max-width: 640px) {
|
|
1035
|
+
.deepdots-popup {
|
|
1036
|
+
width: calc(100% - 24px) !important;
|
|
1037
|
+
max-width: calc(100% - 24px) !important;
|
|
1038
|
+
height: auto !important;
|
|
1039
|
+
max-height: 90vh !important;
|
|
1040
|
+
border-radius: 12px !important;
|
|
1041
|
+
padding: calc(16px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom)) 16px !important;
|
|
1042
|
+
box-sizing: border-box;
|
|
1043
|
+
}
|
|
1044
|
+
.deepdots-popup .mf-spinner-circle { width: 32px; height: 32px; border-width: 4px; }
|
|
1045
|
+
.deepdots-popup button { font-size: 16px !important; }
|
|
1046
|
+
.deepdots-popup-header button { width:48px; height:48px; }
|
|
1047
|
+
.deepdots-popup-header button svg { width:26px; height:26px; }
|
|
1048
|
+
.deepdots-popup-footer { flex-direction: column-reverse !important; gap: 12px !important; }
|
|
1049
|
+
.deepdots-popup-footer button { width: 100%; }
|
|
1050
|
+
}
|
|
1051
|
+
@media (max-width: 400px) {
|
|
1052
|
+
.deepdots-popup { padding: calc(12px + env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom)) 12px !important; }
|
|
1053
|
+
.deepdots-popup-header button { width:48px; height:48px; }
|
|
1054
|
+
.deepdots-popup-header button svg { width:26px; height:26px; }
|
|
1055
|
+
}
|
|
1056
|
+
@media (orientation: landscape) and (max-height: 480px) {
|
|
1057
|
+
.deepdots-popup {
|
|
1058
|
+
height: 100vh !important;
|
|
1059
|
+
max-height: 100vh !important;
|
|
1060
|
+
overflow-y: auto !important;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
`;
|
|
1064
|
+
document.head.appendChild(style);
|
|
1065
|
+
}
|
|
1066
|
+
async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService) {
|
|
1067
|
+
let surveyCompletedEmitted = false;
|
|
1068
|
+
let stylesInjected = false;
|
|
1069
|
+
const isDark = style?.theme === "dark";
|
|
1070
|
+
const theme = {
|
|
1071
|
+
popupBg: isDark ? "#1e1e1e" : "#fff",
|
|
1072
|
+
textPrimary: isDark ? "#f0f0f0" : "#111",
|
|
1073
|
+
closeBtnHoverBg: isDark ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.06)",
|
|
1074
|
+
closeBtnHoverColor: isDark ? "#fff" : "#000000"
|
|
1075
|
+
};
|
|
1076
|
+
const positionMap = {
|
|
1077
|
+
"center": { justifyContent: "center", alignItems: "center", background: "rgba(0,0,0,0.5)" },
|
|
1078
|
+
"bottom": { justifyContent: "center", alignItems: "flex-end", padding: "16px", background: "transparent" },
|
|
1079
|
+
"bottom-right": { justifyContent: "flex-end", alignItems: "flex-end", padding: "16px", background: "transparent" },
|
|
1080
|
+
"bottom-left": { justifyContent: "flex-start", alignItems: "flex-end", padding: "16px", background: "transparent" },
|
|
1081
|
+
"top": { justifyContent: "center", alignItems: "flex-start", padding: "16px", background: "transparent" },
|
|
1082
|
+
"top-right": { justifyContent: "flex-end", alignItems: "flex-start", padding: "16px", background: "transparent" },
|
|
1083
|
+
"top-left": { justifyContent: "flex-start", alignItems: "flex-start", padding: "16px", background: "transparent" }
|
|
1084
|
+
};
|
|
1085
|
+
const pos = positionMap[style?.position ?? "center"] ?? positionMap["center"];
|
|
1086
|
+
const popup = document.createElement("div");
|
|
1087
|
+
popup.className = "deepdots-popup";
|
|
1088
|
+
popup.style.cssText = `
|
|
1089
|
+
position: relative;
|
|
1090
|
+
display: flex;
|
|
1091
|
+
flex-direction: column;
|
|
1092
|
+
justify-content: flex-start;
|
|
1093
|
+
background: ${theme.popupBg};
|
|
1094
|
+
border-radius: 8px;
|
|
1095
|
+
padding: 24px;
|
|
1096
|
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
1097
|
+
max-width: 600px;
|
|
1098
|
+
width: 90%;
|
|
1099
|
+
min-height: 200px;
|
|
1100
|
+
`;
|
|
1101
|
+
const header = document.createElement("div");
|
|
1102
|
+
header.className = "deepdots-popup-header";
|
|
1103
|
+
header.style.cssText = "display:flex; justify-content:flex-end; align-items:center; width:100%;";
|
|
1104
|
+
const closeBtn = document.createElement("button");
|
|
1105
|
+
closeBtn.type = "button";
|
|
1106
|
+
closeBtn.setAttribute("aria-label", "Close popup");
|
|
1107
|
+
closeBtn.innerHTML = `
|
|
1108
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1109
|
+
<path d="M6 6L18 18M6 18L18 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
1110
|
+
</svg>
|
|
1111
|
+
`;
|
|
1112
|
+
closeBtn.style.cssText = `
|
|
1113
|
+
background:transparent;
|
|
1114
|
+
border:none;
|
|
1115
|
+
width:32px;
|
|
1116
|
+
height:32px;
|
|
1117
|
+
display:flex;
|
|
1118
|
+
align-items:center;
|
|
1119
|
+
justify-content:center;
|
|
1120
|
+
border-radius: 8px;
|
|
1121
|
+
cursor:pointer;
|
|
1122
|
+
color:${theme.textPrimary};
|
|
1123
|
+
padding:4px;
|
|
1124
|
+
transition: color .15s ease, transform .15s ease, background .15s ease;
|
|
1125
|
+
box-shadow: none !important;
|
|
1126
|
+
`;
|
|
1127
|
+
closeBtn.onmouseenter = () => {
|
|
1128
|
+
closeBtn.style.color = theme.closeBtnHoverColor;
|
|
1129
|
+
closeBtn.style.background = theme.closeBtnHoverBg;
|
|
1130
|
+
closeBtn.style.transform = "scale(1.06)";
|
|
1131
|
+
};
|
|
1132
|
+
closeBtn.onmouseleave = () => {
|
|
1133
|
+
closeBtn.style.color = theme.textPrimary;
|
|
1134
|
+
closeBtn.style.background = "transparent";
|
|
1135
|
+
closeBtn.style.transform = "scale(1)";
|
|
1136
|
+
};
|
|
1137
|
+
closeBtn.onclick = () => {
|
|
1138
|
+
emit("popup_clicked", surveyId, { action: "close_icon" });
|
|
1139
|
+
onClose();
|
|
1140
|
+
};
|
|
1141
|
+
header.appendChild(closeBtn);
|
|
1142
|
+
ensureMagicFeedbackStyles(popup);
|
|
1143
|
+
ensureSpinnerStyles(popup);
|
|
1144
|
+
ensureResponsiveStyles(popup);
|
|
1145
|
+
const containerContent = document.createElement("div");
|
|
1146
|
+
containerContent.className = "deepdots-popup-container-conetent";
|
|
1147
|
+
containerContent.style.cssText = `
|
|
1148
|
+
display:flex;
|
|
1149
|
+
flex-direction:column;
|
|
1150
|
+
padding: 0 20px 12px 20px;
|
|
1151
|
+
max-height: 80vh; /* l\xEDmite general del popup */
|
|
1152
|
+
overflow: hidden; /* quita scroll del contenedor principal */
|
|
1153
|
+
`;
|
|
1154
|
+
const main = document.createElement("div");
|
|
1155
|
+
main.className = "deepdots-popup-main";
|
|
1156
|
+
main.style.cssText = "display:flex; flex-direction:column; width:100%; max-height:80vh; overflow-y:auto;";
|
|
1157
|
+
const formWrapper = document.createElement("div");
|
|
1158
|
+
formWrapper.style.cssText = "width:100%; flex: 1 1 auto;";
|
|
1159
|
+
const errorHint = document.createElement("div");
|
|
1160
|
+
errorHint.className = "deepdots-error-hint";
|
|
1161
|
+
errorHint.style.cssText = `
|
|
1162
|
+
display: none;
|
|
1163
|
+
margin: 12px 0 0 0;
|
|
1164
|
+
padding: 10px 12px;
|
|
1165
|
+
border-radius: 6px;
|
|
1166
|
+
background: #FEF3C7; /* amber-100 */
|
|
1167
|
+
color: #92400E; /* amber-700 */
|
|
1168
|
+
border: 1px solid #FCD34D; /* amber-300 */
|
|
1169
|
+
font-size: 13px;
|
|
1170
|
+
`;
|
|
1171
|
+
errorHint.setAttribute("role", "alert");
|
|
1172
|
+
errorHint.setAttribute("aria-live", "polite");
|
|
1173
|
+
const spinnerEl = document.createElement("div");
|
|
1174
|
+
spinnerEl.className = "mf-spinner";
|
|
1175
|
+
spinnerEl.setAttribute("role", "status");
|
|
1176
|
+
spinnerEl.setAttribute("aria-label", "Loading survey");
|
|
1177
|
+
spinnerEl.innerHTML = '<div class="mf-spinner-circle"></div>';
|
|
1178
|
+
spinnerEl.style.cssText = "position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);";
|
|
1179
|
+
const formDivId = `magicfeedback-form-${surveyId}`;
|
|
1180
|
+
const formHost = document.createElement("div");
|
|
1181
|
+
formHost.id = formDivId;
|
|
1182
|
+
formHost.style.cssText = "width:100%; visibility:hidden;";
|
|
1183
|
+
formWrapper.appendChild(spinnerEl);
|
|
1184
|
+
formWrapper.appendChild(formHost);
|
|
1185
|
+
main.appendChild(formWrapper);
|
|
1186
|
+
main.appendChild(errorHint);
|
|
1187
|
+
const footer = document.createElement("div");
|
|
1188
|
+
footer.className = "deepdots-popup-footer";
|
|
1189
|
+
footer.setAttribute("data-actions-wrapper", "true");
|
|
1190
|
+
footer.style.cssText = "display:flex; flex-direction: row-reverse ;justify-content:space-between; align-items:center; gap:8px; margin-top:auto; width:100%; padding-top:16px;";
|
|
1191
|
+
const backButton = document.createElement("button");
|
|
1192
|
+
backButton.textContent = actions?.back ? actions.back.label : "Back";
|
|
1193
|
+
backButton.style.cssText = `
|
|
1194
|
+
background: transparent;
|
|
1195
|
+
color: #333;
|
|
1196
|
+
border: 1px solid #999;
|
|
1197
|
+
padding: 12px 24px;
|
|
1198
|
+
border-radius: 4px;
|
|
1199
|
+
cursor: pointer;
|
|
1200
|
+
font-size: 14px;
|
|
1201
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
1202
|
+
transition: filter .15s ease;
|
|
1203
|
+
display: inline-flex;
|
|
1204
|
+
align-items: center;
|
|
1205
|
+
justify-content: center;
|
|
1206
|
+
text-align: center;
|
|
1207
|
+
`;
|
|
1208
|
+
backButton.onmouseenter = () => {
|
|
1209
|
+
backButton.style.filter = "brightness(0.9)";
|
|
1210
|
+
};
|
|
1211
|
+
backButton.onmouseleave = () => {
|
|
1212
|
+
backButton.style.filter = "brightness(1)";
|
|
1213
|
+
};
|
|
1214
|
+
backButton.onclick = () => {
|
|
1215
|
+
emit("popup_clicked", surveyId, { action: "back" });
|
|
1216
|
+
formInstance?.back?.();
|
|
1217
|
+
};
|
|
1218
|
+
const startButton = document.createElement("button");
|
|
1219
|
+
startButton.textContent = actions?.start ? actions.start.label : "Start survey";
|
|
1220
|
+
startButton.style.cssText = `
|
|
1221
|
+
background: #1E293B;
|
|
1222
|
+
color: #fff;
|
|
1223
|
+
border: none;
|
|
1224
|
+
padding: 12px 24px;
|
|
1225
|
+
border-radius: 4px;
|
|
1226
|
+
cursor: pointer;
|
|
1227
|
+
font-size: 14px;
|
|
1228
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
1229
|
+
transition: filter .15s ease;
|
|
1230
|
+
display: inline-flex;
|
|
1231
|
+
align-items: center;
|
|
1232
|
+
justify-content: center;
|
|
1233
|
+
text-align: center;
|
|
1234
|
+
`;
|
|
1235
|
+
startButton.onclick = () => {
|
|
1236
|
+
emit("popup_clicked", surveyId, { action: "start_survey" });
|
|
1237
|
+
formInstance?.startForm?.();
|
|
1238
|
+
};
|
|
1239
|
+
const closeButton = document.createElement("button");
|
|
1240
|
+
closeButton.textContent = actions?.complete ? actions.complete.label : "Complete survey";
|
|
1241
|
+
closeButton.style.cssText = `
|
|
1242
|
+
background: #1E293B;
|
|
1243
|
+
color: #fff;
|
|
1244
|
+
border: none;
|
|
1245
|
+
padding: 12px 24px;
|
|
1246
|
+
border-radius: 4px;
|
|
1247
|
+
cursor: pointer;
|
|
1248
|
+
font-size: 14px;
|
|
1249
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
1250
|
+
transition: filter .15s ease;
|
|
1251
|
+
display: inline-flex;
|
|
1252
|
+
align-items: center;
|
|
1253
|
+
justify-content: center;
|
|
1254
|
+
text-align: center;
|
|
1255
|
+
`;
|
|
1256
|
+
closeButton.onmouseenter = () => {
|
|
1257
|
+
closeButton.style.filter = "brightness(0.9)";
|
|
1258
|
+
};
|
|
1259
|
+
closeButton.onmouseleave = () => {
|
|
1260
|
+
closeButton.style.filter = "brightness(1)";
|
|
1261
|
+
};
|
|
1262
|
+
closeButton.onclick = () => {
|
|
1263
|
+
emit("popup_clicked", surveyId, { action: "complete" });
|
|
1264
|
+
onClose();
|
|
1265
|
+
};
|
|
1266
|
+
const submitButton = document.createElement("button");
|
|
1267
|
+
submitButton.textContent = actions?.accept ? actions.accept.label : "Send";
|
|
1268
|
+
submitButton.style.cssText = `
|
|
1269
|
+
background: #1E293B;
|
|
1270
|
+
color: #fff;
|
|
1271
|
+
border: none;
|
|
1272
|
+
padding: 12px 24px;
|
|
1273
|
+
border-radius: 4px;
|
|
1274
|
+
cursor: pointer;
|
|
1275
|
+
font-size: 14px;
|
|
1276
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
1277
|
+
transition: filter .15s ease;
|
|
1278
|
+
display: inline-flex;
|
|
1279
|
+
align-items: center;
|
|
1280
|
+
justify-content: center;
|
|
1281
|
+
text-align: center;
|
|
1282
|
+
`;
|
|
1283
|
+
submitButton.onclick = () => {
|
|
1284
|
+
if (!surveyCompletedEmitted) {
|
|
1285
|
+
emit("popup_clicked", surveyId, { action: "manual_send" });
|
|
1286
|
+
formInstance?.send?.();
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
backButton.style.display = "none";
|
|
1290
|
+
startButton.style.display = "none";
|
|
1291
|
+
submitButton.style.display = "none";
|
|
1292
|
+
closeButton.style.display = "none";
|
|
1293
|
+
footer.appendChild(submitButton);
|
|
1294
|
+
footer.appendChild(backButton);
|
|
1295
|
+
footer.appendChild(closeButton);
|
|
1296
|
+
footer.appendChild(startButton);
|
|
1297
|
+
main.appendChild(footer);
|
|
1298
|
+
containerContent.appendChild(main);
|
|
1299
|
+
function updateButtons(state) {
|
|
1300
|
+
backButton.style.display = "none";
|
|
1301
|
+
startButton.style.display = "none";
|
|
1302
|
+
submitButton.style.display = "none";
|
|
1303
|
+
closeButton.style.display = "none";
|
|
1304
|
+
backButton.style.width = "";
|
|
1305
|
+
startButton.style.width = "";
|
|
1306
|
+
submitButton.style.width = "";
|
|
1307
|
+
closeButton.style.width = "";
|
|
1308
|
+
switch (state) {
|
|
1309
|
+
case "loading":
|
|
1310
|
+
break;
|
|
1311
|
+
case "start":
|
|
1312
|
+
startButton.style.display = "inline-flex";
|
|
1313
|
+
startButton.style.width = "100%";
|
|
1314
|
+
break;
|
|
1315
|
+
case "in_progress_first":
|
|
1316
|
+
submitButton.style.display = "inline-flex";
|
|
1317
|
+
submitButton.style.width = "";
|
|
1318
|
+
setLoading(false);
|
|
1319
|
+
break;
|
|
1320
|
+
case "in_progress_next":
|
|
1321
|
+
backButton.style.display = "inline-flex";
|
|
1322
|
+
submitButton.style.display = "inline-flex";
|
|
1323
|
+
setLoading(false);
|
|
1324
|
+
break;
|
|
1325
|
+
case "completed":
|
|
1326
|
+
closeButton.style.display = "inline-flex";
|
|
1327
|
+
closeButton.style.width = "100%";
|
|
1328
|
+
setLoading(false);
|
|
1329
|
+
break;
|
|
1330
|
+
case "error":
|
|
1331
|
+
setLoading(false);
|
|
1332
|
+
break;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
popup.appendChild(header);
|
|
1336
|
+
popup.appendChild(containerContent);
|
|
1337
|
+
container.innerHTML = "";
|
|
1338
|
+
container.appendChild(popup);
|
|
1339
|
+
container.style.display = "flex";
|
|
1340
|
+
container.style.justifyContent = pos.justifyContent;
|
|
1341
|
+
container.style.alignItems = pos.alignItems;
|
|
1342
|
+
container.style.background = pos.background;
|
|
1343
|
+
if (pos.padding) container.style.padding = pos.padding;
|
|
1344
|
+
function setLoading(isLoading) {
|
|
1345
|
+
spinnerEl.style.display = isLoading ? "flex" : "none";
|
|
1346
|
+
if (!isLoading) {
|
|
1347
|
+
formHost.style.visibility = "visible";
|
|
1348
|
+
}
|
|
1349
|
+
footer.style.display = isLoading ? "none" : "flex";
|
|
1350
|
+
backButton.disabled = isLoading;
|
|
1351
|
+
startButton.disabled = isLoading;
|
|
1352
|
+
closeButton.disabled = isLoading;
|
|
1353
|
+
submitButton.disabled = isLoading;
|
|
1354
|
+
submitButton.style.opacity = isLoading ? "0.6" : "1";
|
|
1355
|
+
submitButton.style.cursor = isLoading ? "not-allowed" : "pointer";
|
|
1356
|
+
if (isLoading) {
|
|
1357
|
+
updateButtons("loading");
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
setLoading(true);
|
|
1361
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
let formInstance = null;
|
|
1365
|
+
try {
|
|
1366
|
+
if (!import_native.default || typeof import_native.default.form !== "function") {
|
|
1367
|
+
console.warn("[MagicFeedback] form() no disponible. Fallback manual.");
|
|
1368
|
+
setLoading(false);
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
import_native.default.init(
|
|
1372
|
+
{
|
|
1373
|
+
debug: true,
|
|
1374
|
+
env: env === "production" ? "prod" : "dev"
|
|
1375
|
+
}
|
|
1376
|
+
);
|
|
1377
|
+
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null);
|
|
1378
|
+
formInstance = import_native.default.form(surveyId, productId, profile, metadata);
|
|
1379
|
+
const generateOptions = {
|
|
1380
|
+
addButton: false,
|
|
1381
|
+
getMetaData: true
|
|
1382
|
+
};
|
|
1383
|
+
generateOptions.onLoadedEvent = ({ formData }) => {
|
|
1384
|
+
try {
|
|
1385
|
+
} catch (e) {
|
|
1386
|
+
}
|
|
1387
|
+
const s = formData?.style;
|
|
1388
|
+
if (s && !stylesInjected) {
|
|
1389
|
+
stylesInjected = true;
|
|
1390
|
+
if (s.boxBackgroundColor) {
|
|
1391
|
+
popup.style.background = s.boxBackgroundColor;
|
|
1392
|
+
}
|
|
1393
|
+
if (s.contentAlign) {
|
|
1394
|
+
main.style.justifyContent = s.contentAlign === "center" ? "center" : "flex-start";
|
|
1395
|
+
}
|
|
1396
|
+
if (s.buttonPrimaryColor) {
|
|
1397
|
+
submitButton.style.background = s.buttonPrimaryColor;
|
|
1398
|
+
submitButton.style.border = "none";
|
|
1399
|
+
submitButton.style.color = "#fff";
|
|
1400
|
+
startButton.style.background = s.buttonPrimaryColor;
|
|
1401
|
+
startButton.style.border = "none";
|
|
1402
|
+
startButton.style.color = "#fff";
|
|
1403
|
+
closeButton.style.background = s.buttonPrimaryColor;
|
|
1404
|
+
closeButton.style.border = "none";
|
|
1405
|
+
closeButton.style.color = "#fff";
|
|
1406
|
+
}
|
|
1407
|
+
if (s.buttonSecondaryColor) {
|
|
1408
|
+
backButton.style.background = "#fff";
|
|
1409
|
+
backButton.style.color = s.buttonSecondaryColor;
|
|
1410
|
+
backButton.style.border = `1px solid ${s.buttonSecondaryColor}`;
|
|
1411
|
+
}
|
|
1412
|
+
if (s.logo) {
|
|
1413
|
+
if (!document.getElementById("deepdots-popup-logo")) {
|
|
1414
|
+
const logoImg = document.createElement("img");
|
|
1415
|
+
logoImg.id = "deepdots-popup-logo";
|
|
1416
|
+
logoImg.src = s.logo;
|
|
1417
|
+
logoImg.alt = "Logo";
|
|
1418
|
+
logoImg.style.cssText = "max-height:40px; max-width:100%; object-fit:contain;";
|
|
1419
|
+
if (s.logoSize) {
|
|
1420
|
+
switch (s.logoSize) {
|
|
1421
|
+
case "small":
|
|
1422
|
+
logoImg.style.maxHeight = "30px";
|
|
1423
|
+
break;
|
|
1424
|
+
case "medium":
|
|
1425
|
+
logoImg.style.maxHeight = "50px";
|
|
1426
|
+
break;
|
|
1427
|
+
case "large":
|
|
1428
|
+
logoImg.style.maxHeight = "70px";
|
|
1429
|
+
break;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
if (s.logoPosition) {
|
|
1433
|
+
switch (s.logoPosition) {
|
|
1434
|
+
case "left":
|
|
1435
|
+
logoImg.style.margin = "0 16px 42px 0";
|
|
1436
|
+
logoImg.style.display = "block";
|
|
1437
|
+
logoImg.style.marginLeft = "0";
|
|
1438
|
+
break;
|
|
1439
|
+
case "right":
|
|
1440
|
+
logoImg.style.margin = "0 0 42px 16px";
|
|
1441
|
+
logoImg.style.display = "block";
|
|
1442
|
+
logoImg.style.marginLeft = "auto";
|
|
1443
|
+
break;
|
|
1444
|
+
case "center":
|
|
1445
|
+
logoImg.style.margin = "0 auto 42px auto";
|
|
1446
|
+
logoImg.style.display = "block";
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
containerContent.insertBefore(logoImg, main);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (s.startMessage && s.startMessage !== "") {
|
|
1454
|
+
console.log(s.startMessage);
|
|
1455
|
+
updateButtons("start");
|
|
1456
|
+
} else {
|
|
1457
|
+
updateButtons("in_progress_first");
|
|
1458
|
+
}
|
|
1459
|
+
} else {
|
|
1460
|
+
updateButtons("in_progress_first");
|
|
1461
|
+
}
|
|
1462
|
+
emit("popup_clicked", surveyId, { action: "loaded" });
|
|
1463
|
+
setLoading(false);
|
|
1464
|
+
};
|
|
1465
|
+
generateOptions.beforeSubmitEvent = () => {
|
|
1466
|
+
setLoading(true);
|
|
1467
|
+
emit("popup_clicked", surveyId, { action: "before_submit" });
|
|
1468
|
+
};
|
|
1469
|
+
generateOptions.afterSubmitEvent = ({ error, completed, total, progress }) => {
|
|
1470
|
+
const errText = error ? typeof error === "string" ? error : error.message ?? String(error) : "";
|
|
1471
|
+
if (errText) {
|
|
1472
|
+
setLoading(false);
|
|
1473
|
+
if (errText.toLowerCase().includes("no response")) {
|
|
1474
|
+
errorHint.textContent = "Please answer the required question to continue.";
|
|
1475
|
+
errorHint.style.display = "block";
|
|
1476
|
+
emit("popup_clicked", surveyId, { action: "validation_error_required" });
|
|
1477
|
+
updateButtons("in_progress_next");
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
errorHint.textContent = "An error occurred while submitting. Please try again or close the popup.";
|
|
1481
|
+
errorHint.style.display = "block";
|
|
1482
|
+
emit("popup_clicked", surveyId, { action: "submit_error", error: errText });
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
errorHint.style.display = "none";
|
|
1486
|
+
setLoading(false);
|
|
1487
|
+
if (completed) {
|
|
1488
|
+
emit("survey_completed", surveyId, { action: "completed" });
|
|
1489
|
+
surveyCompletedEmitted = true;
|
|
1490
|
+
updateButtons("completed");
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
emit("popup_clicked", surveyId, { action: "partial" });
|
|
1494
|
+
if (total > 1 && progress > 0 && progress < total) {
|
|
1495
|
+
updateButtons("in_progress_next");
|
|
1496
|
+
} else {
|
|
1497
|
+
updateButtons("in_progress_first");
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
generateOptions.onBackEvent = ({ progress }) => {
|
|
1501
|
+
emit("popup_clicked", surveyId, { action: "back" });
|
|
1502
|
+
if (progress === 0) {
|
|
1503
|
+
updateButtons("in_progress_first");
|
|
1504
|
+
} else {
|
|
1505
|
+
updateButtons("in_progress_next");
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
formInstance.generate(formDivId, generateOptions).catch((err) => {
|
|
1509
|
+
console.error("[MagicFeedback] Error generating form:", err);
|
|
1510
|
+
setLoading(false);
|
|
1511
|
+
});
|
|
1512
|
+
} catch (e) {
|
|
1513
|
+
console.error("[MagicFeedback] Exception initializing form:", e);
|
|
1514
|
+
setLoading(false);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
var import_native;
|
|
1518
|
+
var init_renderPopup = __esm({
|
|
1519
|
+
"src/ui/renderPopup.ts"() {
|
|
1520
|
+
"use strict";
|
|
1521
|
+
init_tracking_manager();
|
|
1522
|
+
import_native = __toESM(require("@magicfeedback/native"));
|
|
1523
|
+
init_style();
|
|
1524
|
+
}
|
|
1525
|
+
});
|
|
1526
|
+
|
|
1527
|
+
// src/react-native/index.tsx
|
|
1528
|
+
var react_native_exports = {};
|
|
1529
|
+
__export(react_native_exports, {
|
|
1530
|
+
DeepdotsProvider: () => DeepdotsProvider,
|
|
1531
|
+
ReactNativePopupRenderer: () => ReactNativePopupRenderer,
|
|
1532
|
+
setupReactNative: () => setupReactNative,
|
|
1533
|
+
useDeepdots: () => useDeepdots
|
|
1534
|
+
});
|
|
1535
|
+
module.exports = __toCommonJS(react_native_exports);
|
|
1536
|
+
var import_react = require("react");
|
|
1537
|
+
var import_react_native = require("react-native");
|
|
1538
|
+
var import_react_native_webview = require("react-native-webview");
|
|
1539
|
+
|
|
1540
|
+
// src/types/index.ts
|
|
1541
|
+
var POPUP_TRIGGER_CONDITION_STATUSES = [
|
|
1542
|
+
"SHOWED",
|
|
1543
|
+
"PARTIAL",
|
|
1544
|
+
"COMPLETED"
|
|
1545
|
+
];
|
|
1546
|
+
|
|
1547
|
+
// src/triggers/index.ts
|
|
1548
|
+
function setupTrigger(popups, trigger) {
|
|
1549
|
+
switch (trigger.type) {
|
|
1550
|
+
case "time":
|
|
1551
|
+
setupTimeTrigger(popups, trigger);
|
|
1552
|
+
break;
|
|
1553
|
+
case "scroll":
|
|
1554
|
+
setupScrollTrigger(popups, trigger);
|
|
1555
|
+
break;
|
|
1556
|
+
case "exit":
|
|
1557
|
+
setupExitTrigger(popups, trigger);
|
|
1558
|
+
break;
|
|
1559
|
+
case "event":
|
|
1560
|
+
setupEventTrigger(popups, trigger);
|
|
1561
|
+
break;
|
|
1562
|
+
case "click":
|
|
1563
|
+
setupClickTrigger(popups, trigger);
|
|
1564
|
+
break;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
function setupTimeTrigger(popups, trigger) {
|
|
1568
|
+
const delay = trigger.value || 5e3;
|
|
1569
|
+
setTimeout(() => {
|
|
1570
|
+
popups.triggerSurvey(trigger.surveyId, trigger.popupId);
|
|
1571
|
+
}, delay);
|
|
1572
|
+
popups.debug(`Time trigger set for ${delay}ms`);
|
|
1573
|
+
}
|
|
1574
|
+
function setupScrollTrigger(popups, trigger) {
|
|
1575
|
+
const threshold = trigger.value || 50;
|
|
1576
|
+
const handler = () => {
|
|
1577
|
+
const scrollPercentage = window.scrollY / (document.documentElement.scrollHeight - window.innerHeight) * 100;
|
|
1578
|
+
if (scrollPercentage >= threshold) {
|
|
1579
|
+
popups.triggerSurvey(trigger.surveyId, trigger.popupId);
|
|
1580
|
+
window.removeEventListener("scroll", handler);
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
window.addEventListener("scroll", handler);
|
|
1584
|
+
popups.debug(`Scroll trigger set for ${threshold}%`);
|
|
1585
|
+
}
|
|
1586
|
+
function setupExitTrigger(popups, trigger) {
|
|
1587
|
+
if (typeof window === "undefined") return;
|
|
1588
|
+
const delaySeconds = parseExitDelaySeconds(trigger.value);
|
|
1589
|
+
const historyRef = window.history;
|
|
1590
|
+
const originalPushState = historyRef.pushState.bind(historyRef);
|
|
1591
|
+
const originalReplaceState = historyRef.replaceState.bind(historyRef);
|
|
1592
|
+
let lastUrl = normalizeUrl(window.location.href);
|
|
1593
|
+
let lastQueuedKey = "";
|
|
1594
|
+
let lastQueuedAt = 0;
|
|
1595
|
+
const queueLeave = (fromRaw, toRaw) => {
|
|
1596
|
+
const from = normalizeUrl(fromRaw);
|
|
1597
|
+
const to = normalizeUrl(toRaw);
|
|
1598
|
+
if (!from || !to || from === to) return;
|
|
1599
|
+
const key = `${from}=>${to}`;
|
|
1600
|
+
const now = Date.now();
|
|
1601
|
+
if (key === lastQueuedKey && now - lastQueuedAt < 150) return;
|
|
1602
|
+
lastQueuedKey = key;
|
|
1603
|
+
lastQueuedAt = now;
|
|
1604
|
+
popups.queueExitPopup(trigger.surveyId, delaySeconds, from, trigger.popupId);
|
|
1605
|
+
};
|
|
1606
|
+
const handleNavigation = (to) => {
|
|
1607
|
+
const from = lastUrl;
|
|
1608
|
+
queueLeave(from, to);
|
|
1609
|
+
lastUrl = normalizeUrl(to);
|
|
1610
|
+
};
|
|
1611
|
+
const handleDocumentClick = (event) => {
|
|
1612
|
+
if (event.defaultPrevented) return;
|
|
1613
|
+
if (event.button !== 0) return;
|
|
1614
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
1615
|
+
const anchor = event.target?.closest?.("a");
|
|
1616
|
+
if (!(anchor instanceof HTMLAnchorElement)) return;
|
|
1617
|
+
const href = anchor.getAttribute("href");
|
|
1618
|
+
if (!href || href.startsWith("javascript:") || href.startsWith("mailto:") || href.startsWith("tel:")) {
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
const target = anchor.getAttribute("target");
|
|
1622
|
+
if (target && target !== "_self") return;
|
|
1623
|
+
let destination;
|
|
1624
|
+
try {
|
|
1625
|
+
destination = new URL(href, window.location.href);
|
|
1626
|
+
} catch {
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
if (destination.origin !== window.location.origin) return;
|
|
1630
|
+
queueLeave(window.location.href, destination.href);
|
|
1631
|
+
};
|
|
1632
|
+
const handleHashChange = (event) => {
|
|
1633
|
+
const to = event.newURL || window.location.href;
|
|
1634
|
+
const from = event.oldURL || lastUrl;
|
|
1635
|
+
queueLeave(from, to);
|
|
1636
|
+
lastUrl = normalizeUrl(to);
|
|
1637
|
+
};
|
|
1638
|
+
const handlePopState = () => {
|
|
1639
|
+
handleNavigation(window.location.href);
|
|
1640
|
+
};
|
|
1641
|
+
historyRef.pushState = function(data, unused, url) {
|
|
1642
|
+
const from = window.location.href;
|
|
1643
|
+
const result = originalPushState(data, unused, url);
|
|
1644
|
+
queueLeave(from, window.location.href);
|
|
1645
|
+
lastUrl = normalizeUrl(window.location.href);
|
|
1646
|
+
return result;
|
|
1647
|
+
};
|
|
1648
|
+
historyRef.replaceState = function(data, unused, url) {
|
|
1649
|
+
const from = window.location.href;
|
|
1650
|
+
const result = originalReplaceState(data, unused, url);
|
|
1651
|
+
queueLeave(from, window.location.href);
|
|
1652
|
+
lastUrl = normalizeUrl(window.location.href);
|
|
1653
|
+
return result;
|
|
1654
|
+
};
|
|
1655
|
+
document.addEventListener("click", handleDocumentClick, true);
|
|
1656
|
+
window.addEventListener("hashchange", handleHashChange);
|
|
1657
|
+
window.addEventListener("popstate", handlePopState);
|
|
1658
|
+
popups.debug("Exit trigger set (deferred to next route)", {
|
|
1659
|
+
surveyId: trigger.surveyId,
|
|
1660
|
+
delaySeconds
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
function setupEventTrigger(popups, trigger) {
|
|
1664
|
+
popups.debug("Event trigger registered", {
|
|
1665
|
+
popupId: trigger.popupId,
|
|
1666
|
+
surveyId: trigger.surveyId,
|
|
1667
|
+
eventName: String(trigger.value || "")
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
function parseExitDelaySeconds(value) {
|
|
1671
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1672
|
+
return Math.max(0, value);
|
|
1673
|
+
}
|
|
1674
|
+
if (typeof value === "string" && value.trim()) {
|
|
1675
|
+
const parsed = Number(value);
|
|
1676
|
+
if (Number.isFinite(parsed)) return Math.max(0, parsed);
|
|
1677
|
+
}
|
|
1678
|
+
return 0;
|
|
1679
|
+
}
|
|
1680
|
+
function normalizeUrl(value) {
|
|
1681
|
+
if (!value) return "";
|
|
1682
|
+
const withoutIndex = value.replace(/\/index\.html(?=($|[?#]))/i, "");
|
|
1683
|
+
return withoutIndex.length > 1 && withoutIndex.endsWith("/") ? withoutIndex.slice(0, -1) : withoutIndex;
|
|
1684
|
+
}
|
|
1685
|
+
function setupClickTrigger(popups, trigger) {
|
|
1686
|
+
const targetId = typeof trigger.value === "string" ? trigger.value : "";
|
|
1687
|
+
if (!targetId) {
|
|
1688
|
+
popups.debug("Click trigger missing element id", trigger);
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
const attach = () => {
|
|
1692
|
+
const el = document.getElementById(targetId);
|
|
1693
|
+
if (!el) {
|
|
1694
|
+
popups.debug("Click trigger target not found", targetId);
|
|
1695
|
+
return false;
|
|
1696
|
+
}
|
|
1697
|
+
const handler = (e) => {
|
|
1698
|
+
if (e.defaultPrevented) return;
|
|
1699
|
+
popups.triggerSurvey(trigger.surveyId, trigger.popupId);
|
|
1700
|
+
el.removeEventListener("click", handler);
|
|
1701
|
+
};
|
|
1702
|
+
el.addEventListener("click", handler);
|
|
1703
|
+
popups.debug(`Click trigger set for #${targetId}`);
|
|
1704
|
+
return true;
|
|
1705
|
+
};
|
|
1706
|
+
if (typeof document === "undefined") return;
|
|
1707
|
+
if (attach()) return;
|
|
1708
|
+
window.addEventListener("DOMContentLoaded", () => attach(), { once: true });
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// src/config/env.ts
|
|
1712
|
+
var ENVIRONMENT_MAP = {
|
|
1713
|
+
development: {
|
|
1714
|
+
apiBaseUrl: "https://api-dev.deepdots.com"
|
|
1715
|
+
},
|
|
1716
|
+
production: {
|
|
1717
|
+
apiBaseUrl: "https://api.deepdots.com"
|
|
1718
|
+
}
|
|
1719
|
+
};
|
|
1720
|
+
function resolveEnvironment(nodeEnv) {
|
|
1721
|
+
return ENVIRONMENT_MAP[nodeEnv || "production"];
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
// src/platform/react-native-renderer.ts
|
|
1725
|
+
init_tracking_manager();
|
|
1726
|
+
|
|
1727
|
+
// src/ui/surveyHtml.ts
|
|
1728
|
+
function buildSurveyHtml(opts) {
|
|
1729
|
+
const env = opts.env === "development" ? "dev" : "prod";
|
|
1730
|
+
const version = opts.version ?? "2.2.4";
|
|
1731
|
+
const cdn = `https://cdn.jsdelivr.net/npm/@magicfeedback/native@${version}/dist/magicfeedback-sdk.browser.js`;
|
|
1732
|
+
const sid = JSON.stringify(opts.surveyId);
|
|
1733
|
+
const pid = JSON.stringify(opts.productId);
|
|
1734
|
+
const profileJson = JSON.stringify(opts.profile ?? []);
|
|
1735
|
+
const metaJson = JSON.stringify(opts.metadata ?? []);
|
|
1736
|
+
return `<!DOCTYPE html><html><head>
|
|
1737
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
1738
|
+
<meta name="color-scheme" content="light"/>
|
|
1739
|
+
<style>html,body{margin:0;padding:0;background:transparent;font-family:-apple-system,system-ui,sans-serif}#mf{width:100%;box-sizing:border-box}#mf *{max-width:100%;box-sizing:border-box}</style>
|
|
1740
|
+
</head><body>
|
|
1741
|
+
<div id="mf"></div>
|
|
1742
|
+
<script>
|
|
1743
|
+
(function(){
|
|
1744
|
+
function emit(s){ try{ if(window.ReactNativeWebView){ window.ReactNativeWebView.postMessage(s); } }catch(e){} }
|
|
1745
|
+
function emitJSON(n,p){ try{ emit(JSON.stringify({name:n,payload:p||{}})); }catch(e){} }
|
|
1746
|
+
window.DeepdotsClose = function(){ emitJSON('popup_close'); };
|
|
1747
|
+
var initialized=false;
|
|
1748
|
+
function initMF(){
|
|
1749
|
+
if(initialized || !window.magicfeedback) return initialized;
|
|
1750
|
+
initialized=true;
|
|
1751
|
+
try{
|
|
1752
|
+
window.magicfeedback.init({ debug:false, env:'${env}' });
|
|
1753
|
+
var form = window.magicfeedback.form(${sid}, ${pid}, ${profileJson}, ${metaJson});
|
|
1754
|
+
window.DeepdotsForm = form;
|
|
1755
|
+
form.generate('mf', {
|
|
1756
|
+
addButton:false,
|
|
1757
|
+
getMetaData:true,
|
|
1758
|
+
onLoadedEvent:function(){ emitJSON('loaded'); },
|
|
1759
|
+
beforeSubmitEvent:function(){ emitJSON('before_submit'); },
|
|
1760
|
+
afterSubmitEvent:function(p){
|
|
1761
|
+
try{ if(p && p.completed){ emitJSON('survey_completed', p); } else { emitJSON('after_submit', p); } }
|
|
1762
|
+
catch(e){ emitJSON('after_submit'); }
|
|
1763
|
+
},
|
|
1764
|
+
onBackEvent:function(p){ emitJSON('back', p); }
|
|
1765
|
+
}).catch(function(e){ emit('error:init'); });
|
|
1766
|
+
}catch(e){ emit('error:exception'); }
|
|
1767
|
+
return true;
|
|
1768
|
+
}
|
|
1769
|
+
var s=document.createElement('script'); s.src=${JSON.stringify(cdn)}; s.async=true;
|
|
1770
|
+
s.onload=function(){ initMF(); };
|
|
1771
|
+
s.onerror=function(){ emit('error:load'); };
|
|
1772
|
+
document.head.appendChild(s);
|
|
1773
|
+
var tries=0, t=setInterval(function(){ if(initMF()||++tries>40){ clearInterval(t); } },150);
|
|
1774
|
+
})();
|
|
1775
|
+
</script>
|
|
1776
|
+
</body></html>`;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// src/platform/react-native-renderer.ts
|
|
1780
|
+
var ReactNativePopupRenderer = class {
|
|
1781
|
+
constructor(options = {}) {
|
|
1782
|
+
this.options = options;
|
|
1783
|
+
this.emitFn = null;
|
|
1784
|
+
this.onCloseFn = null;
|
|
1785
|
+
this.currentSurveyId = null;
|
|
1786
|
+
this.partialEmitted = false;
|
|
1787
|
+
}
|
|
1788
|
+
init() {
|
|
1789
|
+
}
|
|
1790
|
+
show(surveyId, productId, _actions, emit, onClose, env = "production", userId, _style, sessionId, miniService) {
|
|
1791
|
+
this.emitFn = emit;
|
|
1792
|
+
this.onCloseFn = onClose;
|
|
1793
|
+
this.currentSurveyId = surveyId;
|
|
1794
|
+
this.partialEmitted = false;
|
|
1795
|
+
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null);
|
|
1796
|
+
const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata });
|
|
1797
|
+
if (this.options.onShow) {
|
|
1798
|
+
this.options.onShow({ surveyId, productId, html });
|
|
1799
|
+
} else {
|
|
1800
|
+
console.warn(
|
|
1801
|
+
"[Deepdots] ReactNativePopupRenderer sin onShow: pasa { onShow } a new ReactNativePopupRenderer({...}) para montar el WebView del survey."
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
hide() {
|
|
1806
|
+
this.options.onHide?.();
|
|
1807
|
+
this.currentSurveyId = null;
|
|
1808
|
+
}
|
|
1809
|
+
/**
|
|
1810
|
+
* El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
|
|
1811
|
+
* eventos del SDK: primera interacción → `popup_clicked` (PARTIAL), completado →
|
|
1812
|
+
* `survey_completed` (COMPLETED) y cierra el popup.
|
|
1813
|
+
*/
|
|
1814
|
+
handleMessage(raw) {
|
|
1815
|
+
const surveyId = this.currentSurveyId;
|
|
1816
|
+
if (!surveyId || !this.emitFn) return;
|
|
1817
|
+
let name;
|
|
1818
|
+
try {
|
|
1819
|
+
name = JSON.parse(raw).name;
|
|
1820
|
+
} catch {
|
|
1821
|
+
name = raw;
|
|
1822
|
+
}
|
|
1823
|
+
switch (name) {
|
|
1824
|
+
case "loaded":
|
|
1825
|
+
case "before_submit":
|
|
1826
|
+
case "after_submit":
|
|
1827
|
+
case "back":
|
|
1828
|
+
if (!this.partialEmitted) {
|
|
1829
|
+
this.partialEmitted = true;
|
|
1830
|
+
this.emitFn("popup_clicked", surveyId, { action: "partial" });
|
|
1831
|
+
}
|
|
1832
|
+
break;
|
|
1833
|
+
case "survey_completed":
|
|
1834
|
+
this.emitFn("survey_completed", surveyId);
|
|
1835
|
+
this.onCloseFn?.();
|
|
1836
|
+
break;
|
|
1837
|
+
case "popup_close":
|
|
1838
|
+
this.onCloseFn?.();
|
|
1839
|
+
break;
|
|
1840
|
+
default:
|
|
1841
|
+
break;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
function isReactNativeEnv() {
|
|
1846
|
+
return typeof navigator !== "undefined" && navigator.product === "ReactNative";
|
|
1847
|
+
}
|
|
1848
|
+
function createReactNativeRenderer() {
|
|
1849
|
+
return new ReactNativePopupRenderer();
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
// src/platform/renderer.ts
|
|
1853
|
+
var NoopPopupRenderer = class {
|
|
1854
|
+
show() {
|
|
1855
|
+
}
|
|
1856
|
+
hide() {
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
var BrowserPopupRenderer = class {
|
|
1860
|
+
constructor() {
|
|
1861
|
+
this.container = null;
|
|
1862
|
+
this.visible = false;
|
|
1863
|
+
}
|
|
1864
|
+
init() {
|
|
1865
|
+
if (typeof document === "undefined") return;
|
|
1866
|
+
if (!this.container) {
|
|
1867
|
+
this.container = document.createElement("div");
|
|
1868
|
+
this.container.id = "deepdots-popup-container";
|
|
1869
|
+
this.container.style.cssText = `
|
|
1870
|
+
position: fixed;
|
|
1871
|
+
top: 0;
|
|
1872
|
+
left: 0;
|
|
1873
|
+
width: 100%;
|
|
1874
|
+
height: 100%;
|
|
1875
|
+
display: none;
|
|
1876
|
+
z-index: 999999;
|
|
1877
|
+
background: rgba(0, 0, 0, 0.5);
|
|
1878
|
+
justify-content: center;
|
|
1879
|
+
align-items: center;
|
|
1880
|
+
`;
|
|
1881
|
+
}
|
|
1882
|
+
if (!document.body.contains(this.container)) {
|
|
1883
|
+
document.body.appendChild(this.container);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService) {
|
|
1887
|
+
if (this.visible) return;
|
|
1888
|
+
if (!this.container || !document.body.contains(this.container)) this.init();
|
|
1889
|
+
if (!this.container) return;
|
|
1890
|
+
this.visible = true;
|
|
1891
|
+
const container = this.container;
|
|
1892
|
+
void Promise.resolve().then(() => (init_renderPopup(), renderPopup_exports)).then(({ renderPopup: renderPopup2 }) => {
|
|
1893
|
+
renderPopup2(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService);
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
hide() {
|
|
1897
|
+
this.visible = false;
|
|
1898
|
+
if (this.container) {
|
|
1899
|
+
this.container.style.display = "none";
|
|
1900
|
+
this.container.innerHTML = "";
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
function createDefaultRenderer() {
|
|
1905
|
+
if (typeof document === "undefined") {
|
|
1906
|
+
if (isReactNativeEnv()) return createReactNativeRenderer();
|
|
1907
|
+
return new NoopPopupRenderer();
|
|
1908
|
+
}
|
|
1909
|
+
if (isReactNativeEnv()) return createReactNativeRenderer();
|
|
1910
|
+
return new BrowserPopupRenderer();
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
// src/core/deepdots-popups.ts
|
|
1914
|
+
init_tracking_manager();
|
|
1915
|
+
|
|
1916
|
+
// src/analytics/analytics-manager.ts
|
|
1917
|
+
var dryRunSink = (payload) => {
|
|
1918
|
+
console.log(
|
|
1919
|
+
"[DeepdotsAnalytics] (dry-run \xB7 NO enviado \xB7 sin init.analytics) POST /sdk/feedback \u2192",
|
|
1920
|
+
JSON.stringify(payload, null, 2)
|
|
1921
|
+
);
|
|
1922
|
+
};
|
|
1923
|
+
var AnalyticsManager = class {
|
|
1924
|
+
constructor(options = {}) {
|
|
1925
|
+
this.events = [];
|
|
1926
|
+
this.attributes = {};
|
|
1927
|
+
this.miniService = null;
|
|
1928
|
+
this.miniServiceEnteredAt = 0;
|
|
1929
|
+
this.sink = options.sink ?? dryRunSink;
|
|
1930
|
+
this.now = options.now ?? (() => Date.now());
|
|
1931
|
+
this.publicKey = options.publicKey;
|
|
1932
|
+
this.language = options.language;
|
|
1933
|
+
this.device = options.device;
|
|
1934
|
+
this.platform = options.platform ?? "web";
|
|
1935
|
+
}
|
|
1936
|
+
/** Mezcla user attributes (se coercionan a string). Mutable en runtime. */
|
|
1937
|
+
setUserAttributes(attrs) {
|
|
1938
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
1939
|
+
if (!k) continue;
|
|
1940
|
+
this.attributes[k] = String(v);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
/** Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`. */
|
|
1944
|
+
enterMiniService(name, entryPointType) {
|
|
1945
|
+
this.miniService = name;
|
|
1946
|
+
this.miniServiceEnteredAt = this.now();
|
|
1947
|
+
this.track("mini_service_enter", { entry_point_type: entryPointType ?? null });
|
|
1948
|
+
}
|
|
1949
|
+
/** Cierra el mini-service activo emitiendo `mini_service_exit` con su duración (#27). No-op si no hay ninguno. */
|
|
1950
|
+
exitMiniService() {
|
|
1951
|
+
const name = this.miniService;
|
|
1952
|
+
if (name == null) return;
|
|
1953
|
+
const durationSeconds = Math.max(0, Math.round((this.now() - this.miniServiceEnteredAt) / 1e3));
|
|
1954
|
+
this.miniService = null;
|
|
1955
|
+
this.track("mini_service_exit", { mini_service: name, duration_seconds: durationSeconds });
|
|
1956
|
+
}
|
|
1957
|
+
/** Registra un evento de analítica (modelo GA: nombre + params). */
|
|
1958
|
+
track(name, params) {
|
|
1959
|
+
const merged = {
|
|
1960
|
+
...this.miniService ? { mini_service: this.miniService } : {},
|
|
1961
|
+
...params ?? {}
|
|
1962
|
+
};
|
|
1963
|
+
this.events.push({
|
|
1964
|
+
name,
|
|
1965
|
+
timestamp: this.now(),
|
|
1966
|
+
params: Object.keys(merged).length ? merged : void 0
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
/** Mini-service activo (para inyectarlo en la metadata del survey, #33). */
|
|
1970
|
+
getMiniService() {
|
|
1971
|
+
return this.miniService;
|
|
1972
|
+
}
|
|
1973
|
+
/** Nº de eventos pendientes de flush. */
|
|
1974
|
+
pending() {
|
|
1975
|
+
return this.events.length;
|
|
1976
|
+
}
|
|
1977
|
+
/** Construye el envelope que se enviaría al endpoint de analytics. */
|
|
1978
|
+
buildPayload(identity) {
|
|
1979
|
+
return {
|
|
1980
|
+
publicKey: this.publicKey,
|
|
1981
|
+
userId: identity.userId,
|
|
1982
|
+
sessionId: identity.sessionId,
|
|
1983
|
+
context: {
|
|
1984
|
+
platform: this.platform,
|
|
1985
|
+
language: this.language,
|
|
1986
|
+
device: this.device,
|
|
1987
|
+
attributes: { ...this.attributes }
|
|
1988
|
+
},
|
|
1989
|
+
events: [...this.events]
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
/** Envía (vía sink) el lote acumulado y vacía el buffer. No-op si no hay eventos. */
|
|
1993
|
+
flush(identity) {
|
|
1994
|
+
if (this.events.length === 0) return null;
|
|
1995
|
+
const payload = this.buildPayload(identity);
|
|
1996
|
+
this.sink(payload);
|
|
1997
|
+
this.events = [];
|
|
1998
|
+
return payload;
|
|
1999
|
+
}
|
|
2000
|
+
};
|
|
2001
|
+
|
|
2002
|
+
// src/analytics/feedback-payload.ts
|
|
2003
|
+
function pushKV(list, key, value) {
|
|
2004
|
+
if (value === void 0 || value === null || value === "") return;
|
|
2005
|
+
list.push({ key, value: String(value) });
|
|
2006
|
+
}
|
|
2007
|
+
function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
2008
|
+
const { context } = envelope;
|
|
2009
|
+
const metrics = envelope.events.map((e) => ({
|
|
2010
|
+
key: e.name,
|
|
2011
|
+
value: JSON.stringify({ timestamp: e.timestamp, ...e.params ?? {} })
|
|
2012
|
+
}));
|
|
2013
|
+
const profile = [];
|
|
2014
|
+
pushKV(profile, "external-user-id", envelope.userId);
|
|
2015
|
+
const metadata = [];
|
|
2016
|
+
pushKV(metadata, "user_id", envelope.userId);
|
|
2017
|
+
pushKV(metadata, "session_id", envelope.sessionId);
|
|
2018
|
+
pushKV(metadata, "platform", context.platform);
|
|
2019
|
+
pushKV(metadata, "language", context.language);
|
|
2020
|
+
if (context.device) {
|
|
2021
|
+
pushKV(metadata, "device_type", context.device.device_type);
|
|
2022
|
+
pushKV(metadata, "os_version", context.device.os_version);
|
|
2023
|
+
pushKV(metadata, "device_model", context.device.device_model);
|
|
2024
|
+
pushKV(metadata, "app_version", context.device.app_version);
|
|
2025
|
+
pushKV(metadata, "user_agent", context.device.user_agent);
|
|
2026
|
+
}
|
|
2027
|
+
for (const [k, v] of Object.entries(context.attributes ?? {})) {
|
|
2028
|
+
pushKV(metadata, k, v);
|
|
2029
|
+
}
|
|
2030
|
+
return {
|
|
2031
|
+
feedback: {
|
|
2032
|
+
text: "",
|
|
2033
|
+
answers: [],
|
|
2034
|
+
metadata,
|
|
2035
|
+
metrics,
|
|
2036
|
+
profile,
|
|
2037
|
+
finished: false
|
|
2038
|
+
},
|
|
2039
|
+
publicKey: keys.publicKey,
|
|
2040
|
+
integration: keys.integration,
|
|
2041
|
+
completed: false,
|
|
2042
|
+
// finished: false,
|
|
2043
|
+
...feedbackSessionId ? { sessionId: feedbackSessionId } : {}
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
function createFeedbackSink(options) {
|
|
2047
|
+
let feedbackSessionId;
|
|
2048
|
+
return (envelope) => {
|
|
2049
|
+
const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId);
|
|
2050
|
+
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
2051
|
+
if (!f) {
|
|
2052
|
+
options.log?.("[DeepdotsAnalytics] no fetch disponible; payload no enviado", body);
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
options.log?.("[DeepdotsAnalytics] POST /sdk/feedback \u2192", body);
|
|
2056
|
+
f(`${options.baseUrl}/sdk/feedback`, {
|
|
2057
|
+
method: "POST",
|
|
2058
|
+
headers: { "Content-Type": "application/json" },
|
|
2059
|
+
body: JSON.stringify(body)
|
|
2060
|
+
}).then(async (res) => {
|
|
2061
|
+
if (res.ok) {
|
|
2062
|
+
try {
|
|
2063
|
+
const data = await res.json();
|
|
2064
|
+
if (data?.sessionId && data.sessionId !== feedbackSessionId) {
|
|
2065
|
+
feedbackSessionId = data.sessionId;
|
|
2066
|
+
options.log?.("[DeepdotsAnalytics] feedbackSessionId cacheado:", feedbackSessionId);
|
|
2067
|
+
}
|
|
2068
|
+
} catch {
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
}).catch((err) => options.log?.("[DeepdotsAnalytics] error enviando feedback", err));
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// src/tracking/navigation-observer.ts
|
|
2076
|
+
var UUID_PREFIX = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
2077
|
+
function normalizeScreen(href) {
|
|
2078
|
+
let pathAndHash;
|
|
2079
|
+
try {
|
|
2080
|
+
const u = new URL(href, "http://deepdots.local");
|
|
2081
|
+
pathAndHash = u.pathname + (u.hash || "");
|
|
2082
|
+
} catch {
|
|
2083
|
+
pathAndHash = href;
|
|
2084
|
+
}
|
|
2085
|
+
pathAndHash = pathAndHash.replace(/\?[^#]*/g, "");
|
|
2086
|
+
const collapsed = pathAndHash.split("/").map((seg) => /^[0-9]+$/.test(seg) || UUID_PREFIX.test(seg) ? ":id" : seg).join("/");
|
|
2087
|
+
return collapsed || "/";
|
|
2088
|
+
}
|
|
2089
|
+
var NavigationObserver = class {
|
|
2090
|
+
constructor(options = {}) {
|
|
2091
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
2092
|
+
this.currentScreen = null;
|
|
2093
|
+
this.entryAt = 0;
|
|
2094
|
+
this.installed = false;
|
|
2095
|
+
this.domHandlers = [];
|
|
2096
|
+
this.now = options.now ?? (() => Date.now());
|
|
2097
|
+
}
|
|
2098
|
+
onVisit(listener) {
|
|
2099
|
+
this.listeners.add(listener);
|
|
2100
|
+
}
|
|
2101
|
+
/** Fija la pantalla inicial (sin emitir visita; su duración se cierra al salir). */
|
|
2102
|
+
begin(href) {
|
|
2103
|
+
this.currentScreen = normalizeScreen(href);
|
|
2104
|
+
this.entryAt = this.now();
|
|
2105
|
+
}
|
|
2106
|
+
/** Navega a una pantalla nueva: cierra la anterior (emite visita) y abre la nueva. */
|
|
2107
|
+
visit(href) {
|
|
2108
|
+
const next = normalizeScreen(href);
|
|
2109
|
+
if (next === this.currentScreen) return;
|
|
2110
|
+
this.closeCurrent();
|
|
2111
|
+
this.currentScreen = next;
|
|
2112
|
+
this.entryAt = this.now();
|
|
2113
|
+
}
|
|
2114
|
+
/** Cierra la pantalla actual emitiendo su visita (p. ej. al cerrar la app/pestaña). */
|
|
2115
|
+
stop() {
|
|
2116
|
+
this.closeCurrent();
|
|
2117
|
+
this.currentScreen = null;
|
|
2118
|
+
}
|
|
2119
|
+
closeCurrent() {
|
|
2120
|
+
if (this.currentScreen == null) return;
|
|
2121
|
+
const exit = this.now();
|
|
2122
|
+
const visit = {
|
|
2123
|
+
screen: this.currentScreen,
|
|
2124
|
+
entry: this.entryAt,
|
|
2125
|
+
exit,
|
|
2126
|
+
durationSeconds: Math.max(0, Math.round((exit - this.entryAt) / 1e3))
|
|
2127
|
+
};
|
|
2128
|
+
this.listeners.forEach((l) => {
|
|
2129
|
+
try {
|
|
2130
|
+
l(visit);
|
|
2131
|
+
} catch {
|
|
2132
|
+
}
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
/** Instala los hooks de History en el navegador y registra la pantalla actual. */
|
|
2136
|
+
install() {
|
|
2137
|
+
if (this.installed || typeof window === "undefined" || typeof history === "undefined") return;
|
|
2138
|
+
this.installed = true;
|
|
2139
|
+
const handle = () => this.visit(window.location.href);
|
|
2140
|
+
const originalPush = history.pushState.bind(history);
|
|
2141
|
+
const originalReplace = history.replaceState.bind(history);
|
|
2142
|
+
history.pushState = (data, unused, url) => {
|
|
2143
|
+
const r = originalPush(data, unused, url);
|
|
2144
|
+
handle();
|
|
2145
|
+
return r;
|
|
2146
|
+
};
|
|
2147
|
+
history.replaceState = (data, unused, url) => {
|
|
2148
|
+
const r = originalReplace(data, unused, url);
|
|
2149
|
+
handle();
|
|
2150
|
+
return r;
|
|
2151
|
+
};
|
|
2152
|
+
window.addEventListener("popstate", handle);
|
|
2153
|
+
window.addEventListener("hashchange", handle);
|
|
2154
|
+
this.domHandlers.push(() => {
|
|
2155
|
+
history.pushState = originalPush;
|
|
2156
|
+
history.replaceState = originalReplace;
|
|
2157
|
+
window.removeEventListener("popstate", handle);
|
|
2158
|
+
window.removeEventListener("hashchange", handle);
|
|
2159
|
+
});
|
|
2160
|
+
this.begin(window.location.href);
|
|
2161
|
+
}
|
|
2162
|
+
uninstall() {
|
|
2163
|
+
this.domHandlers.forEach((off) => off());
|
|
2164
|
+
this.domHandlers = [];
|
|
2165
|
+
this.installed = false;
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
|
|
2169
|
+
// src/analytics/device-info.ts
|
|
2170
|
+
function parseDeviceType(ua) {
|
|
2171
|
+
const s = (ua || "").toLowerCase();
|
|
2172
|
+
if (/ipad|tablet|playbook|silk/.test(s) || /android/.test(s) && !/mobile/.test(s)) {
|
|
2173
|
+
return "tablet";
|
|
2174
|
+
}
|
|
2175
|
+
if (/mobi|iphone|ipod|windows phone/.test(s) || /android/.test(s) && /mobile/.test(s)) {
|
|
2176
|
+
return "mobile";
|
|
2177
|
+
}
|
|
2178
|
+
return "desktop";
|
|
2179
|
+
}
|
|
2180
|
+
function collectDeviceInfo(appVersion) {
|
|
2181
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
2182
|
+
return {
|
|
2183
|
+
device_type: parseDeviceType(ua),
|
|
2184
|
+
user_agent: ua || void 0,
|
|
2185
|
+
app_version: appVersion
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
// src/analytics/engagement-tracker.ts
|
|
2190
|
+
var EngagementTracker = class {
|
|
2191
|
+
constructor(now = () => Date.now()) {
|
|
2192
|
+
this.activeMs = 0;
|
|
2193
|
+
this.lastResumeAt = null;
|
|
2194
|
+
this.now = now;
|
|
2195
|
+
}
|
|
2196
|
+
/** Reanuda el conteo (pestaña visible / app en foreground). Idempotente. */
|
|
2197
|
+
resume() {
|
|
2198
|
+
if (this.lastResumeAt === null) this.lastResumeAt = this.now();
|
|
2199
|
+
}
|
|
2200
|
+
/** Pausa el conteo (pestaña oculta / app en background), acumulando el tramo activo. */
|
|
2201
|
+
pause() {
|
|
2202
|
+
if (this.lastResumeAt !== null) {
|
|
2203
|
+
this.activeMs += this.now() - this.lastResumeAt;
|
|
2204
|
+
this.lastResumeAt = null;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
/** Devuelve los ms activos acumulados desde la última lectura y reinicia (sin parar el timer). */
|
|
2208
|
+
consume() {
|
|
2209
|
+
if (this.lastResumeAt !== null) {
|
|
2210
|
+
const t = this.now();
|
|
2211
|
+
this.activeMs += t - this.lastResumeAt;
|
|
2212
|
+
this.lastResumeAt = t;
|
|
2213
|
+
}
|
|
2214
|
+
const ms = this.activeMs;
|
|
2215
|
+
this.activeMs = 0;
|
|
2216
|
+
return ms;
|
|
2217
|
+
}
|
|
2218
|
+
isActive() {
|
|
2219
|
+
return this.lastResumeAt !== null;
|
|
2220
|
+
}
|
|
2221
|
+
};
|
|
2222
|
+
|
|
2223
|
+
// src/contact/contact-manager.ts
|
|
2224
|
+
init_tracking_manager();
|
|
2225
|
+
function stableSerialize(attrs) {
|
|
2226
|
+
const sorted = {};
|
|
2227
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
2228
|
+
sorted[key] = attrs[key];
|
|
2229
|
+
}
|
|
2230
|
+
return JSON.stringify(sorted);
|
|
2231
|
+
}
|
|
2232
|
+
var ContactManager = class {
|
|
2233
|
+
constructor(options) {
|
|
2234
|
+
this.storage = options.storage;
|
|
2235
|
+
this.publicKey = options.publicKey;
|
|
2236
|
+
this.userId = options.userId;
|
|
2237
|
+
this.post = options.post;
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Envía los atributos si cambiaron respecto a lo último enviado.
|
|
2241
|
+
* @returns `true` si se envió, `false` si no hubo cambios.
|
|
2242
|
+
*/
|
|
2243
|
+
async setAttributes(attributes) {
|
|
2244
|
+
const serialized = stableSerialize(attributes);
|
|
2245
|
+
const last = this.storage.getItem(STORAGE_KEYS.contactAttributes);
|
|
2246
|
+
if (last === serialized) return false;
|
|
2247
|
+
await this.post({
|
|
2248
|
+
publicKey: this.publicKey,
|
|
2249
|
+
userId: this.userId,
|
|
2250
|
+
userAttributes: attributes
|
|
2251
|
+
});
|
|
2252
|
+
this.storage.setItem(STORAGE_KEYS.contactAttributes, serialized);
|
|
2253
|
+
return true;
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
// src/core/deepdots-popups.ts
|
|
2258
|
+
var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
|
|
2259
|
+
var DAY_IN_MS = 24 * 60 * 60 * 1e3;
|
|
2260
|
+
var DeepdotsPopups = class {
|
|
2261
|
+
constructor() {
|
|
2262
|
+
this.config = null;
|
|
2263
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
2264
|
+
this.triggers = [];
|
|
2265
|
+
this.initialized = false;
|
|
2266
|
+
this.renderer = createDefaultRenderer();
|
|
2267
|
+
this.popupContainer = null;
|
|
2268
|
+
// deprecated: mantenido para compatibilidad interna
|
|
2269
|
+
this.popupDefinitions = [];
|
|
2270
|
+
this.popupsLoaded = false;
|
|
2271
|
+
this.pendingAutoLaunch = false;
|
|
2272
|
+
this.answeredSurveys = /* @__PURE__ */ new Set();
|
|
2273
|
+
this.surveyProgress = /* @__PURE__ */ new Map();
|
|
2274
|
+
this.lastShown = /* @__PURE__ */ new Map();
|
|
2275
|
+
// popupId -> timestamp mostrado
|
|
2276
|
+
this.surveyToPopupId = /* @__PURE__ */ new Map();
|
|
2277
|
+
// surveyId -> popupId
|
|
2278
|
+
this.deferredExitTimers = [];
|
|
2279
|
+
this.baseUrl = "";
|
|
2280
|
+
this.env = "production";
|
|
2281
|
+
/** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
|
|
2282
|
+
this.tracking = null;
|
|
2283
|
+
/** Capa de analytics (canal separado del feedback). Null hasta init(). */
|
|
2284
|
+
this.analytics = null;
|
|
2285
|
+
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
2286
|
+
this.navObserver = null;
|
|
2287
|
+
/** Tiempo activo (engagement time, #8). */
|
|
2288
|
+
this.engagement = null;
|
|
2289
|
+
/** Atributos internos del usuario identificado → POST /sdk/popups/contact. Null si no hay userId. */
|
|
2290
|
+
this.contact = null;
|
|
2291
|
+
/** Marca si ya se inició la navegación manual (setScreen) — para RN sin History API. */
|
|
2292
|
+
this.navStarted = false;
|
|
2293
|
+
}
|
|
2294
|
+
/** Initialize the SDK with configuration */
|
|
2295
|
+
init(config) {
|
|
2296
|
+
if (this.initialized) {
|
|
2297
|
+
this.log("SDK already initialized");
|
|
2298
|
+
return;
|
|
2299
|
+
}
|
|
2300
|
+
const env = resolveEnvironment(config.nodeEnv);
|
|
2301
|
+
this.baseUrl = env.apiBaseUrl;
|
|
2302
|
+
this.env = config.nodeEnv || "production";
|
|
2303
|
+
this.config = {
|
|
2304
|
+
apiKey: config.apiKey || void 0,
|
|
2305
|
+
debug: config.debug || false,
|
|
2306
|
+
userId: config.userId || void 0
|
|
2307
|
+
};
|
|
2308
|
+
const storage = config.storage ?? createDefaultStorage();
|
|
2309
|
+
this.tracking = new TrackingManager({
|
|
2310
|
+
storage,
|
|
2311
|
+
clientUserId: this.config.userId,
|
|
2312
|
+
enabled: config.trackingEnabled ?? true
|
|
2313
|
+
});
|
|
2314
|
+
if (this.config.userId) {
|
|
2315
|
+
const userId = this.config.userId;
|
|
2316
|
+
const publicKey = this.config.apiKey ?? "";
|
|
2317
|
+
this.contact = new ContactManager({
|
|
2318
|
+
storage,
|
|
2319
|
+
publicKey,
|
|
2320
|
+
userId,
|
|
2321
|
+
post: (body) => this.postContact(body)
|
|
2322
|
+
});
|
|
2323
|
+
if (config.contactAttributes) {
|
|
2324
|
+
void this.setContactAttributes(config.contactAttributes);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
this.log("tracking \xB7 user_id:", this.tracking.getUserId(), "\xB7 new_user:", this.tracking.isNewUser(), "\xB7 enabled:", this.tracking.isTrackingEnabled());
|
|
2328
|
+
const analyticsSink = config.analytics ? createFeedbackSink({
|
|
2329
|
+
baseUrl: this.baseUrl,
|
|
2330
|
+
keys: config.analytics,
|
|
2331
|
+
log: (...a) => this.log(...a)
|
|
2332
|
+
}) : void 0;
|
|
2333
|
+
this.analytics = new AnalyticsManager({
|
|
2334
|
+
sink: analyticsSink,
|
|
2335
|
+
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
2336
|
+
language: typeof navigator !== "undefined" ? navigator.language : void 0,
|
|
2337
|
+
platform: config.platform ?? "web",
|
|
2338
|
+
device: config.device ?? collectDeviceInfo(config.appVersion)
|
|
2339
|
+
});
|
|
2340
|
+
this.navObserver = new NavigationObserver();
|
|
2341
|
+
this.navObserver.onVisit((v) => this.track("page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
2342
|
+
this.navObserver.install();
|
|
2343
|
+
this.engagement = new EngagementTracker();
|
|
2344
|
+
this.engagement.resume();
|
|
2345
|
+
this.setupAnalyticsFlush();
|
|
2346
|
+
this.initialized = true;
|
|
2347
|
+
this.log("SDK initialized", this.config);
|
|
2348
|
+
if (this.renderer.init) this.renderer.init();
|
|
2349
|
+
this.setupPopupContainer();
|
|
2350
|
+
this.fetchPopupsFromServer().then((defs) => {
|
|
2351
|
+
this.popupDefinitions = this.validatePopupDefinitions(defs);
|
|
2352
|
+
this.popupsLoaded = true;
|
|
2353
|
+
this.log("Popups loaded from API", this.popupDefinitions);
|
|
2354
|
+
this.configureTriggersFromDefinitions();
|
|
2355
|
+
this.processDeferredExitQueue();
|
|
2356
|
+
if (this.pendingAutoLaunch) {
|
|
2357
|
+
this.startTriggers();
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
/** User id actual (generado por el SDK o provisto por el cliente). Null si tracking off. */
|
|
2362
|
+
getUserId() {
|
|
2363
|
+
return this.tracking?.getUserId() ?? null;
|
|
2364
|
+
}
|
|
2365
|
+
/** Session id de navegación actual. Null si tracking off. */
|
|
2366
|
+
getSessionId() {
|
|
2367
|
+
return this.tracking?.getSessionId() ?? null;
|
|
2368
|
+
}
|
|
2369
|
+
/** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */
|
|
2370
|
+
setTrackingEnabled(enabled) {
|
|
2371
|
+
this.tracking?.setTrackingEnabled(enabled);
|
|
2372
|
+
this.log("tracking \xB7 setTrackingEnabled:", enabled, "\xB7 session_id:", this.tracking?.getSessionId() ?? null);
|
|
2373
|
+
}
|
|
2374
|
+
// ───────── Analytics (canal separado del feedback, vinculado por user_id) ─────────
|
|
2375
|
+
/** Registra un evento de analítica (modelo GA: nombre + params). No-op si tracking off. */
|
|
2376
|
+
track(name, params) {
|
|
2377
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2378
|
+
this.analytics?.track(name, params);
|
|
2379
|
+
this.log("analytics \xB7 track:", name, params ?? {});
|
|
2380
|
+
}
|
|
2381
|
+
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
2382
|
+
setUserAttributes(attributes) {
|
|
2383
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2384
|
+
this.analytics?.setUserAttributes(attributes);
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Info interna del usuario que solo conoce el host (idioma, edad, plan…), persistida en el
|
|
2388
|
+
* Contact del backend para segmentación/targeting de popups. Identificada por el `userId` del
|
|
2389
|
+
* init. Solo envía si los atributos cambiaron (diff en storage). No-op si tracking off o sin userId.
|
|
2390
|
+
* @returns `true` si se envió al backend, `false` si no hubo cambios o está deshabilitado.
|
|
2391
|
+
*/
|
|
2392
|
+
async setContactAttributes(attributes) {
|
|
2393
|
+
if (!this.tracking?.isTrackingEnabled()) return false;
|
|
2394
|
+
if (!this.contact) return false;
|
|
2395
|
+
return this.contact.setAttributes(attributes);
|
|
2396
|
+
}
|
|
2397
|
+
/** Marca el inicio de un mini-service; etiqueta los eventos siguientes. No-op si tracking off. */
|
|
2398
|
+
enterMiniService(name, entryPointType) {
|
|
2399
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2400
|
+
this.analytics?.enterMiniService(name, entryPointType);
|
|
2401
|
+
this.log("analytics \xB7 enterMiniService:", name, entryPointType ?? "");
|
|
2402
|
+
}
|
|
2403
|
+
/** Cierra el mini-service activo (emite `mini_service_exit` con duración, #27). No-op si tracking off. */
|
|
2404
|
+
exitMiniService() {
|
|
2405
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2406
|
+
this.analytics?.exitMiniService();
|
|
2407
|
+
this.log("analytics \xB7 exitMiniService");
|
|
2408
|
+
}
|
|
2409
|
+
/** Findability (#31/#35): registra una búsqueda. `has_results` se deriva de `resultsCount`. */
|
|
2410
|
+
trackSearch(query, resultsCount, params) {
|
|
2411
|
+
this.track("search", { query, results_count: resultsCount, has_results: resultsCount > 0, ...params ?? {} });
|
|
2412
|
+
}
|
|
2413
|
+
/** Findability friction (#34/#35): señal de fricción con su `friction_topic`. */
|
|
2414
|
+
trackFindabilityFriction(frictionTopic, params) {
|
|
2415
|
+
this.track("findability_friction", { friction_topic: frictionTopic, ...params ?? {} });
|
|
2416
|
+
}
|
|
2417
|
+
/** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
|
|
2418
|
+
trackFunnelStep(funnel, step, taskId, params) {
|
|
2419
|
+
this.track("funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
|
|
2420
|
+
}
|
|
2421
|
+
/**
|
|
2422
|
+
* Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
|
|
2423
|
+
* El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.
|
|
2424
|
+
*/
|
|
2425
|
+
setScreen(name) {
|
|
2426
|
+
if (!this.navObserver) return;
|
|
2427
|
+
if (!this.navStarted) {
|
|
2428
|
+
this.navObserver.begin(name);
|
|
2429
|
+
this.navStarted = true;
|
|
2430
|
+
} else {
|
|
2431
|
+
this.navObserver.visit(name);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
/** App a foreground (RN: AppState 'active'): reanuda el engagement time. */
|
|
2435
|
+
onForeground() {
|
|
2436
|
+
this.engagement?.resume();
|
|
2437
|
+
}
|
|
2438
|
+
/**
|
|
2439
|
+
* App a background (RN: AppState 'background'): cierra pantalla actual (page_view),
|
|
2440
|
+
* mini-service (mini_service_exit), emite engagement y hace flush del lote.
|
|
2441
|
+
*/
|
|
2442
|
+
onBackground() {
|
|
2443
|
+
this.navObserver?.stop();
|
|
2444
|
+
this.navStarted = false;
|
|
2445
|
+
this.exitMiniService();
|
|
2446
|
+
this.flushEngagement();
|
|
2447
|
+
this.engagement?.pause();
|
|
2448
|
+
this.flushAnalytics();
|
|
2449
|
+
}
|
|
2450
|
+
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
2451
|
+
previewAnalytics() {
|
|
2452
|
+
return this.analytics?.buildPayload(this.analyticsIdentity()) ?? {
|
|
2453
|
+
userId: null,
|
|
2454
|
+
sessionId: null,
|
|
2455
|
+
context: { platform: "web", attributes: {} },
|
|
2456
|
+
events: []
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
/** Envía (hoy dry-run → console.log) el lote acumulado de analytics y vacía el buffer. */
|
|
2460
|
+
flushAnalytics() {
|
|
2461
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2462
|
+
this.analytics?.flush(this.analyticsIdentity());
|
|
2463
|
+
}
|
|
2464
|
+
/** Emite un evento `user_engagement` con el tiempo activo acumulado (#8). */
|
|
2465
|
+
flushEngagement() {
|
|
2466
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2467
|
+
const ms = this.engagement?.consume() ?? 0;
|
|
2468
|
+
if (ms > 0) this.track("user_engagement", { engagement_time_msec: ms });
|
|
2469
|
+
}
|
|
2470
|
+
analyticsIdentity() {
|
|
2471
|
+
return {
|
|
2472
|
+
userId: this.tracking?.getUserId() ?? null,
|
|
2473
|
+
sessionId: this.tracking?.getSessionId() ?? null
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
2477
|
+
setupAnalyticsFlush() {
|
|
2478
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2479
|
+
window.addEventListener("pagehide", () => {
|
|
2480
|
+
this.navObserver?.stop();
|
|
2481
|
+
this.exitMiniService();
|
|
2482
|
+
this.flushEngagement();
|
|
2483
|
+
this.flushAnalytics();
|
|
2484
|
+
});
|
|
2485
|
+
document.addEventListener("visibilitychange", () => {
|
|
2486
|
+
if (document.visibilityState === "hidden") {
|
|
2487
|
+
this.flushEngagement();
|
|
2488
|
+
this.engagement?.pause();
|
|
2489
|
+
this.flushAnalytics();
|
|
2490
|
+
} else if (document.visibilityState === "visible") {
|
|
2491
|
+
this.engagement?.resume();
|
|
2492
|
+
}
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
/** Enable auto-launch functionality with configured triggers */
|
|
2496
|
+
autoLaunch() {
|
|
2497
|
+
if (!this.initialized) {
|
|
2498
|
+
throw new Error("SDK not initialized. Call init() first.");
|
|
2499
|
+
}
|
|
2500
|
+
if (!this.popupsLoaded) {
|
|
2501
|
+
this.pendingAutoLaunch = true;
|
|
2502
|
+
this.log("Auto-launch deferred until popups are loaded");
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
this.startTriggers();
|
|
2506
|
+
}
|
|
2507
|
+
/** Inicia los triggers configurados */
|
|
2508
|
+
startTriggers() {
|
|
2509
|
+
this.log("Auto-launch enabled");
|
|
2510
|
+
this.triggers.forEach((trigger) => setupTrigger(this, trigger));
|
|
2511
|
+
}
|
|
2512
|
+
/** Configure triggers for auto-launching popups (manual) */
|
|
2513
|
+
configureTriggers(triggers) {
|
|
2514
|
+
if (!this.initialized) {
|
|
2515
|
+
throw new Error("SDK not initialized. Call init() first.");
|
|
2516
|
+
}
|
|
2517
|
+
this.triggers = triggers;
|
|
2518
|
+
this.log("Triggers configured", triggers);
|
|
2519
|
+
}
|
|
2520
|
+
/** Deriva triggers desde las definiciones de popup */
|
|
2521
|
+
configureTriggersFromDefinitions() {
|
|
2522
|
+
const derived = [];
|
|
2523
|
+
this.debug("Validated popup definitions", this.popupDefinitions);
|
|
2524
|
+
this.popupDefinitions.forEach((def) => {
|
|
2525
|
+
def.triggers.forEach((trigger) => {
|
|
2526
|
+
const type = this.mapPopupTriggerType(trigger.type);
|
|
2527
|
+
if (!type) {
|
|
2528
|
+
this.debug("Unsupported trigger type", trigger.type);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
const value = type === "time" && typeof trigger.value === "number" ? trigger.value * 1e3 : trigger.value;
|
|
2532
|
+
derived.push({ type, value, surveyId: def.surveyId, popupId: def.id });
|
|
2533
|
+
});
|
|
2534
|
+
});
|
|
2535
|
+
if (derived.length) {
|
|
2536
|
+
this.configureTriggers(derived);
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
/** Lógica para evaluar condiciones antes de mostrar una encuesta */
|
|
2540
|
+
triggerSurvey(surveyId, popupId) {
|
|
2541
|
+
const def = this.findPopupDefinition(surveyId, popupId);
|
|
2542
|
+
if (!def) {
|
|
2543
|
+
this.debug("No popup definition for trigger", { surveyId, popupId });
|
|
2544
|
+
return;
|
|
2545
|
+
}
|
|
2546
|
+
if (!this.shouldShow(def)) {
|
|
2547
|
+
this.debug("Conditions prevented showing popup", def.id);
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
this.showDefinition(def);
|
|
2551
|
+
}
|
|
2552
|
+
/** Trigger popups configured with trigger.type = 'event' and matching trigger.value */
|
|
2553
|
+
triggerEvent(eventName) {
|
|
2554
|
+
if (!this.initialized) {
|
|
2555
|
+
throw new Error("SDK not initialized. Call init() first.");
|
|
2556
|
+
}
|
|
2557
|
+
const normalized = String(eventName || "").trim();
|
|
2558
|
+
if (!normalized) {
|
|
2559
|
+
this.debug("Ignoring empty event trigger name");
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
const candidates = this.popupDefinitions.filter((def) => {
|
|
2563
|
+
return def.triggers.some((trigger) => {
|
|
2564
|
+
const triggerValue = String(trigger.value ?? "").trim();
|
|
2565
|
+
return trigger.type === "event" && triggerValue === normalized;
|
|
2566
|
+
});
|
|
2567
|
+
});
|
|
2568
|
+
if (!candidates.length) {
|
|
2569
|
+
this.debug("No event popup definitions found", { eventName: normalized });
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2572
|
+
const matched = candidates.find((def) => this.shouldShow(def));
|
|
2573
|
+
if (!matched) {
|
|
2574
|
+
this.debug("Event popup blocked by conditions/segments", { eventName: normalized });
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2577
|
+
this.showDefinition(matched);
|
|
2578
|
+
}
|
|
2579
|
+
showDefinition(def) {
|
|
2580
|
+
this.log("Showing popup (definition)", def);
|
|
2581
|
+
this.surveyToPopupId.set(def.surveyId, def.id);
|
|
2582
|
+
this.lastShown.set(def.id, Date.now());
|
|
2583
|
+
this.renderPopup(def.surveyId, def.productId, def.actions, def.style);
|
|
2584
|
+
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
2585
|
+
}
|
|
2586
|
+
shouldShow(def, pathUrl, skipPathCheck = false) {
|
|
2587
|
+
if (!skipPathCheck && !this.matchesSegmentsPath(def, pathUrl)) {
|
|
2588
|
+
return false;
|
|
2589
|
+
}
|
|
2590
|
+
if (!this.matchesSegmentsLang(def)) {
|
|
2591
|
+
return false;
|
|
2592
|
+
}
|
|
2593
|
+
const cooldowns = def.cooldown || [];
|
|
2594
|
+
if (cooldowns.length && !cooldowns.every((cooldown) => this.evaluateCooldown(def, cooldown))) {
|
|
2595
|
+
return false;
|
|
2596
|
+
}
|
|
2597
|
+
const legacyConditions = def.legacyConditions || [];
|
|
2598
|
+
if (!legacyConditions.length) return true;
|
|
2599
|
+
return legacyConditions.every((condition) => this.evaluateLegacyCondition(def, condition));
|
|
2600
|
+
}
|
|
2601
|
+
matchesSegmentsLang(def) {
|
|
2602
|
+
const langs = def.segments?.lang;
|
|
2603
|
+
if (!langs || langs.length === 0) return true;
|
|
2604
|
+
const browserLang = (typeof navigator !== "undefined" ? navigator.language : "").toLowerCase();
|
|
2605
|
+
if (!browserLang) {
|
|
2606
|
+
this.debug("No navigator.language available for lang comparison", { popupId: def.id, langs });
|
|
2607
|
+
return true;
|
|
2608
|
+
}
|
|
2609
|
+
const matches = langs.some((lang) => browserLang.startsWith(lang.toLowerCase()));
|
|
2610
|
+
if (!matches) {
|
|
2611
|
+
this.debug("Lang mismatch for popup", { popupId: def.id, langs, browserLang });
|
|
2612
|
+
}
|
|
2613
|
+
return matches;
|
|
2614
|
+
}
|
|
2615
|
+
matchesSegmentsPath(def, pathUrl) {
|
|
2616
|
+
const paths = def.segments?.path;
|
|
2617
|
+
if (!paths || paths.length === 0) return true;
|
|
2618
|
+
if (typeof window === "undefined" || !window.location) {
|
|
2619
|
+
this.debug("No window.location available for path comparison", { popupId: def.id, paths });
|
|
2620
|
+
return true;
|
|
2621
|
+
}
|
|
2622
|
+
const normalizedHref = this.normalizeUrl(pathUrl || window.location.href || "");
|
|
2623
|
+
const currentUrl = this.safeParseUrl(normalizedHref);
|
|
2624
|
+
const normalizedPath = this.normalizeUrl(currentUrl?.pathname || window.location.pathname || "");
|
|
2625
|
+
const matches = paths.some((rawCandidate) => {
|
|
2626
|
+
const candidate = this.normalizeUrl(rawCandidate);
|
|
2627
|
+
let match = false;
|
|
2628
|
+
if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
|
|
2629
|
+
match = normalizedHref === candidate;
|
|
2630
|
+
} else if (candidate.startsWith("/")) {
|
|
2631
|
+
match = normalizedHref.includes(candidate);
|
|
2632
|
+
} else {
|
|
2633
|
+
match = normalizedPath === candidate;
|
|
2634
|
+
}
|
|
2635
|
+
this.debug("Path comparison", {
|
|
2636
|
+
popupId: def.id,
|
|
2637
|
+
candidate,
|
|
2638
|
+
currentPath: normalizedPath,
|
|
2639
|
+
currentHref: normalizedHref,
|
|
2640
|
+
match
|
|
2641
|
+
});
|
|
2642
|
+
return match;
|
|
2643
|
+
});
|
|
2644
|
+
if (!matches) {
|
|
2645
|
+
this.debug("No path match for popup", { popupId: def.id, paths, currentPath: normalizedPath });
|
|
2646
|
+
}
|
|
2647
|
+
return matches;
|
|
2648
|
+
}
|
|
2649
|
+
evaluateCooldown(def, condition) {
|
|
2650
|
+
switch (condition.answered) {
|
|
2651
|
+
case "SHOWED":
|
|
2652
|
+
return this.hasCooldownElapsed(this.lastShown.get(def.id), condition.cooldownDays);
|
|
2653
|
+
case "PARTIAL":
|
|
2654
|
+
case "COMPLETED": {
|
|
2655
|
+
const progress = this.surveyProgress.get(def.surveyId);
|
|
2656
|
+
if (!progress || progress.status !== condition.answered) {
|
|
2657
|
+
return true;
|
|
2658
|
+
}
|
|
2659
|
+
return this.hasCooldownElapsed(progress.timestamp, condition.cooldownDays);
|
|
2660
|
+
}
|
|
2661
|
+
default:
|
|
2662
|
+
return true;
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
evaluateLegacyCondition(def, condition) {
|
|
2666
|
+
if (!condition.answered && this.answeredSurveys.has(def.surveyId)) {
|
|
2667
|
+
return false;
|
|
2668
|
+
}
|
|
2669
|
+
return this.hasCooldownElapsed(this.lastShown.get(def.id), condition.cooldownDays);
|
|
2670
|
+
}
|
|
2671
|
+
hasCooldownElapsed(timestamp, cooldownDays) {
|
|
2672
|
+
if (!timestamp || !cooldownDays) {
|
|
2673
|
+
return true;
|
|
2674
|
+
}
|
|
2675
|
+
return Date.now() - timestamp >= cooldownDays * DAY_IN_MS;
|
|
2676
|
+
}
|
|
2677
|
+
/** Marcar encuesta como contestada externamente */
|
|
2678
|
+
markSurveyAnswered(surveyId) {
|
|
2679
|
+
this.answeredSurveys.add(surveyId);
|
|
2680
|
+
this.markSurveyProgress(surveyId, "COMPLETED");
|
|
2681
|
+
}
|
|
2682
|
+
/** Queue an exit popup so it can render after navigation */
|
|
2683
|
+
queueExitPopup(surveyId, delaySeconds, sourceUrl, popupId) {
|
|
2684
|
+
const def = this.findPopupDefinition(surveyId, popupId);
|
|
2685
|
+
if (!def) {
|
|
2686
|
+
this.debug("No popup definition for exit trigger", { surveyId, popupId });
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
const originUrl = sourceUrl || (typeof window !== "undefined" ? window.location.href : "");
|
|
2690
|
+
if (!originUrl) {
|
|
2691
|
+
this.debug("Exit popup skipped: missing source URL", { surveyId });
|
|
2692
|
+
return;
|
|
2693
|
+
}
|
|
2694
|
+
if (!this.shouldShow(def, originUrl)) {
|
|
2695
|
+
this.debug("Exit popup skipped by conditions/path", { popupId: def.id, sourceUrl: originUrl });
|
|
2696
|
+
return;
|
|
2697
|
+
}
|
|
2698
|
+
const safeDelayMs = Number.isFinite(delaySeconds) ? Math.max(0, delaySeconds * 1e3) : 0;
|
|
2699
|
+
const deferred = {
|
|
2700
|
+
id: def.id,
|
|
2701
|
+
surveyId: def.surveyId,
|
|
2702
|
+
sourceUrl: this.normalizeUrl(originUrl),
|
|
2703
|
+
dueAt: Date.now() + safeDelayMs
|
|
2704
|
+
};
|
|
2705
|
+
const queue = this.getDeferredExitQueue().filter((item) => !(item.id === deferred.id && item.sourceUrl === deferred.sourceUrl));
|
|
2706
|
+
queue.push(deferred);
|
|
2707
|
+
this.setDeferredExitQueue(queue);
|
|
2708
|
+
this.scheduleDeferredExitPopup(deferred);
|
|
2709
|
+
this.debug("Exit popup queued", deferred);
|
|
2710
|
+
}
|
|
2711
|
+
processDeferredExitQueue() {
|
|
2712
|
+
const queue = this.getDeferredExitQueue();
|
|
2713
|
+
if (!queue.length) return;
|
|
2714
|
+
const pending = [];
|
|
2715
|
+
const now = Date.now();
|
|
2716
|
+
queue.forEach((item) => {
|
|
2717
|
+
const def = this.popupDefinitions.find((popup) => popup.id === item.id && popup.surveyId === item.surveyId);
|
|
2718
|
+
if (!def) {
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
if (item.dueAt <= now) {
|
|
2722
|
+
this.tryShowDeferredExitPopup(item);
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
pending.push(item);
|
|
2726
|
+
this.scheduleDeferredExitPopup(item);
|
|
2727
|
+
});
|
|
2728
|
+
this.setDeferredExitQueue(pending);
|
|
2729
|
+
}
|
|
2730
|
+
scheduleDeferredExitPopup(item) {
|
|
2731
|
+
if (typeof window === "undefined") return;
|
|
2732
|
+
const delay = Math.max(0, item.dueAt - Date.now());
|
|
2733
|
+
const timer = window.setTimeout(() => this.tryShowDeferredExitPopup(item), delay);
|
|
2734
|
+
this.deferredExitTimers.push(timer);
|
|
2735
|
+
}
|
|
2736
|
+
tryShowDeferredExitPopup(item) {
|
|
2737
|
+
const def = this.popupDefinitions.find((popup) => popup.id === item.id && popup.surveyId === item.surveyId);
|
|
2738
|
+
if (!def) {
|
|
2739
|
+
this.removeDeferredExitPopup(item);
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
const currentUrl = typeof window !== "undefined" ? this.normalizeUrl(window.location.href || "") : "";
|
|
2743
|
+
if (!currentUrl || currentUrl === this.normalizeUrl(item.sourceUrl)) {
|
|
2744
|
+
this.removeDeferredExitPopup(item);
|
|
2745
|
+
this.debug("Exit popup dropped because route did not change", item);
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
if (!this.shouldShow(def, void 0, true)) {
|
|
2749
|
+
this.removeDeferredExitPopup(item);
|
|
2750
|
+
this.debug("Exit popup skipped at render-time conditions", item);
|
|
2751
|
+
return;
|
|
2752
|
+
}
|
|
2753
|
+
this.showDefinition(def);
|
|
2754
|
+
this.removeDeferredExitPopup(item);
|
|
2755
|
+
}
|
|
2756
|
+
findPopupDefinition(surveyId, popupId) {
|
|
2757
|
+
if (popupId) {
|
|
2758
|
+
const byId = this.popupDefinitions.find((popup) => popup.id === popupId);
|
|
2759
|
+
if (byId) return byId;
|
|
2760
|
+
}
|
|
2761
|
+
return this.popupDefinitions.find((popup) => popup.surveyId === surveyId);
|
|
2762
|
+
}
|
|
2763
|
+
removeDeferredExitPopup(item) {
|
|
2764
|
+
const queue = this.getDeferredExitQueue().filter((entry) => !(entry.id === item.id && entry.sourceUrl === item.sourceUrl));
|
|
2765
|
+
this.setDeferredExitQueue(queue);
|
|
2766
|
+
}
|
|
2767
|
+
getDeferredExitQueue() {
|
|
2768
|
+
if (typeof window === "undefined") return [];
|
|
2769
|
+
try {
|
|
2770
|
+
const raw = window.sessionStorage.getItem(EXIT_QUEUE_STORAGE_KEY);
|
|
2771
|
+
if (!raw) return [];
|
|
2772
|
+
const parsed = JSON.parse(raw);
|
|
2773
|
+
if (!Array.isArray(parsed)) return [];
|
|
2774
|
+
return parsed.filter((entry) => {
|
|
2775
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
2776
|
+
const item = entry;
|
|
2777
|
+
return typeof item.id === "string" && typeof item.surveyId === "string" && typeof item.sourceUrl === "string" && typeof item.dueAt === "number" && Number.isFinite(item.dueAt);
|
|
2778
|
+
}).map((item) => ({
|
|
2779
|
+
...item,
|
|
2780
|
+
sourceUrl: this.normalizeUrl(item.sourceUrl)
|
|
2781
|
+
}));
|
|
2782
|
+
} catch {
|
|
2783
|
+
return [];
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
setDeferredExitQueue(queue) {
|
|
2787
|
+
if (typeof window === "undefined") return;
|
|
2788
|
+
try {
|
|
2789
|
+
if (!queue.length) {
|
|
2790
|
+
window.sessionStorage.removeItem(EXIT_QUEUE_STORAGE_KEY);
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
window.sessionStorage.setItem(EXIT_QUEUE_STORAGE_KEY, JSON.stringify(queue));
|
|
2794
|
+
} catch {
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
/** Fetch al servidor para obtener popups */
|
|
2798
|
+
async fetchPopupsFromServer() {
|
|
2799
|
+
const apiKey = this.config?.apiKey;
|
|
2800
|
+
const baseUrl = this.baseUrl;
|
|
2801
|
+
const userId = this.config?.userId;
|
|
2802
|
+
if (!apiKey || !baseUrl) {
|
|
2803
|
+
this.log("Missing apiKey or baseUrl. Skipping popups fetch.");
|
|
2804
|
+
return [];
|
|
2805
|
+
}
|
|
2806
|
+
const filter = userId ? { where: { userId } } : null;
|
|
2807
|
+
const query = filter ? `?filter=${encodeURIComponent(JSON.stringify(filter))}` : "";
|
|
2808
|
+
const endpoint = `${baseUrl}/sdk/${encodeURIComponent(apiKey)}/popups${query}`;
|
|
2809
|
+
try {
|
|
2810
|
+
const response = await fetch(endpoint);
|
|
2811
|
+
if (!response.ok) {
|
|
2812
|
+
this.log("Failed to fetch popups", response.status, response.statusText);
|
|
2813
|
+
return [];
|
|
2814
|
+
}
|
|
2815
|
+
const raw = await response.text();
|
|
2816
|
+
const parsed = JSON.parse(raw);
|
|
2817
|
+
if (!Array.isArray(parsed)) {
|
|
2818
|
+
this.log("Unexpected popups payload", parsed);
|
|
2819
|
+
return [];
|
|
2820
|
+
}
|
|
2821
|
+
this.debug("Fetched popups payload", parsed);
|
|
2822
|
+
return parsed;
|
|
2823
|
+
} catch (error) {
|
|
2824
|
+
this.log("Error fetching popups", error);
|
|
2825
|
+
return [];
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
/** Envía los atributos de contact a la API (`POST /sdk/popups/contact`). */
|
|
2829
|
+
async postContact(body) {
|
|
2830
|
+
const baseUrl = this.baseUrl;
|
|
2831
|
+
if (!baseUrl) return;
|
|
2832
|
+
const endpoint = `${baseUrl}/sdk/popups/contact`;
|
|
2833
|
+
try {
|
|
2834
|
+
const response = await fetch(endpoint, {
|
|
2835
|
+
method: "POST",
|
|
2836
|
+
headers: { "Content-Type": "application/json" },
|
|
2837
|
+
body: JSON.stringify(body)
|
|
2838
|
+
});
|
|
2839
|
+
if (!response.ok) {
|
|
2840
|
+
this.log("Failed to post contact", response.status, response.statusText);
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
this.log("contact \xB7 sent attributes for user:", body.userId);
|
|
2844
|
+
} catch (error) {
|
|
2845
|
+
this.log("Error posting contact", error);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
/** Notifica eventos de popup a la API */
|
|
2849
|
+
async postPopupEvent(status, popupId, userId) {
|
|
2850
|
+
const apiKey = this.config?.apiKey;
|
|
2851
|
+
const baseUrl = this.baseUrl;
|
|
2852
|
+
if (!apiKey || !baseUrl) {
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
const endpoint = `${baseUrl}/sdk/popups`;
|
|
2856
|
+
try {
|
|
2857
|
+
const response = await fetch(endpoint, {
|
|
2858
|
+
method: "POST",
|
|
2859
|
+
headers: { "Content-Type": "application/json" },
|
|
2860
|
+
body: JSON.stringify({
|
|
2861
|
+
publicKey: apiKey,
|
|
2862
|
+
status,
|
|
2863
|
+
popupId,
|
|
2864
|
+
userId: userId || void 0
|
|
2865
|
+
})
|
|
2866
|
+
});
|
|
2867
|
+
if (!response.ok) {
|
|
2868
|
+
this.log("Failed to post popup event", response.status, response.statusText);
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
try {
|
|
2872
|
+
const data = await response.json();
|
|
2873
|
+
if (data && typeof data.sessionId === "string") {
|
|
2874
|
+
this.tracking?.setSessionId(data.sessionId);
|
|
2875
|
+
this.log("tracking \xB7 session_id from backend:", data.sessionId);
|
|
2876
|
+
}
|
|
2877
|
+
} catch {
|
|
2878
|
+
}
|
|
2879
|
+
} catch (error) {
|
|
2880
|
+
this.log("Error posting popup event", error);
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
/** Add an event listener */
|
|
2884
|
+
on(eventType, listener) {
|
|
2885
|
+
if (!this.listeners.has(eventType)) {
|
|
2886
|
+
this.listeners.set(eventType, /* @__PURE__ */ new Set());
|
|
2887
|
+
}
|
|
2888
|
+
this.listeners.get(eventType).add(listener);
|
|
2889
|
+
}
|
|
2890
|
+
/** Remove an event listener */
|
|
2891
|
+
off(eventType, listener) {
|
|
2892
|
+
const listeners = this.listeners.get(eventType);
|
|
2893
|
+
if (listeners) {
|
|
2894
|
+
listeners.delete(listener);
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
/** Setup popup container element */
|
|
2898
|
+
setupPopupContainer() {
|
|
2899
|
+
if (typeof document === "undefined") {
|
|
2900
|
+
return;
|
|
2901
|
+
}
|
|
2902
|
+
this.popupContainer = document.createElement("div");
|
|
2903
|
+
this.popupContainer.id = "deepdots-popup-container";
|
|
2904
|
+
this.popupContainer.style.cssText = `
|
|
2905
|
+
position: fixed;
|
|
2906
|
+
top: 0;
|
|
2907
|
+
left: 0;
|
|
2908
|
+
width: 100%;
|
|
2909
|
+
height: 100%;
|
|
2910
|
+
display: none;
|
|
2911
|
+
z-index: 999999;
|
|
2912
|
+
background: rgba(0, 0, 0, 0.5);
|
|
2913
|
+
justify-content: center;
|
|
2914
|
+
align-items: center;
|
|
2915
|
+
`;
|
|
2916
|
+
document.body.appendChild(this.popupContainer);
|
|
2917
|
+
}
|
|
2918
|
+
/** Render the popup UI */
|
|
2919
|
+
renderPopup(surveyId, productId, actions, style) {
|
|
2920
|
+
const userId = this.tracking?.getUserId() ?? this.config?.userId;
|
|
2921
|
+
const sessionId = this.tracking?.getSessionId() ?? void 0;
|
|
2922
|
+
const miniService = this.analytics?.getMiniService() ?? void 0;
|
|
2923
|
+
this.log("tracking \xB7 survey identity \u2192", { userId: userId ?? null, sessionId: sessionId ?? null, miniService: miniService ?? null });
|
|
2924
|
+
if (this.renderer) {
|
|
2925
|
+
this.renderer.show(
|
|
2926
|
+
surveyId,
|
|
2927
|
+
productId,
|
|
2928
|
+
actions,
|
|
2929
|
+
(type, id, payload) => this.emitEvent(type, id, payload),
|
|
2930
|
+
() => this.hidePopup(),
|
|
2931
|
+
this.env,
|
|
2932
|
+
userId,
|
|
2933
|
+
style,
|
|
2934
|
+
sessionId,
|
|
2935
|
+
miniService
|
|
2936
|
+
);
|
|
2937
|
+
return;
|
|
2938
|
+
}
|
|
2939
|
+
if (!this.popupContainer) return;
|
|
2940
|
+
const container = this.popupContainer;
|
|
2941
|
+
void Promise.resolve().then(() => (init_renderPopup(), renderPopup_exports)).then(({ renderPopup: renderPopup2 }) => {
|
|
2942
|
+
renderPopup2(
|
|
2943
|
+
container,
|
|
2944
|
+
surveyId,
|
|
2945
|
+
productId,
|
|
2946
|
+
actions,
|
|
2947
|
+
(type, id, payload) => this.emitEvent(type, id, payload),
|
|
2948
|
+
() => this.hidePopup(),
|
|
2949
|
+
this.env,
|
|
2950
|
+
userId,
|
|
2951
|
+
style,
|
|
2952
|
+
sessionId,
|
|
2953
|
+
miniService
|
|
2954
|
+
);
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
/** Hide the popup */
|
|
2958
|
+
hidePopup() {
|
|
2959
|
+
if (this.renderer) {
|
|
2960
|
+
this.renderer.hide();
|
|
2961
|
+
return;
|
|
2962
|
+
}
|
|
2963
|
+
if (this.popupContainer) {
|
|
2964
|
+
this.popupContainer.style.display = "none";
|
|
2965
|
+
this.popupContainer.innerHTML = "";
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
/** Emit an event */
|
|
2969
|
+
emitEvent(type, surveyId, data) {
|
|
2970
|
+
const event = { type, surveyId, timestamp: Date.now(), data };
|
|
2971
|
+
this.log("Event emitted", event);
|
|
2972
|
+
const isPartialClick = type === "popup_clicked" && data?.action === "partial";
|
|
2973
|
+
if (isPartialClick) {
|
|
2974
|
+
this.markSurveyProgress(surveyId, "PARTIAL");
|
|
2975
|
+
}
|
|
2976
|
+
if (type === "survey_completed") {
|
|
2977
|
+
this.markSurveyAnswered(surveyId);
|
|
2978
|
+
}
|
|
2979
|
+
if (type === "popup_shown" || type === "survey_completed" || isPartialClick) {
|
|
2980
|
+
const popupIdFromData = data?.popupId;
|
|
2981
|
+
const popupId = popupIdFromData || this.surveyToPopupId.get(surveyId);
|
|
2982
|
+
if (popupId) {
|
|
2983
|
+
const userIdFromData = data?.userId;
|
|
2984
|
+
const status = type === "popup_shown" ? "SHOWED" /* SHOWED */ : type === "survey_completed" ? "COMPLETED" /* COMPLETED */ : "PARTIAL" /* PARTIAL */;
|
|
2985
|
+
void this.postPopupEvent(status, popupId, userIdFromData || this.tracking?.getUserId() || this.config?.userId);
|
|
2986
|
+
} else {
|
|
2987
|
+
this.debug("No popupId available to post event", { type, surveyId });
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
const listeners = this.listeners.get(type);
|
|
2991
|
+
if (listeners) {
|
|
2992
|
+
listeners.forEach((listener) => {
|
|
2993
|
+
try {
|
|
2994
|
+
listener(event);
|
|
2995
|
+
} catch (error) {
|
|
2996
|
+
console.error("Error in event listener:", error);
|
|
2997
|
+
}
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
/** Log debug messages */
|
|
3002
|
+
log(...args) {
|
|
3003
|
+
if (this.config?.debug) {
|
|
3004
|
+
console.log("[DeepdotsPopups]", ...args);
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
/** External debug method for triggers */
|
|
3008
|
+
debug(...args) {
|
|
3009
|
+
this.log(...args);
|
|
3010
|
+
}
|
|
3011
|
+
/** Set a custom renderer */
|
|
3012
|
+
setRenderer(renderer) {
|
|
3013
|
+
this.renderer = renderer;
|
|
3014
|
+
if (this.initialized && this.renderer.init) {
|
|
3015
|
+
this.renderer.init();
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
normalizeUrl(value) {
|
|
3019
|
+
if (!value) return "";
|
|
3020
|
+
const withoutIndex = value.replace(/\/index\.html(?=($|[?#]))/i, "");
|
|
3021
|
+
return withoutIndex.length > 1 && withoutIndex.endsWith("/") ? withoutIndex.slice(0, -1) : withoutIndex;
|
|
3022
|
+
}
|
|
3023
|
+
safeParseUrl(url) {
|
|
3024
|
+
if (!url) return null;
|
|
3025
|
+
try {
|
|
3026
|
+
return new URL(url, typeof window !== "undefined" ? window.location.href : void 0);
|
|
3027
|
+
} catch {
|
|
3028
|
+
return null;
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
mapPopupTriggerType(type) {
|
|
3032
|
+
switch (type) {
|
|
3033
|
+
case "time_on_page":
|
|
3034
|
+
return "time";
|
|
3035
|
+
case "scroll":
|
|
3036
|
+
return "scroll";
|
|
3037
|
+
case "exit":
|
|
3038
|
+
return "exit";
|
|
3039
|
+
case "click":
|
|
3040
|
+
return "click";
|
|
3041
|
+
case "event":
|
|
3042
|
+
return "event";
|
|
3043
|
+
default:
|
|
3044
|
+
return null;
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
markSurveyProgress(surveyId, status) {
|
|
3048
|
+
const current = this.surveyProgress.get(surveyId);
|
|
3049
|
+
if (current?.status === "COMPLETED" && status !== "COMPLETED") {
|
|
3050
|
+
return;
|
|
3051
|
+
}
|
|
3052
|
+
this.surveyProgress.set(surveyId, { status, timestamp: Date.now() });
|
|
3053
|
+
}
|
|
3054
|
+
isPopupDefinition(value) {
|
|
3055
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3056
|
+
const popup = value;
|
|
3057
|
+
return typeof popup.id === "string" && typeof popup.title === "string" && typeof popup.message === "string" && typeof popup.surveyId === "string" && typeof popup.productId === "string" && Array.isArray(popup.triggers) && popup.triggers.length > 0 && popup.triggers.every((trigger) => this.isPopupTrigger(trigger)) && (!popup.cooldown || popup.cooldown.every((condition) => this.isPopupTriggerCondition(condition)));
|
|
3058
|
+
}
|
|
3059
|
+
isPopupTrigger(value) {
|
|
3060
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3061
|
+
const trigger = value;
|
|
3062
|
+
return this.isPopupTriggerType(trigger.type) && (typeof trigger.value === "number" || typeof trigger.value === "string");
|
|
3063
|
+
}
|
|
3064
|
+
isPopupTriggerType(value) {
|
|
3065
|
+
return value === "time_on_page" || value === "scroll" || value === "exit" || value === "click" || value === "event";
|
|
3066
|
+
}
|
|
3067
|
+
isPopupTriggerCondition(value) {
|
|
3068
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3069
|
+
const condition = value;
|
|
3070
|
+
return POPUP_TRIGGER_CONDITION_STATUSES.includes(condition.answered) && typeof condition.cooldownDays === "number" && Number.isFinite(condition.cooldownDays) && condition.cooldownDays >= 0;
|
|
3071
|
+
}
|
|
3072
|
+
isLegacyPopupTriggerCondition(value) {
|
|
3073
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3074
|
+
const condition = value;
|
|
3075
|
+
if (condition.answered !== void 0 && typeof condition.answered !== "boolean") {
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
3078
|
+
if (condition.cooldownDays !== void 0) {
|
|
3079
|
+
return typeof condition.cooldownDays === "number" && Number.isFinite(condition.cooldownDays) && condition.cooldownDays >= 0;
|
|
3080
|
+
}
|
|
3081
|
+
return true;
|
|
3082
|
+
}
|
|
3083
|
+
normalizeTriggers(value) {
|
|
3084
|
+
const items = Array.isArray(value) ? value : value ? [value] : [];
|
|
3085
|
+
return items.filter((trigger) => this.isPopupTrigger(trigger)).map((trigger) => ({
|
|
3086
|
+
type: trigger.type,
|
|
3087
|
+
value: trigger.value
|
|
3088
|
+
}));
|
|
3089
|
+
}
|
|
3090
|
+
normalizeCooldown(value) {
|
|
3091
|
+
if (!Array.isArray(value)) return [];
|
|
3092
|
+
return value.filter((condition) => this.isPopupTriggerCondition(condition));
|
|
3093
|
+
}
|
|
3094
|
+
normalizeLegacyConditions(value) {
|
|
3095
|
+
if (!Array.isArray(value)) return [];
|
|
3096
|
+
return value.filter((condition) => this.isLegacyPopupTriggerCondition(condition));
|
|
3097
|
+
}
|
|
3098
|
+
normalizePopupDefinition(def) {
|
|
3099
|
+
if (typeof def !== "object" || def === null) return null;
|
|
3100
|
+
const raw = def;
|
|
3101
|
+
const rawTriggers = raw.triggers ?? raw.trigger;
|
|
3102
|
+
const triggers = this.normalizeTriggers(rawTriggers);
|
|
3103
|
+
if (!triggers.length) return null;
|
|
3104
|
+
const rawTriggerList = Array.isArray(rawTriggers) ? rawTriggers : rawTriggers ? [rawTriggers] : [];
|
|
3105
|
+
const legacyConditions = [
|
|
3106
|
+
...this.normalizeLegacyConditions(raw.conditions),
|
|
3107
|
+
...rawTriggerList.flatMap((trigger) => this.normalizeLegacyConditions(trigger.condition))
|
|
3108
|
+
];
|
|
3109
|
+
const cooldown = this.normalizeCooldown(raw.cooldown);
|
|
3110
|
+
return {
|
|
3111
|
+
...raw,
|
|
3112
|
+
triggers,
|
|
3113
|
+
cooldown,
|
|
3114
|
+
legacyConditions
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
validatePopupDefinitions(defs) {
|
|
3118
|
+
return defs.map((def) => this.normalizePopupDefinition(def)).filter((def) => !!def && this.isPopupDefinition(def));
|
|
3119
|
+
}
|
|
3120
|
+
};
|
|
3121
|
+
|
|
3122
|
+
// src/react-native/setup.ts
|
|
3123
|
+
function mmkvStorage(mmkv) {
|
|
3124
|
+
return {
|
|
3125
|
+
getItem: (k) => mmkv.getString(k) ?? null,
|
|
3126
|
+
setItem: (k, v) => mmkv.set(k, v),
|
|
3127
|
+
removeItem: (k) => mmkv.delete(k)
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
function collectRnDevice(d) {
|
|
3131
|
+
return {
|
|
3132
|
+
device_type: d.isTablet?.() ? "tablet" : "mobile",
|
|
3133
|
+
os_version: d.getSystemVersion?.(),
|
|
3134
|
+
device_model: d.getDeviceId?.(),
|
|
3135
|
+
app_version: d.getVersion?.()
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
function setupReactNative(sdk2, config, deps = {}) {
|
|
3139
|
+
if (deps.renderer) sdk2.setRenderer(deps.renderer);
|
|
3140
|
+
const storage = deps.mmkv ? mmkvStorage(deps.mmkv) : config.storage;
|
|
3141
|
+
const device = deps.deviceInfo ? collectRnDevice(deps.deviceInfo) : config.device;
|
|
3142
|
+
const platform = deps.platform ?? config.platform;
|
|
3143
|
+
sdk2.init({ ...config, storage, device, platform });
|
|
3144
|
+
let subscription;
|
|
3145
|
+
if (deps.appState?.addEventListener) {
|
|
3146
|
+
subscription = deps.appState.addEventListener("change", (state) => {
|
|
3147
|
+
if (state === "active") sdk2.onForeground();
|
|
3148
|
+
else sdk2.onBackground();
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
return () => {
|
|
3152
|
+
if (subscription && typeof subscription.remove === "function") subscription.remove();
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
// src/react-native/index.tsx
|
|
3157
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
3158
|
+
var sdk = new DeepdotsPopups();
|
|
3159
|
+
var DeepdotsContext = (0, import_react.createContext)(sdk);
|
|
3160
|
+
var useDeepdots = () => (0, import_react.useContext)(DeepdotsContext);
|
|
3161
|
+
function optionalRequire(name) {
|
|
3162
|
+
try {
|
|
3163
|
+
return require(name);
|
|
3164
|
+
} catch {
|
|
3165
|
+
return null;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
function DeepdotsProvider({ config, children }) {
|
|
3169
|
+
const [survey, setSurvey] = (0, import_react.useState)(null);
|
|
3170
|
+
const rendererRef = (0, import_react.useRef)(null);
|
|
3171
|
+
(0, import_react.useEffect)(() => {
|
|
3172
|
+
const renderer = new ReactNativePopupRenderer({
|
|
3173
|
+
onShow: (p) => setSurvey(p),
|
|
3174
|
+
onHide: () => setSurvey(null)
|
|
3175
|
+
});
|
|
3176
|
+
rendererRef.current = renderer;
|
|
3177
|
+
const MmkvMod = optionalRequire("react-native-mmkv");
|
|
3178
|
+
const mmkv = MmkvMod?.MMKV ? new MmkvMod.MMKV({ id: "deepdots-sdk" }) : null;
|
|
3179
|
+
const deviceInfo = optionalRequire("react-native-device-info")?.default ?? null;
|
|
3180
|
+
const dispose = setupReactNative(sdk, config, {
|
|
3181
|
+
mmkv,
|
|
3182
|
+
deviceInfo,
|
|
3183
|
+
appState: import_react_native.AppState,
|
|
3184
|
+
platform: import_react_native.Platform.OS === "ios" ? "ios" : "android",
|
|
3185
|
+
renderer
|
|
3186
|
+
});
|
|
3187
|
+
return dispose;
|
|
3188
|
+
}, []);
|
|
3189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DeepdotsContext.Provider, { value: sdk, children: [
|
|
3190
|
+
children,
|
|
3191
|
+
survey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Modal, { visible: true, transparent: true, animationType: "slide", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3192
|
+
import_react_native_webview.WebView,
|
|
3193
|
+
{
|
|
3194
|
+
originWhitelist: ["*"],
|
|
3195
|
+
javaScriptEnabled: true,
|
|
3196
|
+
source: { html: survey.html },
|
|
3197
|
+
onMessage: (e) => rendererRef.current?.handleMessage(e.nativeEvent.data)
|
|
3198
|
+
}
|
|
3199
|
+
) }) }) : null
|
|
3200
|
+
] });
|
|
3201
|
+
}
|
|
3202
|
+
//# sourceMappingURL=react-native.js.map
|