Skip to content

Commit d4482e1

Browse files
authored
Forms Verification (#13)
* remove OTP verification step * remove unused import * remove controller from lib folder * update README * log failed responses * display API error messages * yarn:build * use input.setCustomValidity * show error messages * adjust [data-error-container] style * remove unused constant * update docs * add blank message to error codes * 1.7.2
1 parent 4fff886 commit d4482e1

File tree

18 files changed

+77
-413
lines changed

18 files changed

+77
-413
lines changed

dist/hellotext.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/forms.md

+17-4
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,28 @@ This will mount the form regardless if the user has completed the form or not.
7979

8080
Hellotext automatically validates the form inputs based on how they were configured on the dashboard
8181
using browser's native [checkValidity()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement/checkValidity).
82+
8283
Once the user tries to submit the form and there are missing required fields,
8384
the submission is halted and we display default browser's error message using [HTMLObjectElement.validationMessage](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validationMessage) property.
8485

85-
### Authentication
86+
If the form contains a unique property and the client enters a value that is already taken,
87+
the submission will not be complete. Instead, an error will be reported to the client and the form will not be submitted.
88+
89+
Uniqueness errors use the browser's native [setCustomValidity()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity)
90+
method which are reported as normal form validation errors, they can be styled with the `input:invalid` selector.
91+
92+
### Understanding Verification
8693

8794
Hellotext protects you from bot submissions and protects your customers from identity theft and impersonation.
88-
When a subscriber fills the form, Hellotext sends a One Time Password (OTP) code to the subscriber. If they have an email,
89-
an email is sent to them, otherwise an SMS is sent to their phone number.
90-
Until a valid OTP has been entered, data will not show up on the dashboard and will not contribute to attribution marketing.
95+
96+
When a subscriber fills the form with an email and/or phone number, Hellotext sends a verification link to the value the submission had at the time of creation.
97+
When the customer clicks the link in their email and/or the SMS they receive, the attribute is verified.
98+
Once the email/phone number were verified, the data is considered to be _verified_.
99+
100+
Once verification of a submission is complete, Hellotext performs an automatic merge (if needed) of all profiles
101+
that have the same email and/or phone number. After automatic merging, if a property has multiple values,
102+
it will be visible in the Audience page and the user can see the values that were merged and they can decide which one
103+
to ignore and which one to accept.
91104

92105
### Form Completion
93106

lib/builders/otp_builder.js

-39
This file was deleted.

lib/controllers/form_controller.js

+19-14
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ var _stimulus = require("@hotwired/stimulus");
88
var _hellotext = _interopRequireDefault(require("../hellotext"));
99
var _models = require("../models");
1010
var _forms = _interopRequireDefault(require("../api/forms"));
11-
var _otp_builder = require("../builders/otp_builder");
1211
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1312
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
1413
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
@@ -60,30 +59,35 @@ var _default = /*#__PURE__*/function (_Controller) {
6059
this.buttonTarget.disabled = true;
6160
var response = yield _forms.default.submit(this.form.id, this.formData);
6261
this.buttonTarget.disabled = false;
62+
var data = yield response.json();
6363
if (response.failed) {
64-
return;
64+
data.errors.forEach(error => {
65+
var {
66+
type,
67+
parameter
68+
} = error;
69+
var input = this.inputTargets.find(input => input.name === parameter);
70+
input.setCustomValidity(_hellotext.default.business.locale.errors[type]);
71+
input.reportValidity();
72+
input.addEventListener('input', () => {
73+
input.setCustomValidity('');
74+
input.reportValidity();
75+
});
76+
});
77+
return this.showErrorMessages();
6578
}
6679
this.buttonTarget.style.display = 'none';
6780
this.element.querySelectorAll('input').forEach(input => input.disabled = true);
68-
var submission = yield response.json();
69-
if (submission.identified) {
70-
this.completed();
71-
} else {
72-
_hellotext.default.setSession(submission.session);
73-
this.revealOTPContainer(submission.id);
81+
if (!data.identified) {
82+
_hellotext.default.setSession(data.session);
7483
}
84+
this.completed();
7585
});
7686
function submit(_x) {
7787
return _submit.apply(this, arguments);
7888
}
7989
return submit;
8090
}()
81-
}, {
82-
key: "revealOTPContainer",
83-
value: function revealOTPContainer(submissionId) {
84-
var paragraph = this.requiredInputs.find(input => input.name === 'email') ? _hellotext.default.business.locale.otp.sent_to_email : _hellotext.default.business.locale.otp.sent_to_phone;
85-
this.element.appendChild(_otp_builder.OTPBuilder.build(submissionId, paragraph));
86-
}
8791
}, {
8892
key: "completed",
8993
value: function completed() {
@@ -104,6 +108,7 @@ var _default = /*#__PURE__*/function (_Controller) {
104108
key: "clearErrorMessages",
105109
value: function clearErrorMessages() {
106110
this.element.querySelectorAll('input').forEach(input => {
111+
input.setCustomValidity('');
107112
var parent = input.closest('article');
108113
parent.querySelector('[data-error-container]').innerText = '';
109114
});

lib/controllers/otp_controller.js

-139
This file was deleted.

lib/index.js

-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ exports.default = void 0;
77
var _stimulus = require("@hotwired/stimulus");
88
var _hellotext = _interopRequireDefault(require("./hellotext"));
99
var _form_controller = _interopRequireDefault(require("./controllers/form_controller"));
10-
var _otp_controller = _interopRequireDefault(require("./controllers/otp_controller"));
1110
require("../styles/index.css");
1211
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1312
var application = _stimulus.Application.start();
1413
application.register('hellotext--form', _form_controller.default);
15-
application.register('hellotext--otp', _otp_controller.default);
1614
window.Hellotext = _hellotext.default;
1715
var _default = _hellotext.default;
1816
exports.default = _default;

lib/locales/en.js

+4-10
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,12 @@ Object.defineProperty(exports, "__esModule", {
55
});
66
exports.default = void 0;
77
var _default = {
8-
otp: {
9-
sent_to_email: 'A One-Time Password has been sent to your email address',
10-
sent_to_phone: 'An One-Time Password has been sent to your phone number',
11-
resend_successful: 'The One-Time Password has been resent successfully',
12-
invalid: 'Invalid One-Time Password',
13-
resend: 'Resend verification code',
14-
submit: 'Submit',
15-
throttled: 'You have reached the maximum number of attempts. Please try again in 1 minute.',
16-
placeholder: 'Enter the code here'
17-
},
188
white_label: {
199
powered_by: 'Powered by'
10+
},
11+
errors: {
12+
parameter_not_unique: 'This value is taken.',
13+
blank: 'This field is required.'
2014
}
2115
};
2216
exports.default = _default;

0 commit comments

Comments
 (0)