diff --git a/angular.json b/angular.json index 6f10acf..72002b0 100644 --- a/angular.json +++ b/angular.json @@ -79,7 +79,13 @@ "development": { "optimization": false, "extractLicenses": false, - "sourceMap": true + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] } }, "defaultConfiguration": "production" diff --git a/package-lock.json b/package-lock.json index d04959c..3c5eadc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@angular/router": "^19.1.7", "@tailwindcss/postcss": "^4.0.9", "aos": "^2.3.4", + "posthog-js": "^1.225.1", "rxjs": "~7.8.2", "tslib": "^2.8.1", "zone.js": "~0.15.0" @@ -4727,6 +4728,16 @@ "win32" ] }, + "node_modules/@rrweb/types": { + "version": "2.0.0-alpha.17", + "resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.0.0-alpha.17.tgz", + "integrity": "sha512-AfDTVUuCyCaIG0lTSqYtrZqJX39ZEYzs4fYKnexhQ+id+kbZIpIJtaut5cto6dWZbB3SEe4fW0o90Po3LvTmfg==", + "license": "MIT", + "peer": true, + "dependencies": { + "rrweb-snapshot": "^2.0.0-alpha.17" + } + }, "node_modules/@schematics/angular": { "version": "19.1.8", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.1.8.tgz", @@ -6887,6 +6898,17 @@ "webpack": "^5.1.0" } }, + "node_modules/core-js": { + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", + "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.40.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", @@ -7935,6 +7957,12 @@ "node": ">=0.8.0" } }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -12010,6 +12038,31 @@ "dev": true, "license": "MIT" }, + "node_modules/posthog-js": { + "version": "1.225.1", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.225.1.tgz", + "integrity": "sha512-JO12aGlKbznD91osyNPgsQXP4jwLYdDtGrEL6idjGBb4ETFpud6tfe2Jdaow3672c3fNjFFNyu2ybXC3793VOA==", + "license": "MIT", + "dependencies": { + "core-js": "^3.38.1", + "fflate": "^0.4.8", + "preact": "^10.19.3", + "web-vitals": "^4.2.0" + }, + "peerDependencies": { + "@rrweb/types": "2.0.0-alpha.17" + } + }, + "node_modules/preact": { + "version": "10.26.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.4.tgz", + "integrity": "sha512-KJhO7LBFTjP71d83trW+Ilnjbo+ySsaAgCfXOXUlmGzJ4ygYPWmysm77yg4emwfmoz3b22yvH5IsVFHbhUaH5w==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/proc-log": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", @@ -12502,6 +12555,16 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrweb-snapshot": { + "version": "2.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-2.0.0-alpha.18.tgz", + "integrity": "sha512-hBHZL/NfgQX6wO1D9mpwqFu1NJPpim+moIcKhFEjVTZVRUfCln+LOugRc4teVTCISYHN8Cw5e2iNTWCSm+SkoA==", + "license": "MIT", + "peer": true, + "dependencies": { + "postcss": "^8.4.38" + } + }, "node_modules/run-applescript": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", @@ -14832,6 +14895,12 @@ "license": "MIT", "optional": true }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" + }, "node_modules/webpack": { "version": "5.97.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", diff --git a/package.json b/package.json index 315a041..adcc7a6 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@angular/router": "^19.1.7", "@tailwindcss/postcss": "^4.0.9", "aos": "^2.3.4", + "posthog-js": "^1.225.1", "rxjs": "~7.8.2", "tslib": "^2.8.1", "zone.js": "~0.15.0" diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 315ca01..aea51b6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,11 +1,12 @@ // src/app/app.component.ts import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; +import { NavigationEnd, Router, RouterOutlet } from '@angular/router'; import * as AOS from 'aos'; import { OverlayService } from './services/overlay.service'; -import { Observable } from 'rxjs'; +import { filter, Observable } from 'rxjs'; import { LoadingSpinnerComponent } from './components/loading-spinner.component'; +import { PosthogService } from './services/posthog.service'; @Component({ selector: 'app-root', @@ -45,7 +46,7 @@ export class AppComponent { isLoading$: Observable; isSuccess$: Observable; - constructor(private overlayService: OverlayService) { + constructor(private overlayService: OverlayService,private router: Router, private posthogService: PosthogService) { this.isLoading$ = this.overlayService.loading$; this.isSuccess$ = this.overlayService.success$; } @@ -55,6 +56,11 @@ export class AppComponent { duration: 1000, once: true, }); + this.router.events + .pipe(filter(event => event instanceof NavigationEnd)) + .subscribe((event: NavigationEnd) => { + this.posthogService.capture('$pageview', { path: event.urlAfterRedirects }); + }); } ngAfterViewInit(): void { diff --git a/src/app/services/posthog.service.ts b/src/app/services/posthog.service.ts new file mode 100644 index 0000000..ef7e80a --- /dev/null +++ b/src/app/services/posthog.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import posthog from 'posthog-js'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class PosthogService { + constructor() { + // Initialisierung kann alternativ hier statt in main.ts erfolgen + posthog.init(environment.POSTHOG_KEY, { + api_host: environment.POSTHOG_HOST, + capture_pageview: false // Wir steuern Seitenaufrufe manuell (siehe Schritt 4) + }); + } + + // Methode zum Erfassen eines benutzerdefinierten Events + capture(eventName: string, properties?: Record) { + posthog.capture(eventName, properties); + } + + // Benutzer identifizieren + identify(userId: string, properties?: Record) { + posthog.identify(userId, properties); + } + + // Feature Flags prüfen + isFeatureEnabled(flagKey: string): boolean { + return posthog.isFeatureEnabled(flagKey) || false; + } +} \ No newline at end of file diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts new file mode 100644 index 0000000..909c155 --- /dev/null +++ b/src/environments/environment.development.ts @@ -0,0 +1,4 @@ +export const environment = { + POSTHOG_KEY:"phc_kA3JWHlvuSSi4QIAHastC1hETfCn1B8L1GZuoo281dO", + POSTHOG_HOST:"https://eu.i.posthog.com" +}; \ No newline at end of file diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000..da7cbee --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,4 @@ +export const environment = { + POSTHOG_KEY:"phc_kA3JWHlvuSSi4QIAHastC1hETfCn1B8L1GZuoo281dO", + POSTHOG_HOST:"https://eu.i.posthog.com" +};