@kundai/angular 1.0.0

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 ADDED
@@ -0,0 +1,332 @@
1
+ # @kundai/angular
2
+
3
+ Module Angular officiel pour intégrer le tracking KundAi dans une application Angular.
4
+
5
+ ---
6
+
7
+ ## Sommaire
8
+
9
+ - [Installation](#installation)
10
+ - [Configuration](#configuration)
11
+ - [Démarrage automatique](#démarrage-automatique)
12
+ - [Identifier un utilisateur](#identifier-un-utilisateur)
13
+ - [Événements](#événements)
14
+ - [Conversions](#conversions)
15
+ - [Passer le sessionId au backend](#passer-le-sessionid-au-backend)
16
+ - [Référence API](#référence-api)
17
+ - [Ce qui est collecté automatiquement](#ce-qui-est-collecté-automatiquement)
18
+
19
+ ---
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @kundai/angular
25
+ ```
26
+
27
+ **Versions Angular supportées :** Angular 17, 18, 19+
28
+
29
+ ---
30
+
31
+ ## Configuration
32
+
33
+ ### App standalone (Angular 17+)
34
+
35
+ ```typescript
36
+ // app.config.ts
37
+ import { ApplicationConfig } from '@angular/core';
38
+ import { provideRouter } from '@angular/router';
39
+ import { KundaiModule } from '@kundai/angular';
40
+ import { routes } from './app.routes';
41
+
42
+ export const appConfig: ApplicationConfig = {
43
+ providers: [
44
+ provideRouter(routes),
45
+ ...KundaiModule.forRoot({
46
+ baseUrl: 'https://api.kundai.io/v1/tracking',
47
+ apiKey: 'kundai_xxxxxxxxxxxx',
48
+ appId: 'mon-app-web',
49
+ }),
50
+ ],
51
+ };
52
+ ```
53
+
54
+ ### AppModule (Angular classique)
55
+
56
+ ```typescript
57
+ // app.module.ts
58
+ import { NgModule } from '@angular/core';
59
+ import { BrowserModule } from '@angular/platform-browser';
60
+ import { KundaiModule } from '@kundai/angular';
61
+
62
+ @NgModule({
63
+ imports: [BrowserModule],
64
+ providers: [
65
+ ...KundaiModule.forRoot({
66
+ baseUrl: 'https://api.kundai.io/v1/tracking',
67
+ apiKey: 'kundai_xxxxxxxxxxxx',
68
+ appId: 'mon-app-web',
69
+ }),
70
+ ],
71
+ })
72
+ export class AppModule {}
73
+ ```
74
+
75
+ ### Options de configuration
76
+
77
+ | Option | Type | Requis | Description |
78
+ |---|---|---|---|
79
+ | `baseUrl` | `string` | ✅ | URL du endpoint tracking KundAi |
80
+ | `apiKey` | `string` | ✅ | Clé API générée dans le dashboard KundAi |
81
+ | `appId` | `string` | — | Identifiant de ton app (défaut : `"web"`) |
82
+ | `timeoutMs` | `number` | — | Timeout des requêtes HTTP en ms (défaut : `5000`) |
83
+ | `autoPageView` | `boolean` | — | Page-view auto à chaque route (défaut : `true`) |
84
+
85
+ ### Variables d'environnement recommandées
86
+
87
+ ```typescript
88
+ // environments/environment.ts
89
+ export const environment = {
90
+ production: false,
91
+ kundaiTrackingUrl: 'http://localhost:3000/v1/tracking',
92
+ kundaiApiKey: 'kundai_xxxxxxxxxxxx',
93
+ };
94
+
95
+ // environments/environment.prod.ts
96
+ export const environment = {
97
+ production: true,
98
+ kundaiTrackingUrl: 'https://api.kundai.io/v1/tracking',
99
+ kundaiApiKey: 'kundai_xxxxxxxxxxxx',
100
+ };
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Démarrage automatique
106
+
107
+ Dès que `KundaiModule.forRoot()` est configuré, le module :
108
+
109
+ 1. Génère un `visitorId` persistant (localStorage)
110
+ 2. Capture les paramètres UTM et ad click IDs depuis l'URL
111
+ 3. Enregistre un clic si des UTM sont présents (touchpoint d'attribution)
112
+ 4. Démarre une session
113
+ 5. Enregistre la première page-view
114
+ 6. Écoute les changements de route Angular et enregistre une page-view à chaque `NavigationEnd`
115
+ 7. Clôture la session à la fermeture de l'onglet (`pagehide`)
116
+
117
+ **Aucun code supplémentaire n'est nécessaire pour ces comportements.**
118
+
119
+ ---
120
+
121
+ ## Identifier un utilisateur
122
+
123
+ À appeler après la connexion de l'utilisateur pour lier son identité à la session.
124
+
125
+ ```typescript
126
+ import { inject, Injectable } from '@angular/core';
127
+ import { KundaiService } from '@kundai/angular';
128
+
129
+ @Injectable({ providedIn: 'root' })
130
+ export class AuthService {
131
+ private readonly kundai = inject(KundaiService);
132
+
133
+ async afterLogin(user: { id: string; email: string }) {
134
+ // Lie l'utilisateur à la session navigateur courante
135
+ await this.kundai.identify(user.id, user.email);
136
+ }
137
+
138
+ afterLogout() {
139
+ // Efface l'identité locale (ne clôture pas la session)
140
+ this.kundai.clearUser();
141
+ }
142
+ }
143
+ ```
144
+
145
+ > L'email n'est **jamais stocké en clair** — il est haché SHA-256 côté serveur
146
+ > avant persistance en base de données.
147
+
148
+ ---
149
+
150
+ ## Événements
151
+
152
+ ### Événements comportementaux libres
153
+
154
+ `track()` accepte n'importe quel nom d'événement et des propriétés libres.
155
+
156
+ ```typescript
157
+ import { Component, inject } from '@angular/core';
158
+ import { KundaiService } from '@kundai/angular';
159
+
160
+ @Component({ ... })
161
+ export class MyComponent {
162
+ private readonly kundai = inject(KundaiService);
163
+
164
+ onButtonClick() {
165
+ this.kundai.track('button_click', { button: 'cta_hero', plan: 'pro' });
166
+ }
167
+
168
+ onFormSubmit() {
169
+ this.kundai.track('form_submit', { form: 'contact' });
170
+ }
171
+
172
+ onVideoPlay(videoId: string) {
173
+ this.kundai.track('video_play', { videoId });
174
+ }
175
+ }
176
+ ```
177
+
178
+ ### Événements recommandés par catégorie
179
+
180
+ Ces noms sont des conventions — KundAi les reconnaît et les affiche dans le dashboard.
181
+
182
+ **Acquisition / Onboarding**
183
+
184
+ | Événement | Propriétés suggérées | Description |
185
+ |---|---|---|
186
+ | `page_view` | `url`, `title` | Vue de page (automatique) |
187
+ | `signup_started` | `plan`, `source` | Début d'inscription |
188
+ | `signup_completed` | `plan`, `method` | Inscription terminée |
189
+ | `account_created` | `plan`, `country` | Compte créé |
190
+ | `login` | `method` | Connexion réussie |
191
+ | `logout` | — | Déconnexion |
192
+
193
+ **Engagement**
194
+
195
+ | Événement | Propriétés suggérées | Description |
196
+ |---|---|---|
197
+ | `button_click` | `button`, `page` | Clic sur un bouton |
198
+ | `form_submit` | `form` | Soumission de formulaire |
199
+ | `search` | `query`, `results_count` | Recherche effectuée |
200
+ | `video_play` | `videoId`, `duration` | Lecture vidéo |
201
+ | `download` | `file`, `type` | Téléchargement |
202
+ | `share` | `platform`, `content` | Partage |
203
+ | `feature_used` | `feature` | Fonctionnalité utilisée |
204
+
205
+ **Funnel commercial**
206
+
207
+ | Événement | Propriétés suggérées | Description |
208
+ |---|---|---|
209
+ | `plan_viewed` | `plan` | Page de tarification vue |
210
+ | `plan_selected` | `plan`, `price`, `currency` | Plan sélectionné |
211
+ | `checkout_started` | `plan`, `amount`, `currency` | Paiement initié |
212
+ | `checkout_abandoned` | `plan`, `step` | Paiement abandonné |
213
+
214
+ **Conversions** (déclenchent l'attribution dans KundAi)
215
+
216
+ | Événement | Propriétés requises | Propriétés optionnelles |
217
+ |---|---|---|
218
+ | `purchase` | `amount`, `currency` | `planId`, `txRef` |
219
+ | `payment` | `amount`, `currency` | `planId`, `provider` |
220
+ | `conversion` | `amount`, `currency` | `planId` |
221
+ | `checkout` | `amount`, `currency` | `planId` |
222
+
223
+ > Les événements `purchase`, `payment`, `conversion` et `checkout` sont traités
224
+ > spécialement par KundAi : ils déclenchent le moteur d'attribution et marquent
225
+ > la session comme convertie.
226
+
227
+ ---
228
+
229
+ ## Conversions
230
+
231
+ ### Conversion côté frontend (paiement popup)
232
+
233
+ Pour les paiements confirmés directement dans le navigateur (ex. Flutterwave popup) :
234
+
235
+ ```typescript
236
+ onPaymentSuccess(result: { amount: number; currency: string; planId: string }) {
237
+ this.kundai.trackConversion({
238
+ amount: result.amount,
239
+ currency: result.currency,
240
+ planId: result.planId,
241
+ event: 'purchase',
242
+ });
243
+ }
244
+ ```
245
+
246
+ ### Conversion côté serveur (webhook)
247
+
248
+ Pour les paiements confirmés par webhook (Flutterwave, Stripe, etc.), le frontend
249
+ doit passer son `sessionId` au backend. Voir la section suivante.
250
+
251
+ ---
252
+
253
+ ## Passer le sessionId au backend
254
+
255
+ Le `sessionId` est le lien entre la session navigateur et les conversions confirmées
256
+ côté serveur. Sans lui, l'attribution est aveugle.
257
+
258
+ ```typescript
259
+ // checkout.component.ts
260
+ import { Component, inject } from '@angular/core';
261
+ import { HttpClient } from '@angular/common/http';
262
+ import { KundaiService } from '@kundai/angular';
263
+
264
+ @Component({ ... })
265
+ export class CheckoutComponent {
266
+ private readonly kundai = inject(KundaiService);
267
+ private readonly http = inject(HttpClient);
268
+
269
+ async pay(planId: string, amount: number) {
270
+ this.kundai.track('checkout_started', { planId, amount, currency: 'XOF' });
271
+
272
+ await this.http.post('/api/payments/checkout', {
273
+ planId,
274
+ amount,
275
+ currency: 'XOF',
276
+ sessionId: this.kundai.getSessionId(), // ← transmettre au backend
277
+ }).toPromise();
278
+ }
279
+ }
280
+ ```
281
+
282
+ Le backend stocke ce `sessionId` et le renvoie à KundAi lors de la confirmation
283
+ du paiement via `@kundai/nestjs`. Voir la documentation de `@kundai/nestjs`.
284
+
285
+ ---
286
+
287
+ ## Référence API
288
+
289
+ ```typescript
290
+ // Injecter le service
291
+ private readonly kundai = inject(KundaiService);
292
+
293
+ // Identifier l'utilisateur connecté
294
+ await kundai.identify(userId: string, email?: string): Promise<void>
295
+
296
+ // Effacer l'identité locale (après logout)
297
+ kundai.clearUser(): void
298
+
299
+ // Envoyer un événement comportemental
300
+ kundai.track(event: string, properties?: Record<string, unknown>): void
301
+
302
+ // Notifier une conversion avec revenu (retry automatique x3)
303
+ kundai.trackConversion(opts: {
304
+ amount: number
305
+ currency: string // ISO 4217 : 'XOF', 'EUR', 'USD', 'GHS', 'NGN'...
306
+ planId?: string
307
+ event?: 'conversion' | 'purchase' | 'payment' | 'checkout'
308
+ metadata?: Record<string, unknown>
309
+ }): void
310
+
311
+ // Enregistrer manuellement une page-view
312
+ await kundai.recordPageView(): Promise<void>
313
+
314
+ // Récupérer le sessionId courant (à passer au backend)
315
+ kundai.getSessionId(): string | null
316
+
317
+ // Récupérer le visitorId courant
318
+ kundai.getVisitorId(): string
319
+ ```
320
+
321
+ ---
322
+
323
+ ## Ce qui est collecté automatiquement
324
+
325
+ | Donnée | Stockage | Note |
326
+ |---|---|---|
327
+ | `visitorId` | localStorage | UUID généré à la première visite |
328
+ | UTM (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`) | localStorage | Capturés depuis l'URL, persistés pour les conversions futures |
329
+ | Ad click IDs (`fbclid`, `gclid`, `ttclid`, `liclid`) | localStorage | Capturés depuis l'URL |
330
+ | IP address | Serveur | Anonymisée (dernier octet masqué IPv4, 80 bits IPv6) |
331
+ | User-Agent | Serveur | Utilisé pour détecter device/browser/OS |
332
+ | Email | Serveur | Haché SHA-256, jamais stocké en clair |
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@kundai/angular",
3
+ "version": "1.0.0",
4
+ "description": "Module Angular KundAi — tracking sessions, UTM, conversions et identify en 2 lignes.",
5
+ "keywords": ["kundai", "angular", "tracking", "analytics", "attribution", "utm"],
6
+ "license": "MIT",
7
+ "author": "KundAi <dev@kundai.io>",
8
+ "sideEffects": false,
9
+ "main": "./src/index.js",
10
+ "types": "./src/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./src/index.d.ts",
14
+ "import": "./src/index.js",
15
+ "require": "./src/index.js"
16
+ }
17
+ },
18
+ "files": ["src", "README.md"],
19
+ "publishConfig": { "access": "public" },
20
+ "dependencies": {
21
+ "tslib": "^2.3.0",
22
+ "rxjs": "^7.0.0",
23
+ "@kundai/contracts": "1.0.0"
24
+ },
25
+ "peerDependencies": {
26
+ "@angular/core": ">=17",
27
+ "@angular/common": ">=17",
28
+ "@angular/router": ">=17"
29
+ }
30
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { KundaiModule } from './lib/kundai.module';
2
+ export { KundaiService } from './lib/kundai.service';
3
+ export { KundaiRouterService } from './lib/kundai-router.service';
4
+ export { KundaiConfig, KUNDAI_CONFIG } from './lib/kundai.config';
package/src/index.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KUNDAI_CONFIG = exports.KundaiRouterService = exports.KundaiService = exports.KundaiModule = void 0;
4
+ var kundai_module_1 = require("./lib/kundai.module");
5
+ Object.defineProperty(exports, "KundaiModule", { enumerable: true, get: function () { return kundai_module_1.KundaiModule; } });
6
+ var kundai_service_1 = require("./lib/kundai.service");
7
+ Object.defineProperty(exports, "KundaiService", { enumerable: true, get: function () { return kundai_service_1.KundaiService; } });
8
+ var kundai_router_service_1 = require("./lib/kundai-router.service");
9
+ Object.defineProperty(exports, "KundaiRouterService", { enumerable: true, get: function () { return kundai_router_service_1.KundaiRouterService; } });
10
+ var kundai_config_1 = require("./lib/kundai.config");
11
+ Object.defineProperty(exports, "KUNDAI_CONFIG", { enumerable: true, get: function () { return kundai_config_1.KUNDAI_CONFIG; } });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["file:///F:/Projet/Kundai/src/libs/angular/src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAmD;AAA1C,6GAAA,YAAY,OAAA;AACrB,uDAAqD;AAA5C,+GAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,4HAAA,mBAAmB,OAAA;AAC5B,qDAAkE;AAA3C,8GAAA,aAAa,OAAA"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Service d'écoute du Router Angular pour le tracking automatique des pages.
3
+ * Enregistre une page-view à chaque NavigationEnd si `autoPageView` est activé.
4
+ * Bootstrappé automatiquement par KundaiModule via APP_INITIALIZER.
5
+ */
6
+ import { OnDestroy } from '@angular/core';
7
+ export declare class KundaiRouterService implements OnDestroy {
8
+ private readonly kundai;
9
+ private readonly router;
10
+ private readonly config;
11
+ private sub;
12
+ private firstNav;
13
+ /** Initialise le tracker et branche l'écoute du router. */
14
+ bootstrap(): Promise<void>;
15
+ ngOnDestroy(): void;
16
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KundaiRouterService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Service d'écoute du Router Angular pour le tracking automatique des pages.
7
+ * Enregistre une page-view à chaque NavigationEnd si `autoPageView` est activé.
8
+ * Bootstrappé automatiquement par KundaiModule via APP_INITIALIZER.
9
+ */
10
+ const core_1 = require("@angular/core");
11
+ const router_1 = require("@angular/router");
12
+ const rxjs_1 = require("rxjs");
13
+ const kundai_config_1 = require("./kundai.config");
14
+ const kundai_service_1 = require("./kundai.service");
15
+ let KundaiRouterService = class KundaiRouterService {
16
+ kundai = (0, core_1.inject)(kundai_service_1.KundaiService);
17
+ router = (0, core_1.inject)(router_1.Router);
18
+ config = (0, core_1.inject)(kundai_config_1.KUNDAI_CONFIG);
19
+ sub = null;
20
+ firstNav = true;
21
+ /** Initialise le tracker et branche l'écoute du router. */
22
+ async bootstrap() {
23
+ await this.kundai.init();
24
+ if (this.config.autoPageView === false)
25
+ return;
26
+ this.sub = this.router.events
27
+ .pipe((0, rxjs_1.filter)(e => e instanceof router_1.NavigationEnd))
28
+ .subscribe(() => {
29
+ // La première navigation est déjà couverte par init() → recordPageView().
30
+ if (this.firstNav) {
31
+ this.firstNav = false;
32
+ return;
33
+ }
34
+ this.kundai.recordPageView().catch(() => undefined);
35
+ });
36
+ }
37
+ ngOnDestroy() {
38
+ this.sub?.unsubscribe();
39
+ }
40
+ };
41
+ exports.KundaiRouterService = KundaiRouterService;
42
+ exports.KundaiRouterService = KundaiRouterService = tslib_1.__decorate([
43
+ (0, core_1.Injectable)()
44
+ ], KundaiRouterService);
45
+ //# sourceMappingURL=kundai-router.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kundai-router.service.js","sourceRoot":"","sources":["file:///F:/Projet/Kundai/src/libs/angular/src/lib/kundai-router.service.ts"],"names":[],"mappings":";;;;AAAA;;;;GAIG;AACH,wCAA8D;AAC9D,4CAAwD;AACxD,+BAA4C;AAC5C,mDAAgD;AAChD,qDAAiD;AAG1C,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IACb,MAAM,GAAG,IAAA,aAAM,EAAC,8BAAa,CAAC,CAAC;IAC/B,MAAM,GAAG,IAAA,aAAM,EAAC,eAAM,CAAC,CAAC;IACxB,MAAM,GAAG,IAAA,aAAM,EAAC,6BAAa,CAAC,CAAC;IACxC,GAAG,GAAwB,IAAI,CAAC;IAChC,QAAQ,GAAG,IAAI,CAAC;IAExB,2DAA2D;IAC3D,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAEzB,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,KAAK,KAAK;YAAE,OAAO;QAE/C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM;aAC1B,IAAI,CAAC,IAAA,aAAM,EAAC,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,sBAAa,CAAC,CAAC;aAC7C,SAAS,CAAC,GAAG,EAAE;YACd,0EAA0E;YAC1E,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAAC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;gBAAC,OAAO;YAAC,CAAC;YACrD,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;IACP,CAAC;IAED,WAAW;QACT,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,CAAC;IAC1B,CAAC;CACF,CAAA;AAzBY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,iBAAU,GAAE;GACA,mBAAmB,CAyB/B"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Configuration du module Angular KundAi (`@kundai/angular`).
3
+ */
4
+ import { InjectionToken } from '@angular/core';
5
+ /** Configuration passée à `KundaiModule.forRoot()`. */
6
+ export interface KundaiConfig {
7
+ /**
8
+ * URL de base du endpoint de tracking KundAi.
9
+ * Ex. : https://api.votre-domaine.com/v1/tracking
10
+ */
11
+ baseUrl: string;
12
+ /** Clé API de tracking du tenant (kundai_...). */
13
+ apiKey: string;
14
+ /** Identifiant de l'application source (défaut : "web"). */
15
+ appId?: string;
16
+ /** Timeout des requêtes HTTP en ms (défaut : 5000). */
17
+ timeoutMs?: number;
18
+ /**
19
+ * Si true, enregistre automatiquement une page-view à chaque
20
+ * changement de route Angular (NavigationEnd). Défaut : true.
21
+ */
22
+ autoPageView?: boolean;
23
+ }
24
+ /** Token d'injection de la configuration. */
25
+ export declare const KUNDAI_CONFIG: InjectionToken<KundaiConfig>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KUNDAI_CONFIG = void 0;
4
+ /**
5
+ * Configuration du module Angular KundAi (`@kundai/angular`).
6
+ */
7
+ const core_1 = require("@angular/core");
8
+ /** Token d'injection de la configuration. */
9
+ exports.KUNDAI_CONFIG = new core_1.InjectionToken('KUNDAI_CONFIG');
10
+ //# sourceMappingURL=kundai.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kundai.config.js","sourceRoot":"","sources":["file:///F:/Projet/Kundai/src/libs/angular/src/lib/kundai.config.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,wCAA+C;AAsB/C,6CAA6C;AAChC,QAAA,aAAa,GAAG,IAAI,qBAAc,CAAe,eAAe,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Module Angular KundAi (`@kundai/angular`).
3
+ *
4
+ * Intégration en 2 lignes dans n'importe quelle app Angular :
5
+ *
6
+ * ```ts
7
+ * // app.config.ts (standalone) ou AppModule
8
+ * import { KundaiModule } from '@kundai/angular';
9
+ *
10
+ * providers: [
11
+ * ...KundaiModule.forRoot({
12
+ * baseUrl: 'https://api.votre-domaine.com/v1/tracking',
13
+ * apiKey: 'kundai_xxxxxxxxxxxx',
14
+ * appId: 'mon-app',
15
+ * }),
16
+ * ]
17
+ * ```
18
+ *
19
+ * Puis dans n'importe quel composant ou service :
20
+ *
21
+ * ```ts
22
+ * private readonly kundai = inject(KundaiService);
23
+ * this.kundai.track('button_click', { plan: 'pro' });
24
+ * ```
25
+ */
26
+ import { EnvironmentProviders } from '@angular/core';
27
+ import { KundaiConfig } from './kundai.config';
28
+ export declare class KundaiModule {
29
+ /**
30
+ * Configure et fournit le module KundAi.
31
+ * À appeler dans `providers` de `app.config.ts` (standalone) ou `AppModule`.
32
+ */
33
+ static forRoot(config: KundaiConfig): EnvironmentProviders;
34
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KundaiModule = void 0;
4
+ /**
5
+ * Module Angular KundAi (`@kundai/angular`).
6
+ *
7
+ * Intégration en 2 lignes dans n'importe quelle app Angular :
8
+ *
9
+ * ```ts
10
+ * // app.config.ts (standalone) ou AppModule
11
+ * import { KundaiModule } from '@kundai/angular';
12
+ *
13
+ * providers: [
14
+ * ...KundaiModule.forRoot({
15
+ * baseUrl: 'https://api.votre-domaine.com/v1/tracking',
16
+ * apiKey: 'kundai_xxxxxxxxxxxx',
17
+ * appId: 'mon-app',
18
+ * }),
19
+ * ]
20
+ * ```
21
+ *
22
+ * Puis dans n'importe quel composant ou service :
23
+ *
24
+ * ```ts
25
+ * private readonly kundai = inject(KundaiService);
26
+ * this.kundai.track('button_click', { plan: 'pro' });
27
+ * ```
28
+ */
29
+ const core_1 = require("@angular/core");
30
+ const http_1 = require("@angular/common/http");
31
+ const kundai_config_1 = require("./kundai.config");
32
+ const kundai_service_1 = require("./kundai.service");
33
+ const kundai_router_service_1 = require("./kundai-router.service");
34
+ /** Factory APP_INITIALIZER : bootstrap du tracker au démarrage de l'app. */
35
+ function kundaiInitFactory(router) {
36
+ return () => router.bootstrap();
37
+ }
38
+ class KundaiModule {
39
+ /**
40
+ * Configure et fournit le module KundAi.
41
+ * À appeler dans `providers` de `app.config.ts` (standalone) ou `AppModule`.
42
+ */
43
+ static forRoot(config) {
44
+ return (0, core_1.makeEnvironmentProviders)([
45
+ (0, http_1.provideHttpClient)(),
46
+ { provide: kundai_config_1.KUNDAI_CONFIG, useValue: config },
47
+ kundai_service_1.KundaiService,
48
+ kundai_router_service_1.KundaiRouterService,
49
+ {
50
+ provide: core_1.APP_INITIALIZER,
51
+ useFactory: kundaiInitFactory,
52
+ deps: [kundai_router_service_1.KundaiRouterService],
53
+ multi: true,
54
+ },
55
+ ]);
56
+ }
57
+ }
58
+ exports.KundaiModule = KundaiModule;
59
+ //# sourceMappingURL=kundai.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kundai.module.js","sourceRoot":"","sources":["file:///F:/Projet/Kundai/src/libs/angular/src/lib/kundai.module.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wCAAgG;AAChG,+CAAyD;AACzD,mDAA8D;AAC9D,qDAAiD;AACjD,mEAA8D;AAE9D,4EAA4E;AAC5E,SAAS,iBAAiB,CAAC,MAA2B;IACpD,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;AAClC,CAAC;AAED,MAAa,YAAY;IACvB;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,MAAoB;QACjC,OAAO,IAAA,+BAAwB,EAAC;YAC9B,IAAA,wBAAiB,GAAE;YACnB,EAAE,OAAO,EAAE,6BAAa,EAAE,QAAQ,EAAE,MAAM,EAAE;YAC5C,8BAAa;YACb,2CAAmB;YACnB;gBACE,OAAO,EAAE,sBAAe;gBACxB,UAAU,EAAE,iBAAiB;gBAC7B,IAAI,EAAE,CAAC,2CAAmB,CAAC;gBAC3B,KAAK,EAAE,IAAI;aACZ;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAnBD,oCAmBC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Service principal du module Angular KundAi (`@kundai/angular`).
3
+ *
4
+ * Injectable partout dans l'application. Gère :
5
+ * - l'identifiant visiteur persistant (localStorage)
6
+ * - la capture des UTM depuis l'URL courante
7
+ * - le démarrage de session
8
+ * - les événements comportementaux et conversions
9
+ * - la liaison utilisateur connecté (identify)
10
+ */
11
+ import { HttpClient } from '@angular/common/http';
12
+ import { OnDestroy } from '@angular/core';
13
+ import { KundaiConfig } from './kundai.config';
14
+ export declare class KundaiService implements OnDestroy {
15
+ private readonly config;
16
+ private readonly http;
17
+ private sessionId;
18
+ private visitorId;
19
+ private userId;
20
+ private utmMeta;
21
+ private initialized;
22
+ constructor(config: KundaiConfig, http: HttpClient);
23
+ /**
24
+ * Démarre la session : capture les UTM, enregistre le click si UTM présents,
25
+ * démarre la session et enregistre la première page-view.
26
+ * Appelé automatiquement par KundaiRouterService au bootstrap.
27
+ */
28
+ init(): Promise<void>;
29
+ /** Enregistre une vue de page dans la session courante (appelé par le router). */
30
+ recordPageView(): Promise<void>;
31
+ /**
32
+ * Enregistre un événement comportemental.
33
+ *
34
+ * @example
35
+ * this.kundai.track('signup_form_view');
36
+ * this.kundai.track('button_click', { button: 'cta_hero', plan: 'pro' });
37
+ */
38
+ track(event: string, properties?: Record<string, unknown>): void;
39
+ /**
40
+ * Enregistre une conversion avec revenu (retry automatique x3).
41
+ *
42
+ * @example
43
+ * this.kundai.trackConversion({ amount: 9900, currency: 'XOF', planId: 'pro' });
44
+ */
45
+ trackConversion(opts: {
46
+ amount: number;
47
+ currency: string;
48
+ planId?: string;
49
+ event?: 'conversion' | 'purchase' | 'payment' | 'checkout';
50
+ metadata?: Record<string, unknown>;
51
+ }): void;
52
+ /**
53
+ * Lie l'utilisateur connecté à la session (à appeler après login).
54
+ *
55
+ * @example
56
+ * this.kundai.identify('user_123', 'user@example.com');
57
+ */
58
+ identify(userId: string, email?: string): Promise<void>;
59
+ /** Déconnecte l'identité locale (à appeler après logout). */
60
+ clearUser(): void;
61
+ /** Retourne le sessionId courant (utile pour le passer au backend). */
62
+ getSessionId(): string | null;
63
+ /** Retourne le visitorId courant. */
64
+ getVisitorId(): string;
65
+ private endSession;
66
+ ngOnDestroy(): void;
67
+ private baseUrl;
68
+ private headersObj;
69
+ private headers;
70
+ private post;
71
+ private withRetry;
72
+ private captureUtmFromUrl;
73
+ private campaignFields;
74
+ private readStoredUtm;
75
+ private writeStoredUtm;
76
+ private getOrCreateVisitorId;
77
+ private read;
78
+ private write;
79
+ }
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KundaiService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Service principal du module Angular KundAi (`@kundai/angular`).
7
+ *
8
+ * Injectable partout dans l'application. Gère :
9
+ * - l'identifiant visiteur persistant (localStorage)
10
+ * - la capture des UTM depuis l'URL courante
11
+ * - le démarrage de session
12
+ * - les événements comportementaux et conversions
13
+ * - la liaison utilisateur connecté (identify)
14
+ */
15
+ const http_1 = require("@angular/common/http");
16
+ const core_1 = require("@angular/core");
17
+ const rxjs_1 = require("rxjs");
18
+ const kundai_config_1 = require("./kundai.config");
19
+ /** Clés localStorage. */
20
+ const VISITOR_KEY = 'kundai_visitor_id';
21
+ const USER_KEY = 'kundai_user_id';
22
+ const UTM_KEY = 'kundai_utm_meta';
23
+ let KundaiService = class KundaiService {
24
+ config;
25
+ http;
26
+ sessionId = null;
27
+ visitorId;
28
+ userId;
29
+ utmMeta = null;
30
+ initialized = false;
31
+ constructor(config, http) {
32
+ this.config = config;
33
+ this.http = http;
34
+ this.visitorId = this.getOrCreateVisitorId();
35
+ this.userId = this.read(USER_KEY);
36
+ }
37
+ // ── Initialisation ────────────────────────────────────────────────────────
38
+ /**
39
+ * Démarre la session : capture les UTM, enregistre le click si UTM présents,
40
+ * démarre la session et enregistre la première page-view.
41
+ * Appelé automatiquement par KundaiRouterService au bootstrap.
42
+ */
43
+ async init() {
44
+ if (this.initialized)
45
+ return;
46
+ this.initialized = true;
47
+ this.utmMeta = this.readStoredUtm() ?? this.captureUtmFromUrl();
48
+ if (this.utmMeta && !this.readStoredUtm()) {
49
+ this.writeStoredUtm(this.utmMeta);
50
+ }
51
+ let clickId = null;
52
+ if (this.utmMeta) {
53
+ try {
54
+ const click = await this.post('/click', {
55
+ visitorId: this.visitorId,
56
+ landingPageUrl: window.location.href,
57
+ ...this.utmMeta,
58
+ });
59
+ clickId = click.clickId ?? null;
60
+ if (click.visitorId)
61
+ this.visitorId = click.visitorId;
62
+ }
63
+ catch {
64
+ // Un échec de click ne bloque jamais la session.
65
+ }
66
+ }
67
+ try {
68
+ const result = await this.post('/sessions', {
69
+ visitorId: this.visitorId,
70
+ appId: this.config.appId ?? 'web',
71
+ entryPageUrl: window.location.href,
72
+ clickId,
73
+ });
74
+ this.sessionId = result.sessionId;
75
+ if (result.visitorId)
76
+ this.visitorId = result.visitorId;
77
+ }
78
+ catch {
79
+ return;
80
+ }
81
+ await this.recordPageView();
82
+ window.addEventListener('pagehide', () => this.endSession());
83
+ }
84
+ // ── Page view ─────────────────────────────────────────────────────────────
85
+ /** Enregistre une vue de page dans la session courante (appelé par le router). */
86
+ async recordPageView() {
87
+ if (!this.sessionId)
88
+ return;
89
+ const dto = {
90
+ visitorId: this.visitorId,
91
+ url: window.location.href,
92
+ title: document.title,
93
+ referrerUrl: document.referrer || undefined,
94
+ viewportWidth: window.innerWidth,
95
+ viewportHeight: window.innerHeight,
96
+ };
97
+ await this.post(`/sessions/${this.sessionId}/page-view`, dto).catch(() => undefined);
98
+ }
99
+ // ── Événements ────────────────────────────────────────────────────────────
100
+ /**
101
+ * Enregistre un événement comportemental.
102
+ *
103
+ * @example
104
+ * this.kundai.track('signup_form_view');
105
+ * this.kundai.track('button_click', { button: 'cta_hero', plan: 'pro' });
106
+ */
107
+ track(event, properties) {
108
+ const dto = {
109
+ event,
110
+ visitorId: this.visitorId,
111
+ sessionId: this.sessionId ?? undefined,
112
+ userId: this.userId ?? undefined,
113
+ properties: properties ?? {},
114
+ pageUrl: window.location.href,
115
+ };
116
+ this.post('/events', dto).catch(() => undefined);
117
+ }
118
+ /**
119
+ * Enregistre une conversion avec revenu (retry automatique x3).
120
+ *
121
+ * @example
122
+ * this.kundai.trackConversion({ amount: 9900, currency: 'XOF', planId: 'pro' });
123
+ */
124
+ trackConversion(opts) {
125
+ const dto = {
126
+ event: opts.event ?? 'conversion',
127
+ visitorId: this.visitorId,
128
+ sessionId: this.sessionId ?? undefined,
129
+ userId: this.userId ?? undefined,
130
+ properties: {
131
+ amount: opts.amount,
132
+ currency: opts.currency,
133
+ planId: opts.planId ?? null,
134
+ ...(opts.metadata ?? {}),
135
+ ...this.campaignFields(),
136
+ },
137
+ };
138
+ this.withRetry(() => this.post('/events', dto)).catch(() => undefined);
139
+ }
140
+ /**
141
+ * Lie l'utilisateur connecté à la session (à appeler après login).
142
+ *
143
+ * @example
144
+ * this.kundai.identify('user_123', 'user@example.com');
145
+ */
146
+ async identify(userId, email) {
147
+ this.userId = userId;
148
+ this.write(USER_KEY, userId);
149
+ if (!this.sessionId)
150
+ await this.init();
151
+ if (!this.sessionId)
152
+ return;
153
+ const dto2 = { userId, ...(email ? { email } : {}) };
154
+ await this.post(`/sessions/${this.sessionId}/identify`, dto2).catch(() => undefined);
155
+ }
156
+ /** Déconnecte l'identité locale (à appeler après logout). */
157
+ clearUser() {
158
+ this.userId = null;
159
+ localStorage.removeItem(USER_KEY);
160
+ }
161
+ /** Retourne le sessionId courant (utile pour le passer au backend). */
162
+ getSessionId() {
163
+ return this.sessionId;
164
+ }
165
+ /** Retourne le visitorId courant. */
166
+ getVisitorId() {
167
+ return this.visitorId;
168
+ }
169
+ // ── Fin de session ────────────────────────────────────────────────────────
170
+ endSession() {
171
+ if (!this.sessionId)
172
+ return;
173
+ const sessionId = this.sessionId;
174
+ this.sessionId = null;
175
+ // fetch keepalive : fiable au déchargement de page, supporte les headers
176
+ fetch(`${this.baseUrl()}/sessions/${sessionId}/end`, {
177
+ method: 'POST',
178
+ headers: this.headersObj(),
179
+ body: JSON.stringify({ exitPageUrl: window.location.href }),
180
+ keepalive: true,
181
+ }).catch(() => undefined);
182
+ }
183
+ ngOnDestroy() {
184
+ this.endSession();
185
+ }
186
+ // ── HTTP ──────────────────────────────────────────────────────────────────
187
+ baseUrl() {
188
+ return this.config.baseUrl.replace(/\/+$/, '');
189
+ }
190
+ headersObj() {
191
+ return {
192
+ 'Content-Type': 'application/json',
193
+ 'X-API-Key': this.config.apiKey,
194
+ 'X-KundAi-App': this.config.appId ?? 'web',
195
+ };
196
+ }
197
+ headers() {
198
+ return new http_1.HttpHeaders(this.headersObj());
199
+ }
200
+ post(path, body) {
201
+ return (0, rxjs_1.firstValueFrom)(this.http.post(`${this.baseUrl()}${path}`, body, {
202
+ headers: this.headers(),
203
+ }));
204
+ }
205
+ // ── Retry ─────────────────────────────────────────────────────────────────
206
+ async withRetry(fn, attempts = 3) {
207
+ for (let i = 0; i < attempts; i++) {
208
+ try {
209
+ await fn();
210
+ return;
211
+ }
212
+ catch {
213
+ if (i < attempts - 1) {
214
+ await new Promise(r => setTimeout(r, 1000 * 2 ** i));
215
+ }
216
+ }
217
+ }
218
+ }
219
+ // ── UTM ───────────────────────────────────────────────────────────────────
220
+ captureUtmFromUrl() {
221
+ const params = new URLSearchParams(window.location.search);
222
+ const utm = {
223
+ utmSource: params.get('utm_source'),
224
+ utmMedium: params.get('utm_medium'),
225
+ utmCampaign: params.get('utm_campaign'),
226
+ utmContent: params.get('utm_content'),
227
+ utmTerm: params.get('utm_term'),
228
+ fbclid: params.get('fbclid'),
229
+ gclid: params.get('gclid'),
230
+ ttclid: params.get('ttclid'),
231
+ liclid: params.get('liclid'),
232
+ };
233
+ const hasData = Object.values(utm).some(v => v !== null);
234
+ return hasData ? utm : null;
235
+ }
236
+ campaignFields() {
237
+ if (!this.utmMeta)
238
+ return {};
239
+ return Object.fromEntries(Object.entries(this.utmMeta).filter(([, v]) => v !== null && v !== undefined));
240
+ }
241
+ readStoredUtm() {
242
+ const raw = localStorage.getItem(UTM_KEY);
243
+ if (!raw)
244
+ return null;
245
+ try {
246
+ return JSON.parse(raw);
247
+ }
248
+ catch {
249
+ return null;
250
+ }
251
+ }
252
+ writeStoredUtm(utm) {
253
+ localStorage.setItem(UTM_KEY, JSON.stringify(utm));
254
+ }
255
+ // ── Visitor ID ────────────────────────────────────────────────────────────
256
+ getOrCreateVisitorId() {
257
+ let id = localStorage.getItem(VISITOR_KEY);
258
+ if (!id) {
259
+ id = crypto.randomUUID();
260
+ localStorage.setItem(VISITOR_KEY, id);
261
+ }
262
+ return id;
263
+ }
264
+ read(key) {
265
+ return localStorage.getItem(key);
266
+ }
267
+ write(key, value) {
268
+ localStorage.setItem(key, value);
269
+ }
270
+ };
271
+ exports.KundaiService = KundaiService;
272
+ exports.KundaiService = KundaiService = tslib_1.__decorate([
273
+ (0, core_1.Injectable)(),
274
+ tslib_1.__param(0, (0, core_1.Inject)(kundai_config_1.KUNDAI_CONFIG)),
275
+ tslib_1.__metadata("design:paramtypes", [Object, http_1.HttpClient])
276
+ ], KundaiService);
277
+ //# sourceMappingURL=kundai.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kundai.service.js","sourceRoot":"","sources":["file:///F:/Projet/Kundai/src/libs/angular/src/lib/kundai.service.ts"],"names":[],"mappings":"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"}