@codebards/ik-embeddable-form 0.0.29-qa
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 +556 -0
- package/dist/embed.js +23870 -0
- package/dist/embed.js.map +1 -0
- package/dist/form-variants/masterclass.json +23 -0
- package/dist/form-variants/webinar-card.json +43 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
# IK Embeddable Form
|
|
2
|
+
|
|
3
|
+
A production-ready, config-driven embeddable form platform built with **Preact**, **TypeScript**, **Vite**, **Tailwind CSS**, and **Shadow DOM** isolation.
|
|
4
|
+
|
|
5
|
+
> **Integrating the form on your site?** See **[INTEGRATION.md](INTEGRATION.md)** for step-by-step guides (HTML, WordPress, React), variant usage, custom JSON variants, and fallback behavior.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [Architecture Overview](#architecture-overview)
|
|
12
|
+
2. [Installation](#installation)
|
|
13
|
+
3. [Script Usage](#script-usage)
|
|
14
|
+
4. [Config Schema](#config-schema)
|
|
15
|
+
5. [Environment Setup](#environment-setup)
|
|
16
|
+
6. [Build & Deployment](#build--deployment)
|
|
17
|
+
7. [Versioning Strategy](#versioning-strategy)
|
|
18
|
+
8. [Adding New Form Variants](#adding-new-form-variants)
|
|
19
|
+
9. [Analytics Integration](#analytics-integration)
|
|
20
|
+
10. [API Integration](#api-integration)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Architecture Overview
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
src/
|
|
28
|
+
├── core/
|
|
29
|
+
│ ├── FormEngine/ # Orchestrates step transitions, state, API calls
|
|
30
|
+
│ ├── StepResolver/ # Evaluates conditions, resolves next steps
|
|
31
|
+
│ ├── ConfigResolver/ # Resolves FormConfig from OpenConfig + variants
|
|
32
|
+
│ ├── ApiClient/ # HTTP client with retries, body/response mapping
|
|
33
|
+
│ └── Analytics/ # GTM · Sentry · Clarity · Cookie facades
|
|
34
|
+
│
|
|
35
|
+
├── components/
|
|
36
|
+
│ ├── Modal/ # Accessible modal with backdrop + progress bar
|
|
37
|
+
│ ├── FormSteps/ # StepRenderer · AutoStep · SuccessStep · FieldRenderer
|
|
38
|
+
│ └── Shared/ # Button · Input · Select · Textarea · Radio · Checkbox
|
|
39
|
+
│
|
|
40
|
+
├── configs/
|
|
41
|
+
│ ├── gql-webinar.base.ts # Full webinar form superset
|
|
42
|
+
│ ├── gql-webinar.config.ts # Base + bundled variants registry
|
|
43
|
+
│ └── variants/ # Structural overrides (default, india, event)
|
|
44
|
+
│
|
|
45
|
+
├── hooks/
|
|
46
|
+
│ ├── useFormEngine.ts # Subscribes to FormEngine state
|
|
47
|
+
│ └── useAnalytics.ts # Analytics action hooks
|
|
48
|
+
│
|
|
49
|
+
├── styles/
|
|
50
|
+
│ └── base.css # Tailwind directives + Shadow DOM resets
|
|
51
|
+
│
|
|
52
|
+
├── types/
|
|
53
|
+
│ └── index.ts # FormConfig · FormStep · ValidationRule · ApiContract · AnalyticsEvent
|
|
54
|
+
│
|
|
55
|
+
├── App.tsx # Root Preact component
|
|
56
|
+
└── sdk/
|
|
57
|
+
├── index.ts # window.IKForm global API
|
|
58
|
+
└── mount.ts # Shadow DOM mount/unmount/destroy
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Shadow DOM Isolation
|
|
62
|
+
|
|
63
|
+
The form is mounted inside a **Shadow DOM** attached to a `<div id="ik-form-host">` injected into `document.body`. This means:
|
|
64
|
+
|
|
65
|
+
- Global CSS on the host page **cannot** bleed into the form
|
|
66
|
+
- Form styles **cannot** leak out to the host page
|
|
67
|
+
- No class name or selector conflicts
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
### Via CDN (Recommended)
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<script src="https://cdn.example.com/forms/v1.0.11/embed.js" async></script>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Self-hosted
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Build for production
|
|
83
|
+
npm run build:production
|
|
84
|
+
|
|
85
|
+
# Upload dist/embed.js to your CDN / server
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### npm (for framework integration)
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm install @codebards/ik-embeddable-form
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { IKForm } from '@codebards/ik-embeddable-form';
|
|
96
|
+
|
|
97
|
+
IKForm.open({
|
|
98
|
+
eventName: 'How to Nail your next Technical Interview',
|
|
99
|
+
webinarType: 'REGULAR',
|
|
100
|
+
site: 'organic',
|
|
101
|
+
variant: 'default',
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Full integration examples (HTML, WordPress, React, variants, custom JSON): **[INTEGRATION.md](INTEGRATION.md)**
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Script Usage
|
|
110
|
+
|
|
111
|
+
### Basic
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<script src="https://cdn.example.com/forms/v1.0.11/embed.js" async></script>
|
|
115
|
+
<script>
|
|
116
|
+
window.IKForm.open({
|
|
117
|
+
eventName: 'How to Nail your next Technical Interview',
|
|
118
|
+
webinarType: 'REGULAR',
|
|
119
|
+
site: 'organic', // or 'learn'
|
|
120
|
+
variant: 'default', // 'default' | 'india' | 'event' | content variant name
|
|
121
|
+
});
|
|
122
|
+
</script>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Common options
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
window.IKForm.open({
|
|
129
|
+
// Required
|
|
130
|
+
eventName: 'How to Nail your next Technical Interview',
|
|
131
|
+
webinarType: 'REGULAR',
|
|
132
|
+
site: 'learn',
|
|
133
|
+
variant: 'india',
|
|
134
|
+
|
|
135
|
+
// Optional
|
|
136
|
+
prefilledValues: {
|
|
137
|
+
email: 'user@example.com',
|
|
138
|
+
fullName: 'Jane Doe',
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
configOverrides: {
|
|
142
|
+
layout: {
|
|
143
|
+
leftPanel: { data: { headline: 'Campaign headline' } },
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
dataClickId: 'hero_cta',
|
|
148
|
+
|
|
149
|
+
// WordPress — theme already loads GTM
|
|
150
|
+
loadGtm: false,
|
|
151
|
+
loadClarity: false,
|
|
152
|
+
|
|
153
|
+
onSuccess: (result) => console.log('Submitted:', result.data),
|
|
154
|
+
onClose: () => console.log('Closed'),
|
|
155
|
+
onError: (err) => console.error(err.code, err.message),
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
See **[INTEGRATION.md](INTEGRATION.md)** for WordPress, React/SPA, variant JSON, and fallback details.
|
|
160
|
+
|
|
161
|
+
### Programmatic Control
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
window.IKForm.close();
|
|
165
|
+
window.IKForm.destroy(); // SPA route change
|
|
166
|
+
console.log(window.IKForm.getVersion()); // "1.0.11"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Config Schema
|
|
172
|
+
|
|
173
|
+
### `FormConfig` (root)
|
|
174
|
+
|
|
175
|
+
| Property | Type | Required | Description |
|
|
176
|
+
|------------------|-------------------|----------|--------------------------------------------------|
|
|
177
|
+
| `id` | `string` | ✅ | Unique config identifier |
|
|
178
|
+
| `name` | `string` | ✅ | Human-readable form name |
|
|
179
|
+
| `version` | `string` | ✅ | Semver (e.g. `"1.2.0"`) |
|
|
180
|
+
| `eventName` | `string` | ✅ | Matches `OpenConfig.eventName` |
|
|
181
|
+
| `webinarType` | `string` | – | Matches `OpenConfig.webinarType` |
|
|
182
|
+
| `defaultVariant` | `string` | ✅ | Fallback variant ID |
|
|
183
|
+
| `variants` | `FormVariant[]` | – | Named overrides applied on top of base config |
|
|
184
|
+
| `steps` | `FormStep[]` | ✅ | Ordered step definitions |
|
|
185
|
+
| `api` | `ApiContract` | ✅ | Default submission endpoint |
|
|
186
|
+
| `analytics` | `AnalyticsConfig` | – | Open/close/submit event config |
|
|
187
|
+
| `theme` | `ThemeConfig` | – | Visual customisation |
|
|
188
|
+
| `i18n` | `Record<string, string>` | – | Localisation strings |
|
|
189
|
+
| `metadata` | `Record<string, string \| number \| boolean>` | – | Forwarded to analytics |
|
|
190
|
+
|
|
191
|
+
### `FormStep`
|
|
192
|
+
|
|
193
|
+
| Property | Type | Description |
|
|
194
|
+
|------------------|----------------------------------|--------------------------------------------------|
|
|
195
|
+
| `id` | `string` | Unique step identifier |
|
|
196
|
+
| `type` | `"form" \| "auto" \| "success" \| "error" \| "info"` | Step type |
|
|
197
|
+
| `title` | `string?` | Step heading |
|
|
198
|
+
| `subtitle` | `string?` | Step sub-heading |
|
|
199
|
+
| `hidden` | `boolean?` | Hide from progress indicator |
|
|
200
|
+
| `condition` | `ConditionalRule?` | Step only reachable when condition passes |
|
|
201
|
+
| `autoExecute` | `AutoExecuteConfig?` | API call fired automatically on step entry |
|
|
202
|
+
| `fields` | `FormField[]?` | Input fields rendered in this step |
|
|
203
|
+
| `api` | `ApiContract?` | Per-step API call (fires on Next) |
|
|
204
|
+
| `nextStep` | `string \| ConditionalNextStep[]?` | Fixed or conditional next step routing |
|
|
205
|
+
| `allowBack` | `boolean?` | Show Back button (default: `true`) |
|
|
206
|
+
| `submitLabel` | `string?` | Override CTA label |
|
|
207
|
+
| `analyticsEvents`| `AnalyticsEvent[]?` | Events fired when step becomes active |
|
|
208
|
+
|
|
209
|
+
### `FormField`
|
|
210
|
+
|
|
211
|
+
| Property | Type | Description |
|
|
212
|
+
|----------------|-------------------|--------------------------------------------------|
|
|
213
|
+
| `name` | `string` | Field name (key in form data) |
|
|
214
|
+
| `type` | `FieldType` | `text \| email \| tel \| number \| textarea \| select \| radio \| checkbox \| date \| hidden \| slot-picker` |
|
|
215
|
+
| `label` | `string?` | Label text |
|
|
216
|
+
| `placeholder` | `string?` | Placeholder text |
|
|
217
|
+
| `defaultValue` | `string \| number \| boolean?` | Initial value |
|
|
218
|
+
| `options` | `FieldOption[]?` | Options for select/radio |
|
|
219
|
+
| `validation` | `ValidationRule[]?` | Array of validation rules |
|
|
220
|
+
| `condition` | `ConditionalRule?` | Show/hide field based on other field values |
|
|
221
|
+
| `colSpan` | `number?` | Grid column span (1 = half, 2 = full) |
|
|
222
|
+
|
|
223
|
+
### `ValidationRule`
|
|
224
|
+
|
|
225
|
+
| Property | Type | Description |
|
|
226
|
+
|--------------|------------------|---------------------------------------------------|
|
|
227
|
+
| `type` | `ValidationType` | `required \| email \| phone \| min \| max \| minLength \| maxLength \| pattern \| custom \| async` |
|
|
228
|
+
| `value` | `number \| string?` | Threshold or pattern string |
|
|
229
|
+
| `message` | `string` | Error message shown to user |
|
|
230
|
+
| `condition` | `ConditionalRule?` | Only apply this rule when condition is true |
|
|
231
|
+
|
|
232
|
+
### `ApiContract`
|
|
233
|
+
|
|
234
|
+
| Property | Type | Description |
|
|
235
|
+
|-------------------|-----------------------|--------------------------------------------------|
|
|
236
|
+
| `endpoint` | `string` | Relative path or full URL. Supports `{fieldName}` interpolation |
|
|
237
|
+
| `method` | `HttpMethod` | `GET \| POST \| PUT \| PATCH \| DELETE` |
|
|
238
|
+
| `bodyMapping` | `Record<string,string> \| "$all"` | Map form fields → request body keys |
|
|
239
|
+
| `responseMapping` | `Record<string,string>?` | Map response JSON paths → form field names |
|
|
240
|
+
| `queryParams` | `Record<string,string>?` | Query params with `{fieldName}` interpolation|
|
|
241
|
+
| `authenticated` | `boolean?` | Include `Authorization: Bearer` header |
|
|
242
|
+
| `retries` | `number?` | Retry count (default: 1) |
|
|
243
|
+
| `timeoutMs` | `number?` | Request timeout (default: 10,000ms) |
|
|
244
|
+
| `mockResponse` | `unknown?` | Used in `local` env instead of real network call |
|
|
245
|
+
|
|
246
|
+
### `ConditionalRule`
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
{
|
|
250
|
+
logical?: "AND" | "OR", // default: "AND"
|
|
251
|
+
clauses: [
|
|
252
|
+
{
|
|
253
|
+
field: "fieldName", // dot notation supported: "address.city"
|
|
254
|
+
operator: "equals" | "not_equals" | "contains" | "not_contains" |
|
|
255
|
+
"starts_with" | "ends_with" | "greater_than" | "less_than" |
|
|
256
|
+
"is_empty" | "is_not_empty" | "matches_regex",
|
|
257
|
+
value?: "some value" // not needed for is_empty / is_not_empty
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Environment Setup
|
|
266
|
+
|
|
267
|
+
| File | Used For |
|
|
268
|
+
|-------------------|------------------------|
|
|
269
|
+
| `.env` | Local development |
|
|
270
|
+
| `.env.staging` | Staging builds |
|
|
271
|
+
| `.env.production` | Production builds |
|
|
272
|
+
|
|
273
|
+
### Required Variables
|
|
274
|
+
|
|
275
|
+
```ini
|
|
276
|
+
VITE_API_BASE_URL=https://api.example.com/api
|
|
277
|
+
VITE_GTM_ID=GTM-XXXXXXX
|
|
278
|
+
VITE_SENTRY_DSN=https://xxx@sentry.io/xxx
|
|
279
|
+
VITE_CLARITY_ID=xxxxxxxxxx
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Build & Deployment
|
|
285
|
+
|
|
286
|
+
### Local Dev Server
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
npm install
|
|
290
|
+
npm run dev
|
|
291
|
+
# Opens http://localhost:3000 with a test page
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Build Commands
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
npm run build # Builds with .env (local defaults)
|
|
298
|
+
npm run build:staging # Builds with .env.staging
|
|
299
|
+
npm run build:production # Builds with .env.production (minified)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Build Output
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
dist/
|
|
306
|
+
├── embed.js # Self-contained IIFE bundle (reference in <script>)
|
|
307
|
+
└── embed.js.map # Source map (only in staging/local builds)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Deployment Checklist
|
|
311
|
+
|
|
312
|
+
1. Run `npm run build:production`
|
|
313
|
+
2. Upload `dist/embed.js` to your CDN with a versioned path:
|
|
314
|
+
```
|
|
315
|
+
https://cdn.example.com/forms/v1.0.0/embed.js
|
|
316
|
+
```
|
|
317
|
+
3. Set cache headers:
|
|
318
|
+
- `embed.js` → `Cache-Control: public, max-age=31536000, immutable` (versioned path)
|
|
319
|
+
- Use a `latest` alias for convenience: `cdn.example.com/forms/latest/embed.js` with shorter TTL
|
|
320
|
+
|
|
321
|
+
4. Reference on the host page:
|
|
322
|
+
```html
|
|
323
|
+
<script src="https://cdn.example.com/forms/v1.0.0/embed.js" async></script>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Versioning Strategy
|
|
329
|
+
|
|
330
|
+
This project follows **Semantic Versioning** (`MAJOR.MINOR.PATCH`):
|
|
331
|
+
|
|
332
|
+
| Change Type | Version Bump | Example |
|
|
333
|
+
|----------------------|-------------|---------|
|
|
334
|
+
| Breaking API change | MAJOR | `2.0.0` |
|
|
335
|
+
| New form variant | MINOR | `1.1.0` |
|
|
336
|
+
| Bug fix / patch | PATCH | `1.0.1` |
|
|
337
|
+
|
|
338
|
+
### Release Process
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
# 1. Bump version in package.json
|
|
342
|
+
npm version patch # or minor / major
|
|
343
|
+
|
|
344
|
+
# 2. Build production artefact
|
|
345
|
+
npm run build:production
|
|
346
|
+
|
|
347
|
+
# 3. Tag the release
|
|
348
|
+
git tag v$(node -p "require('./package.json').version")
|
|
349
|
+
git push --tags
|
|
350
|
+
|
|
351
|
+
# 4. Upload dist/embed.js to CDN under versioned path
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The `__VERSION__` constant is automatically injected from `package.json` at build time.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Adding New Form Variants
|
|
359
|
+
|
|
360
|
+
The form uses a **base config + variant overrides** merge pattern. See **[INTEGRATION.md](INTEGRATION.md)** for host-page usage, JSON schema, resolution order, and fallback behavior.
|
|
361
|
+
|
|
362
|
+
| Tier | Examples | Where | Deploy needed? |
|
|
363
|
+
|------|----------|-------|----------------|
|
|
364
|
+
| **Structural** | `default`, `india`, `event` | `src/configs/variants/*.variant.ts` | Yes (code review) |
|
|
365
|
+
| **Content** | `masterclass`, campaign names | CDN JSON at `{VITE_FORM_VARIANTS_BASE_URL}/{variant}.json` | No (upload JSON only) |
|
|
366
|
+
|
|
367
|
+
**Resolution order:** bundled variant → remote JSON → fallback to `default` (console warning).
|
|
368
|
+
|
|
369
|
+
Set `VITE_FORM_VARIANTS_BASE_URL` at build time. Local dev defaults to `/form-variants` (`public/form-variants/masterclass.json`).
|
|
370
|
+
|
|
371
|
+
### Bundled variant files
|
|
372
|
+
|
|
373
|
+
| File | Purpose |
|
|
374
|
+
|------|---------|
|
|
375
|
+
| `default.variant.ts` | Standard flow |
|
|
376
|
+
| `india.variant.ts` | Hide `primaryGoal` on profile step |
|
|
377
|
+
| `event.variant.ts` | Hide slot picker; event copy; auto-book slot |
|
|
378
|
+
|
|
379
|
+
Base config (`gql-webinar.base.ts`) holds the full superset. Each variant file declares only what differs.
|
|
380
|
+
|
|
381
|
+
### Adding a structural variant (engineering)
|
|
382
|
+
|
|
383
|
+
1. Create `src/configs/variants/my-variant.variant.ts` with only the diff:
|
|
384
|
+
|
|
385
|
+
```ts
|
|
386
|
+
import type { FormVariant } from '@/types';
|
|
387
|
+
|
|
388
|
+
export const myVariant: FormVariant = {
|
|
389
|
+
id: 'my-variant',
|
|
390
|
+
overrides: {
|
|
391
|
+
steps: {
|
|
392
|
+
'profile-details': {
|
|
393
|
+
fields: { primaryGoal: { show: false } },
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
2. Register in `src/configs/variants/index.ts`
|
|
401
|
+
3. Deploy embed
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
### Adding a content variant (no code deploy)
|
|
406
|
+
|
|
407
|
+
1. Create `{variant}.json` — see [INTEGRATION.md → Create your own variant JSON](INTEGRATION.md#create-your-own-variant-json)
|
|
408
|
+
2. Upload to `{VITE_FORM_VARIANTS_BASE_URL}/{variant}.json`
|
|
409
|
+
3. Landing page: `IKForm.open({ ..., variant: 'masterclass' })`
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
### Option B — New FormConfig File
|
|
414
|
+
|
|
415
|
+
For a fundamentally different product flow (not a variant of GQL webinar):
|
|
416
|
+
|
|
417
|
+
1. Create `src/configs/my-new-form.base.ts`
|
|
418
|
+
2. Export a `FormConfig` with a unique `webinarType`
|
|
419
|
+
3. Register in `src/configs/index.ts`
|
|
420
|
+
|
|
421
|
+
```ts
|
|
422
|
+
IKForm.open({ webinarType: 'MASTERCLASS', variant: 'default', ... });
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Analytics Integration
|
|
428
|
+
|
|
429
|
+
### GTM
|
|
430
|
+
|
|
431
|
+
The embed can **load its own GTM container** (per architecture doc) so form conversions work on Lovable / Framer / plain HTML without host-page GTM.
|
|
432
|
+
|
|
433
|
+
| Setting | Default | Purpose |
|
|
434
|
+
|---------|---------|---------|
|
|
435
|
+
| `VITE_GTM_ID` | `GTM-P335R9N` (staging/prod) | Container injected when form opens |
|
|
436
|
+
| `loadGtm` in `IKForm.open()` | `true` | Set `false` on WordPress if the theme already loads the same container |
|
|
437
|
+
| `gtmContainerId` in `IKForm.open()` | build-time ID | Optional override (e.g. dedicated embed container) |
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
// Self-contained LP (embed loads GTM)
|
|
441
|
+
IKForm.open({
|
|
442
|
+
eventName: 'How to Nail your next Technical Interview',
|
|
443
|
+
webinarType: 'REGULAR',
|
|
444
|
+
site: 'learn',
|
|
445
|
+
variant: 'default',
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// WordPress page that already has GTM-P335R9N in the theme
|
|
449
|
+
IKForm.open({ ..., loadGtm: false, loadClarity: false });
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**dataLayer events** (WordPress parity + embed lifecycle):
|
|
453
|
+
|
|
454
|
+
| When | Event |
|
|
455
|
+
|------|--------|
|
|
456
|
+
| Contact step success | `new_webinar_registration_form_submitted` |
|
|
457
|
+
| Slot proceed | `wordpress_form_submitted` (`formName: "Webinar Slot Selection"`) |
|
|
458
|
+
| Profile step | `pa_new_webinar_registration_form_submitted` (conversion — Meta / Google Ads / LinkedIn) |
|
|
459
|
+
| Form open / step view | `form_open`, `form_step_view`, … |
|
|
460
|
+
|
|
461
|
+
GTM fires **marketing pixels**. **Clickstream** (`ve2lt1a8il/qa` → BigQuery) is separate — see above.
|
|
462
|
+
|
|
463
|
+
Events are pushed to `window.dataLayer`. Configure GTM triggers on:
|
|
464
|
+
|
|
465
|
+
- `form_open` — fired when modal opens
|
|
466
|
+
- `form_close` — fired when modal closes
|
|
467
|
+
- `form_step_view` — fired on each step
|
|
468
|
+
- `form_step_complete` — fired after Next/Submit per step
|
|
469
|
+
- `form_submit_success` — fired on final submission
|
|
470
|
+
- `form_submit_error` — fired on submission failure
|
|
471
|
+
|
|
472
|
+
### Sentry
|
|
473
|
+
|
|
474
|
+
Install `@sentry/browser` and uncomment the placeholder code in `src/core/Analytics/sentry.ts`. The DSN is injected from `VITE_SENTRY_DSN`.
|
|
475
|
+
|
|
476
|
+
### Clarity
|
|
477
|
+
|
|
478
|
+
Ensure the Clarity script is on the host page, or uncomment the injection block in `src/core/Analytics/clarity.ts`.
|
|
479
|
+
|
|
480
|
+
### Custom Events
|
|
481
|
+
|
|
482
|
+
Listen on the host page:
|
|
483
|
+
|
|
484
|
+
```js
|
|
485
|
+
window.addEventListener('ikform:analytics', (e) => {
|
|
486
|
+
console.log('IKForm event:', e.detail);
|
|
487
|
+
});
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## API Integration
|
|
493
|
+
|
|
494
|
+
All API calls are defined in `ApiContract` objects inside your form config — **no hardcoded fetch calls**.
|
|
495
|
+
|
|
496
|
+
### Dynamic URL Interpolation
|
|
497
|
+
|
|
498
|
+
```ts
|
|
499
|
+
endpoint: 'webinar/{site}/add-info'
|
|
500
|
+
// If form data has { site: "organic" }
|
|
501
|
+
// → POST https://api.example.com/api/webinar/organic/add-info
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Body Mapping
|
|
505
|
+
|
|
506
|
+
```ts
|
|
507
|
+
bodyMapping: {
|
|
508
|
+
email: 'user.email', // form field → nested request body key
|
|
509
|
+
slot: 'booking.slotId',
|
|
510
|
+
}
|
|
511
|
+
// → { user: { email: "..." }, booking: { slotId: "..." } }
|
|
512
|
+
|
|
513
|
+
// OR forward all fields:
|
|
514
|
+
bodyMapping: '$all'
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Response Mapping
|
|
518
|
+
|
|
519
|
+
```ts
|
|
520
|
+
responseMapping: {
|
|
521
|
+
'data.registrationId': 'registrationId', // response path → form field
|
|
522
|
+
}
|
|
523
|
+
// Writes response.data.registrationId back into form state as "registrationId"
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Auth Token
|
|
527
|
+
|
|
528
|
+
```ts
|
|
529
|
+
import { apiClient } from '@codebards/ik-embeddable-form';
|
|
530
|
+
|
|
531
|
+
apiClient.setAuthToken('Bearer your-token-here');
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
## TypeScript Types Reference
|
|
537
|
+
|
|
538
|
+
```ts
|
|
539
|
+
import type {
|
|
540
|
+
FormConfig,
|
|
541
|
+
FormStep,
|
|
542
|
+
FormField,
|
|
543
|
+
ValidationRule,
|
|
544
|
+
ApiContract,
|
|
545
|
+
AnalyticsEvent,
|
|
546
|
+
OpenConfig,
|
|
547
|
+
ConditionalRule,
|
|
548
|
+
IKFormSDK,
|
|
549
|
+
} from '@codebards/ik-embeddable-form';
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## License
|
|
555
|
+
|
|
556
|
+
MIT — see [LICENSE](LICENSE)
|