@subiz/widget-v4 4.1.213 → 4.1.214
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/api.md +57 -0
- package/compiled_src/lib.js +52 -30
- package/compiled_src/store/api.js +3 -0
- package/dist/.HASH +1 -0
- package/dist/{1a1101c351b.app.js → 1a12a372880.app.js} +1 -1
- package/dist/{1a1101c351b.chat.html → 1a12a372880.chat.html} +1 -1
- package/dist/{1a1101c351b.chat.js → 1a12a372880.chat.js} +1 -1
- package/dist/{1a1101c351b.form.html → 1a12a372880.form.html} +1 -1
- package/dist/1a12a372880.form.js +1 -0
- package/dist/{1a1101c351b.index.html → 1a12a372880.index.html} +1 -1
- package/dist/{1a1101c351b.library.html → 1a12a372880.library.html} +1 -1
- package/dist/{1a1101c351b.library.js → 1a12a372880.library.js} +1 -1
- package/dist/{1a1101c351b.preview.html → 1a12a372880.preview.html} +1 -1
- package/dist/{1a1101c351b.preview.js → 1a12a372880.preview.js} +1 -1
- package/dist/{1a1101c351b.webview.js → 1a12a372880.webview.js} +1 -1
- package/dist/screenshoot.js +54 -29
- package/package.json +1 -1
- package/src/lib.js +54 -24
- package/src/store/api.js +9 -0
- package/dist/1a1101c351b.form.js +0 -1
- package/dist/HASH +0 -1
- /package/dist/{1a1101c351b.127.js → 1a12a372880.127.js} +0 -0
- /package/dist/{1a1101c351b.132.js → 1a12a372880.132.js} +0 -0
- /package/dist/{1a1101c351b.185.js → 1a12a372880.185.js} +0 -0
- /package/dist/{1a1101c351b.27.js → 1a12a372880.27.js} +0 -0
- /package/dist/{1a1101c351b.278.js → 1a12a372880.278.js} +0 -0
- /package/dist/{1a1101c351b.339.js → 1a12a372880.339.js} +0 -0
- /package/dist/{1a1101c351b.459.js → 1a12a372880.459.js} +0 -0
- /package/dist/{1a1101c351b.490.js → 1a12a372880.490.js} +0 -0
package/api.md
CHANGED
|
@@ -1222,3 +1222,60 @@ subiz('event', 'product_viewed', {
|
|
|
1222
1222
|
name: 'Bàn phím cơ không dây',
|
|
1223
1223
|
})
|
|
1224
1224
|
```
|
|
1225
|
+
|
|
1226
|
+
## Form website: tạo hội thoại và kiểm tra lần gửi trước
|
|
1227
|
+
|
|
1228
|
+
```js
|
|
1229
|
+
subiz('waitReady', async function () {
|
|
1230
|
+
const result = await subiz('submitForm', 'website_contact', [
|
|
1231
|
+
{key: 'fullname', label: 'Họ tên', value: 'Nguyễn Văn An'},
|
|
1232
|
+
{key: 'phones', value: '0901234567'},
|
|
1233
|
+
], {
|
|
1234
|
+
name: 'Đăng ký tư vấn',
|
|
1235
|
+
// page_url: 'https://example.com/contact',
|
|
1236
|
+
// submission_id: 'registration-123',
|
|
1237
|
+
})
|
|
1238
|
+
// result: {conversation_id, submission_id}
|
|
1239
|
+
})
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
Chữ ký: `submitForm(formId, fields, options?)`. Tham số thứ hai luôn là mảng.
|
|
1243
|
+
`label` mặc định bằng `key`. Giá trị string, number, boolean được gửi dưới dạng
|
|
1244
|
+
chuỗi; null/undefined thành chuỗi rỗng. `options.name` mặc định bằng formId,
|
|
1245
|
+
`page_url` bằng URL hiện tại, `submission_id` được SDK tự sinh.
|
|
1246
|
+
|
|
1247
|
+
SDK chờ phiên khách sẵn sàng và dùng xác thực hiện có. API lỗi hoặc phản hồi sai
|
|
1248
|
+
định dạng sẽ reject Promise, lỗi có `code` và `status`. Không tự retry submit:
|
|
1249
|
+
`submission_id` chưa chống tạo trùng. Website nên khóa nút trong khi gửi.
|
|
1250
|
+
Khi SDK chưa tải, loader chỉ xếp hàng lệnh và chưa trả Promise; dùng `waitReady`
|
|
1251
|
+
như ví dụ để gọi `await` an toàn.
|
|
1252
|
+
|
|
1253
|
+
ID bắt đầu bằng `cp` vẫn dùng luồng popup/campaign cũ, giữ nguyên kiểu kết quả cũ;
|
|
1254
|
+
options mới không áp dụng cho nhánh này. ID website khác `cp` chuyển sang API form
|
|
1255
|
+
mới: tạo hội thoại kênh form, không tự cập nhật thuộc tính hồ sơ khách. Backend mới
|
|
1256
|
+
phải được deploy trước khi phát hành SDK.
|
|
1257
|
+
|
|
1258
|
+
Giới hạn: ID form và key dài 1–64 ký tự ASCII chữ/số/`_`/`-`, bắt đầu bằng chữ/số;
|
|
1259
|
+
1–50 trường; label/tên form tối đa 200 byte, giá trị 4096 byte, URL HTTP(S) 500 byte,
|
|
1260
|
+
toàn bộ JSON 32 KiB. Ít nhất một giá trị không rỗng. Kiểm tra bắt buộc/email theo
|
|
1261
|
+
nghiệp vụ của form do website thực hiện.
|
|
1262
|
+
|
|
1263
|
+
```js
|
|
1264
|
+
subiz('waitReady', async function () {
|
|
1265
|
+
try {
|
|
1266
|
+
const result = await subiz('getFormStatus', 'website_contact')
|
|
1267
|
+
if (result.submitted) document.getElementById('contact-form').hidden = true
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
// Không ẩn form khi kiểm tra thất bại.
|
|
1270
|
+
}
|
|
1271
|
+
})
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1274
|
+
`getFormStatus(formId)` trả `{submitted, status, conversation_id?}`, chỉ hỗ trợ ID
|
|
1275
|
+
website không bắt đầu bằng `cp`. Cả hội thoại đã kết thúc cũng được tính.
|
|
1276
|
+
`status: 'found'` nghĩa là tìm thấy lần gửi; `'not_found_in_available_history'`
|
|
1277
|
+
không khẳng định khách chưa từng gửi. Service hiện chỉ đọc tối đa 200 dòng lịch sử
|
|
1278
|
+
khách trước khi lọc theo form, sau đó trả tối đa 6 hội thoại; cache và việc xóa dữ
|
|
1279
|
+
liệu cũng ảnh hưởng kết quả. Lần gửi theo luồng cũ dùng hostname làm nguồn hoặc
|
|
1280
|
+
CF7 ticket chưa được tra cứu bằng API này. Khách cần được nhận diện lại bằng cùng
|
|
1281
|
+
phiên widget; không đảm bảo khi xóa dữ liệu trình duyệt hoặc đổi thiết bị.
|
package/compiled_src/lib.js
CHANGED
|
@@ -199,8 +199,9 @@ class Widget {
|
|
|
199
199
|
this.impressionTokens[formid] = impressiontoken;
|
|
200
200
|
return impressiontoken;
|
|
201
201
|
}
|
|
202
|
-
async submitForm(formid, values) {
|
|
203
|
-
if (!formid || !values)
|
|
202
|
+
async submitForm(formid, values, options = {}) {
|
|
203
|
+
if (!formid || !values) throw new TypeError('submitForm requires a form ID and fields');
|
|
204
|
+
await this.store.readyGate.entry();
|
|
204
205
|
if (_index.default.startsWith(formid, 'cp')) {
|
|
205
206
|
let storeuser = _index.default.cloneDeep(this.store.matchUser());
|
|
206
207
|
let user = {
|
|
@@ -231,33 +232,13 @@ class Widget {
|
|
|
231
232
|
this.store.setUserDiff(user);
|
|
232
233
|
return this.store.sendConversion(formid, values, impressiontoken);
|
|
233
234
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return this.store.startConvo({
|
|
242
|
-
subject: 'Khách điền form',
|
|
243
|
-
touchpoint: {
|
|
244
|
-
channel: 'form',
|
|
245
|
-
source: window.location.hostname,
|
|
246
|
-
id: Date.now() + '_' + _index.default.randomString()
|
|
247
|
-
},
|
|
248
|
-
form_submission: {
|
|
249
|
-
id: formid,
|
|
250
|
-
values: values
|
|
251
|
-
},
|
|
252
|
-
message_sent: {
|
|
253
|
-
data: {
|
|
254
|
-
message: {
|
|
255
|
-
format: 'plaintext',
|
|
256
|
-
text: text
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
});
|
|
235
|
+
const payload = formRequest(formid, values, options);
|
|
236
|
+
return formResult(await this.store.api.submitWebsiteForm(formid, payload), 'submit');
|
|
237
|
+
}
|
|
238
|
+
async getFormStatus(formid) {
|
|
239
|
+
validateWebsiteFormId(formid);
|
|
240
|
+
await this.store.readyGate.entry();
|
|
241
|
+
return formResult(await this.store.api.getWebsiteFormStatus(formid), 'status');
|
|
261
242
|
}
|
|
262
243
|
startConvo(req) {
|
|
263
244
|
req = _index.default.cloneDeep(req) || {};
|
|
@@ -369,4 +350,45 @@ class Widget {
|
|
|
369
350
|
}
|
|
370
351
|
}
|
|
371
352
|
window.__sbz_WIDGET = Widget;
|
|
372
|
-
var _default = exports.default = Widget;
|
|
353
|
+
var _default = exports.default = Widget;
|
|
354
|
+
function validateWebsiteFormId(formid) {
|
|
355
|
+
if (typeof formid !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(formid)) {
|
|
356
|
+
throw new TypeError('Form ID must contain 1-64 letters, digits, underscores or hyphens');
|
|
357
|
+
}
|
|
358
|
+
if (formid.startsWith('cp')) throw new TypeError('Form IDs starting with cp are reserved for Subiz campaigns');
|
|
359
|
+
}
|
|
360
|
+
function formRequest(formid, fields, options = {}) {
|
|
361
|
+
validateWebsiteFormId(formid);
|
|
362
|
+
if (!Array.isArray(fields) || !fields.length) throw new TypeError('Form fields must be a nonempty array');
|
|
363
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Form options must be an object');
|
|
364
|
+
return {
|
|
365
|
+
form_name: options.name === undefined ? formid : options.name,
|
|
366
|
+
page_url: options.page_url === undefined ? window.location.href : options.page_url,
|
|
367
|
+
submission_id: options.submission_id === undefined ? _index.default.randomString(36) : options.submission_id,
|
|
368
|
+
values: fields.map(field => {
|
|
369
|
+
if (!field || typeof field.key !== 'string') throw new TypeError('Each field requires a key');
|
|
370
|
+
const value = field.value;
|
|
371
|
+
if (value != null && !['string', 'number', 'boolean'].includes(typeof value)) {
|
|
372
|
+
throw new TypeError('Form field values must be strings, numbers or booleans');
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
key: field.key,
|
|
376
|
+
label: field.label === undefined ? field.key : field.label,
|
|
377
|
+
value: value == null ? '' : String(value)
|
|
378
|
+
};
|
|
379
|
+
})
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function formResult(response, operation) {
|
|
383
|
+
const body = response && response.body;
|
|
384
|
+
const detail = response && response.error || body && body.error;
|
|
385
|
+
const status = response && response.code;
|
|
386
|
+
const valid = operation === 'submit' ? body && typeof body.conversation_id === 'string' && body.conversation_id && typeof body.submission_id === 'string' && body.submission_id : body && typeof body.submitted === 'boolean' && typeof body.status === 'string';
|
|
387
|
+
if (detail || !(status >= 200 && status < 300) || !valid) {
|
|
388
|
+
const error = new Error('Subiz form request failed');
|
|
389
|
+
error.code = detail && detail.code || 'form_request_failed';
|
|
390
|
+
error.status = status;
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
return body;
|
|
394
|
+
}
|
|
@@ -63,6 +63,9 @@ class _default {
|
|
|
63
63
|
_defineProperty(this, "getConversation", cid => this.apireq.get(`/accounts/${this.accid}/conversations/${cid}`));
|
|
64
64
|
_defineProperty(this, "getLocaleContent", locale => this.apireq.get(`/accounts/${this.accid}/languages/${locale}`));
|
|
65
65
|
_defineProperty(this, "sendEvent", data => this.apireq.addQuery('shorten', true).addQuery('x-referer', _index.default.get(location, 'origin', '')).post(`/accounts/${this.accid}/users/${this.uid}/events`, data));
|
|
66
|
+
_defineProperty(this, "websiteFormRequest", () => this.apireq.setBaseUrl(this.ApiURL.replace(/\/4\.[01]\/?$/, '/4.1')));
|
|
67
|
+
_defineProperty(this, "submitWebsiteForm", (formid, payload) => this.websiteFormRequest().post(`/accounts/${this.accid}/external-forms/${encodeURIComponent(formid)}/submissions`, payload));
|
|
68
|
+
_defineProperty(this, "getWebsiteFormStatus", formid => this.websiteFormRequest().get(`/accounts/${this.accid}/external-forms/${encodeURIComponent(formid)}/status`));
|
|
66
69
|
_defineProperty(this, "submitForm", (form_id, page_url, device, data, user) => this.apireq.addQuery('x-referer', _index.default.get(location, 'origin', '')).post(`/accounts/${this.accid}/users/${this.uid}/events`, {
|
|
67
70
|
by: {
|
|
68
71
|
id: this.uid,
|
package/dist/.HASH
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1a12a372880
|