Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Website Forms #8

Merged
merged 123 commits into from
Jun 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
640fb57
expose a Forms object that can collect forms on the page
rockwellll May 24, 2024
49e524c
update selector
rockwellll May 24, 2024
4713ebb
build
rockwellll May 24, 2024
33da3e4
return the forms object
rockwellll May 24, 2024
a8807b1
add forms as an attribute of Hellotext class
rockwellll May 24, 2024
1e64a09
make form a static property
rockwellll May 24, 2024
14d2052
update code
rockwellll May 24, 2024
14b5623
update api url to point to dev
rockwellll May 24, 2024
3f8c952
refactoring
rockwellll May 24, 2024
8bf4f2c
warn when the business has not enabled whitelist
rockwellll May 24, 2024
e25fac0
build
rockwellll May 24, 2024
242be60
update code
rockwellll May 24, 2024
9fad062
ad test for business model
rockwellll May 24, 2024
3b84521
add a Form model that can be built
rockwellll May 24, 2024
d1d807a
build output
rockwellll May 24, 2024
130b7d6
safely clone the node
rockwellll May 24, 2024
f19a273
set the data-attribute on the created element
rockwellll May 24, 2024
b911e1c
adjust code
rockwellll May 24, 2024
3f578c9
set the for attribute on the generated label for the input
rockwellll May 24, 2024
8f17e06
set the id attribute for the generated input
rockwellll May 24, 2024
9140020
install Stimulus to dependencies
rockwellll May 24, 2024
27204a2
add values and targets to the stimulus controller
rockwellll May 27, 2024
0c30672
update code
rockwellll May 27, 2024
1e79014
update code
rockwellll May 27, 2024
8b05c55
update code
rockwellll May 27, 2024
1c8122d
update code
rockwellll May 27, 2024
bd75a6d
update code
rockwellll May 27, 2024
c00dc52
update code
rockwellll May 27, 2024
031d2bb
show the validation message
rockwellll May 27, 2024
face114
use css to show and hide the error container
rockwellll May 27, 2024
3a6572a
update code
rockwellll May 27, 2024
aa0488c
automatically import styles
rockwellll May 27, 2024
3ae63d3
refactoring
rockwellll May 27, 2024
32d3b0d
update code
rockwellll May 27, 2024
67427c8
update code
rockwellll May 27, 2024
906c463
add forms API
rockwellll May 27, 2024
b7612dc
update code
rockwellll May 27, 2024
0d3024c
update endpoints
rockwellll May 27, 2024
5ad232e
add events API
rockwellll May 27, 2024
72be66d
move files into api namespace
rockwellll May 27, 2024
8ace7f7
refactoring
rockwellll May 27, 2024
e6977a4
update code
rockwellll May 27, 2024
7846bfe
set the form action from the form submission url
rockwellll Jun 19, 2024
b5d9c6b
yarn build
rockwellll Jun 19, 2024
fe8137f
only prevent the form submission when form is invalid
rockwellll Jun 19, 2024
922fea4
use FormData to attach headers to
rockwellll Jun 19, 2024
9082a28
update generated input names
rockwellll Jun 20, 2024
8cd29ed
log the response
rockwellll Jun 20, 2024
68328c3
update code
rockwellll Jun 20, 2024
c01d49d
WIP
rockwellll Jun 20, 2024
d97cfe3
WIP
rockwellll Jun 20, 2024
c7793ac
WIP
rockwellll Jun 20, 2024
bfd4384
update OTP container
rockwellll Jun 21, 2024
4f3583e
update code
rockwellll Jun 21, 2024
9ccee32
update code. CI SKIP
rockwellll Jun 21, 2024
0a02ad9
remove verify OTP button
rockwellll Jun 21, 2024
550ac75
add dedicated controller for OTP
rockwellll Jun 21, 2024
7897062
use html template literals
rockwellll Jun 21, 2024
2576144
add maxLength to otp input
rockwellll Jun 21, 2024
857f1ab
resend OTP once button is pressed
rockwellll Jun 21, 2024
44d20bb
WIP
rockwellll Jun 21, 2024
9570fad
WIP
rockwellll Jun 21, 2024
6978e47
verify the OTP
rockwellll Jun 21, 2024
2674eb5
update otp verification URL
rockwellll Jun 21, 2024
3342528
renable button and input
rockwellll Jun 21, 2024
2d5a3de
dispatch a verification event to the parent once otp is verified
rockwellll Jun 21, 2024
839bb77
WIP
rockwellll Jun 21, 2024
17f9cdc
WIP
rockwellll Jun 21, 2024
d31de36
WIP
rockwellll Jun 21, 2024
a2a1f1c
set the Hellotext.session once a submission is completed successfully
rockwellll Jun 21, 2024
f0ebfdc
add localized values based on business.locale
rockwellll Jun 21, 2024
828368e
update resend button label to be localized
rockwellll Jun 21, 2024
c08b070
send Hellotext.session to the server
rockwellll Jun 21, 2024
88d5b0f
autoload the phone input with a country prefix
rockwellll Jun 24, 2024
f5a857e
update code
rockwellll Jun 24, 2024
a4f53fc
update code
rockwellll Jun 24, 2024
6dc8864
WIP
rockwellll Jun 24, 2024
5b2b65a
WIP
rockwellll Jun 24, 2024
9ba649e
WIP
rockwellll Jun 24, 2024
851a809
WIP
rockwellll Jun 24, 2024
1b605d1
update code
rockwellll Jun 24, 2024
8e80090
update code
rockwellll Jun 24, 2024
8949d0e
getByIndex and getById to Forms
rockwellll Jun 24, 2024
cae4e28
focus the first input when form is loaded if no input is active in th…
rockwellll Jun 24, 2024
5ee6b39
rename Forms.js to FormCollection.js
rockwellll Jun 26, 2024
4970eb8
merge EventEmitter to Event
rockwellll Jun 26, 2024
757c304
rename Event.emit to Event.dispatch
rockwellll Jun 26, 2024
7420113
add a dedicated OTP element builder
rockwellll Jun 26, 2024
43871a7
rename build to mount
rockwellll Jun 26, 2024
09d30bf
update generated tag for form footer
rockwellll Jun 26, 2024
d20bcb5
refactoring
rockwellll Jun 26, 2024
b4ed507
refactoring
rockwellll Jun 26, 2024
b17d270
add throttling support for OTP sending
rockwellll Jun 26, 2024
9b9b4cf
update tests
rockwellll Jun 26, 2024
d82136b
update sessionsAPI
rockwellll Jun 26, 2024
a46d5ee
add new test config
rockwellll Jun 26, 2024
3b0e453
introduce src/core for core classes that dont belong to elsewhere
rockwellll Jun 26, 2024
2fd7e14
rely on Core configuration to generate API endpoints
rockwellll Jun 26, 2024
85a40a1
run prettier
rockwellll Jun 26, 2024
4c2ad94
WIP
rockwellll Jun 26, 2024
622ac5c
add default style for brand logo
rockwellll Jun 26, 2024
a8c808e
adjust paragraph to small
rockwellll Jun 26, 2024
98c48e5
pass the form data back to the subscriber
rockwellll Jun 26, 2024
c58bf49
update code
rockwellll Jun 26, 2024
6f4ec2d
update code
rockwellll Jun 26, 2024
a68bb2c
only show the Hellotext brand when the business package does not have…
rockwellll Jun 26, 2024
8efbfd1
correctly return features hash
rockwellll Jun 26, 2024
63ee888
WIP
rockwellll Jun 26, 2024
5830ed2
add forms docs
rockwellll Jun 26, 2024
01dc4ae
update main readme
rockwellll Jun 26, 2024
c673f38
update code to match API results
rockwellll Jun 28, 2024
e3f7d50
dont set the session once OTP is verified
rockwellll Jun 28, 2024
6652332
update code
rockwellll Jun 28, 2024
2d5f19b
remove useless class
rockwellll Jun 28, 2024
30ce476
reorganize errors
rockwellll Jun 28, 2024
3fe0398
update readme
rockwellll Jun 28, 2024
53ce613
WIP
rockwellll Jun 28, 2024
1eb12e1
update readme
rockwellll Jun 28, 2024
175bc2f
cleanup
rockwellll Jun 30, 2024
cf0e164
merge with master
rockwellll Jun 30, 2024
e017b6c
update forms docs
rockwellll Jun 30, 2024
baa838e
update node matrix
rockwellll Jun 30, 2024
2ff8a42
drop v20 from matrix
rockwellll Jun 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
]
],
"plugins": [
["@babel/plugin-proposal-private-methods", {
"loose": true
}],
[
"@babel/plugin-proposal-private-methods",
{
"loose": true
}
],
["@babel/plugin-proposal-private-property-in-object", { "loose": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
"@babel/plugin-transform-classes"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: ['16']
node: ['20', '22']
name: Node ${{ matrix.node }}
steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__tests__
dist
200 changes: 30 additions & 170 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Hellotext.js

Track the events happening on your site to [Hellotext](https://www.hellotext.com) in real-time with this library.
Official [Hellotext](https://www.hellotext.com) (client-side) JavaScript library.

## Installation
This library allows you the following,

- Track events happening on your site to [Hellotext](https://www.hellotext.com) in real-time.
- Use Hellotext Forms to dynamically collect data from your customers based on your specific business requirements.


## Installation

### Using NPM

Expand All @@ -21,173 +27,25 @@ yarn add @hellotext/hellotext
Import the library into your app.

```javascript
import Hellotext from "@hellotext/hellotext";
import Hellotext from '@hellotext/hellotext'
```

Initialize the library passing the public `HELLOTEXT_BUSINESS_ID` identifier that represents the business.

You can find it from the business's settings page.

```javascript
Hellotext.initialize("HELLOTEXT_BUSINESS_ID");
Hellotext.initialize('HELLOTEXT_BUSINESS_ID')
```

Failing to initialize the class before calling any other method will throw a `NotInitializedError`.

## Usage

Tracking events is straightforward and perhaps the simplest example is tracking a page view:

```javascript
Hellotext.track("page.viewed");
```

In the example above only the name of the action is required.

### Handling Responses

The `track` method returns a Promise that can be `await`ed using the async/await syntax. Or using `.then` on the returned Promise.

```javascript
const response = await Hellotext.track("page.viewed");
```

The return of the `Hellotext.track` method is an instance of a `Response` object that ships with the package. You can check the status of the response via methods, like:

```javascript
if(response.failed) {
console.log("failed because", response.data)
}

if(response.succeeded) {
console.log("success")
console.log(response.data) // { status: "received" }
}
```

### Parameters

The parameters passed to the action must be a valid set of parameters as described in
[Tracking Actions](https://www.hellotext.com/api#tracking).

#### URL Parameter

The library takes care of handling the `url` parameter with the current URL automatically and is not required to specify it explicitly.
If you want to provide another url, you can pass a `url` key in the params object when tracking an event.

```javascript
Hellotext.track("page.viewed", {
url: "www.example.org"
});
```

### Errors

Failing to provide valid set of parameters will result in an error object being returned, describing the parameters that did not satisfy the rules.

```javascript
const response = await Hellotext.track("app.installed", { app_parameters: { name: "My App" }})

console.log(response.data)
```

yields

```javascript
{
errors: [
{
type: 'parameter_not_unique',
parameter: 'name',
description: 'The value must be unique and it is already present in another object of the same type.'
},
]
}
```

For a complete list of errors types. See [Error Types](https://www.hellotext.com/api#errors)

### Associated objects

Generally, most actions also require an associated object. These can be of type [`app`](https://www.hellotext.com/api#apps), [`coupon`](https://www.hellotext.com/api#coupons), [`form`](https://www.hellotext.com/api#forms), [`order`](https://www.hellotext.com/api#orders), [`product`](https://www.hellotext.com/api#products) and [`refund`](https://www.hellotext.com/api#refunds).
Aside from [Custom Actions](https://www.hellotext.com/api#create_an_action), which don't require the trackable to be present.


You can create the associated object directly by defining its parameters in a hash:

```javascript
Hellotext.track("order.placed", {
amount: 395.00,
currency: "USD",
order_parameters: {
"amount": "395.00",
"reference": "654321",
}
});
```

If you want to reuse existing objects, you must pass the identifier of an existing associated object. For example, to track a product purchase the identifier of a previously created product object as the `product`.
For more information about identifiers, view the [Tracking API](https://www.hellotext.com/api#tracking)

```javascript
Hellotext.track("product.purchased", {
amount: 395.00,
currency: "USD",
product: "erA2RAXE"
});
```

## List of actions

The following is a complete list of built-in actions and their required associated objects.

| Action | Description | Required Parameter |
|-----------------------|----------------------------------------------------------| --- |
| **app.installed** | An app was installed. | `app` or [app_parameters](https://www.hellotext.com/api#app)
| **app.removed** | An app was removed. | `app` or [app_parameters](https://www.hellotext.com/api#app)
| **app.spent** | A customer spent on an app. | `app` or [app_parameters](https://www.hellotext.com/api#app)
| **cart.abandoned** | A cart was abandoned. | `product` or [product_parameters](https://www.hellotext.com/api#products)
| **cart.added** | Added an item to the cart. | `product` or [product_parameters](https://www.hellotext.com/api#products)
| **cart.removed** | Removed an item from the cart. | `product` or [product_parameters](https://www.hellotext.com/api#products)
| **coupon.redeemed** | A coupon was redeem by a customer. | `coupon` or [coupon_parameters](https://www.hellotext.com/api#coupons)
| **form.completed** | A form was completed by the customer. | `form` or [form_parameters](https://www.hellotext.com/api#forms)
| **order.placed** | Order has been placed. | `order` or [order_parameters](https://www.hellotext.com/api#orders)
| **order.confirmed** | Order has been confirmed by you. | `order` or [order_parameters](https://www.hellotext.com/api#orders)
| **order.cancelled** | Order has been cancelled either by you or your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders)
| **order.shipped** | Order has been shipped to your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders)
| **order.delivered** | Order has been delivered to your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders)
| **page.viewed** | A page was viewed by a customer. | `url`
| **product.purchased** | A product has been purchased. | `product` or [product_parameters](https://www.hellotext.com/api#products)
| **product.viewed** | A product page has been viewed. | `product` or [product_parameters](https://www.hellotext.com/api#products)
| **refund.requested** | A customer requested a refund. | `refund` or [refund_parameters](https://www.hellotext.com/api#refunds)
| **refund.received** | A refund was issued by you to your customer. | `refund` or [refund_parameters](https://www.hellotext.com/api#refunds)

You can also create your **[own defined actions](https://www.hellotext.com/api#actions)**.

## Additional Properties

You can include additional attributes to the tracked event, additional properties must be included inside the `metadata` object:

```javascript
Hellotext.track("product.purchased", {
amount: 0.20,
currency: "USD",
metadata: {
myProperty: "custom"
},
tracked_at: 1665684173
});
```

### List of additional attributes
## Documentation

| Property | Description | Type | Default |
| --- | --- | --- | --- |
| **amount** | Monetary amount that represents the revenue associated to this tracked event. | float | `0`
| **currency** | Currency for the `amount` given in ISO 4217 format. | currency | `USD`
| **metadata** | Set of key-value pairs that you can attach to an event. This can be useful for storing additional information about the object in a structured format. | hash | `{}`
| **tracked_at** | Original date when the event happened. This is useful if you want to record an event that happened in the past. If no value is provided its value will be the same from `created_at`. | epoch | `null`
Learn how to leverage the library to track events and collect forms.

- [Tracking Events](/docs/tracking.md)
- [Forms](/docs/forms.md)

## Events

Expand All @@ -209,54 +67,56 @@ Hellotext.removeEventListener(eventName, callback)
### List of events

- `session-set`: This event is fired when the session value for `Hellotext.session` is set. Either through an API request, or if the session was found in the cookie.
- `forms:collected` This event is fired when forms are collected. The callback will receive the array of forms collected.
- `form:completed` This event is fired when a form has been completed. A form is completed when the user fills all required inputs and verifies their OTP(One-Time Password). The callback will receive the form object that was completed, alongside the data the user filled in the form.

## Understanding Sessions

The library looks for a session identifier present on the `hellotext_session` query parameter. If the session is not present as a cookie neither it will create a new random session identifier, you can disable this default behaviour via the configuration, see [Configuration Options](#configuration-options) for more information.
The session is automatically sent to Hellotext any time the `Hellotext.track` method is called.
The library looks for a session identifier present on the `hellotext_session` query parameter. If the session is not present as a cookie neither it will create a new random session identifier, you can disable this default behaviour via the configuration, see [Configuration Options](#configuration-options) for more information.
The session is automatically sent to Hellotext any time the `Hellotext.track` method is called.

Short links redirections attaches a session identifier to the destination url as `hellotext_session` query parameter. This will identify all the events back to the customer who opened the link.

### Get session

It is possible to obtain the current session by simply calling `Hellotext.session`.
It is possible to obtain the current session by simply calling `Hellotext.session`.

```javascript
await Hellotext.session
// Returns bBJn9vR15yPaYkWmR2QK0jopMeNxrA6l
```

If the session has not been set yet, the result returned will be `undefined`.
If the session has not been set yet, the result returned will be `undefined`.
You can check whether the session has been set or not by calling `Hellotext.isInitialized`.

```javascript
if(Hellotext.isInitialized) {
console.log("session is present")
if (Hellotext.isInitialized) {
console.log('session is present')
} else {
console.log("session has not been set")
console.log('session has not been set')
}
```

Moreover, you can hook in and listen for the session being set, such that when it's set, you're notified about the change, like so

```javascript
Hellotext.on("session-set", (session) => {
console.log("session is: ", session)
Hellotext.on('session-set', session => {
console.log('session is: ', session)
})
```

You may want to store the session on your backend when customers are unidentified so you can later [attach it to a profile](https://www.hellotext.com/api#attach_session) when it becomes known.

### Configuration
### Configuration

When initializing the library, you may pass an optional configuration object as the second argument.
When initializing the library, you may pass an optional configuration object as the second argument.

```javascript
Hellotext.initialize("HELLOTEXT_BUSINESS_ID", configurationOptions);
Hellotext.initialize('HELLOTEXT_BUSINESS_ID', configurationOptions)
```

#### Configuration Options

| Property | Description | Type | Default |
|---------------------|------------------------------------------------------------------------------------------------------------------|---------| --- |
| autogenerateSession | Whether the library should automatically generate a session when no session is found in the query or the cookies | Boolean | true
| ------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | ------- |
| autogenerateSession | Whether the library should automatically generate a session when no session is found in the query or the cookies | Boolean | true |
12 changes: 12 additions & 0 deletions __tests__/api/sessions_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import API from '../../src/api/sessions'

beforeEach(() => {
global.fetch = jest.fn().mockResolvedValue({ json: jest.fn().mockResolvedValue({ id: 1 }) })
})

describe('create', () => {
it('creates a session object', async () => {
const response = await new API('M01az53K').create()
expect(response.id).toEqual(1)
})
})
Loading
Loading