diff --git a/package-lock.json b/package-lock.json index 3cb20f1..2e14467 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,8 @@ "@angular/router": "^18.0.0", "@angular/ssr": "^18.0.0", "express": "^4.18.2", + "ng-yandex-metrika": "^17.1.2", + "ngx-google-analytics": "^14.0.1", "rxjs": "~7.8.0", "swiper": "^11.1.3", "tslib": "^2.3.0", @@ -8854,6 +8856,30 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/ng-yandex-metrika": { + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/ng-yandex-metrika/-/ng-yandex-metrika-17.1.2.tgz", + "integrity": "sha512-2VZCjEpEJvnMqjZfsRRzloaOWEQXqy6npzOBupqcm0o9VFl/SeEa2dmJN6V0HpD7GGdUFl7T9MQguti1rB4N7A==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^17.0.0", + "@angular/compiler": "^17.0.0" + } + }, + "node_modules/ngx-google-analytics": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/ngx-google-analytics/-/ngx-google-analytics-14.0.1.tgz", + "integrity": "sha512-PfOtnshSyq15EKevKlFW9IRgH+dTtPG4Q9HJYksuRNYDzjce0eqK3Bf6hz0tAZdyqbzTCyx5g+NgWBfpqQfb2w==", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@angular/common": ">=12.0.0", + "@angular/core": ">=12.0.0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", diff --git a/package.json b/package.json index 9a404c9..ab4b47e 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ "@angular/router": "^18.0.0", "@angular/ssr": "^18.0.0", "express": "^4.18.2", + "ng-yandex-metrika": "^17.1.2", + "ngx-google-analytics": "^14.0.1", "rxjs": "~7.8.0", "swiper": "^11.1.3", "tslib": "^2.3.0", diff --git a/src/app/_services/googleAnalytics.service.ts b/src/app/_services/googleAnalytics.service.ts new file mode 100644 index 0000000..8bc5ca6 --- /dev/null +++ b/src/app/_services/googleAnalytics.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@angular/core'; +import { NavigationEnd, Router } from '@angular/router'; +import { environment } from '../../environments/environment'; + +declare var gtag: Function; + +@Injectable({ + providedIn: 'root', +}) +export class GoogleAnalyticsService { + constructor(private router: Router) {} + + public initialize() { + this.onRouteChange(); + + // dynamically add analytics scripts to document head + try { + const url = 'https://www.googletagmanager.com/gtag/js?id='; + const gTagScript = document.createElement('script'); + gTagScript.async = true; + gTagScript.src = `${url}${environment.googleAnalyticsId}`; + document.head.appendChild(gTagScript); + + const dataLayerScript = document.createElement('script'); + dataLayerScript.innerHTML = ` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '${environment.googleAnalyticsId}', {'send_page_view': false});`; + document.head.appendChild(dataLayerScript); + } catch (e) { + console.error('Error adding Google Analytics', e); + } + } + + // track visited routes + private onRouteChange() { + this.router.events.subscribe((event) => { + if (event instanceof NavigationEnd) { + gtag('config', environment.googleAnalyticsId, { + page_path: event.urlAfterRedirects, + }); + console.log('Sending Google Analytics tracking for: ', event.urlAfterRedirects); + console.log('Google Analytics property ID: ', environment.googleAnalyticsId); + } + }); + } + + // use gtag.js to send Google Analytics Events + public event(action: string, eventCategory?: string, eventLabel?: string, value?: string) { + gtag('event', action, { + ...(eventCategory && { event_category: eventCategory }), + ...(eventLabel && { event_label: eventLabel }), + ...(value && { value: value }), + }); + } +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b3ed8be..00bff7f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,7 +1,9 @@ -import { Component } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import { CommonModule, DatePipe, ViewportScroller } from '@angular/common'; import { RouterOutlet, NavigationEnd, Router, RouterLink, RouterLinkActive } from '@angular/router'; import { RequestComponent } from './request/request.component'; +import {GoogleAnalyticsService} from "./_services/googleAnalytics.service"; +import {environment} from "../environments/environment"; @Component({ selector: 'app-root', @@ -10,19 +12,23 @@ import { RequestComponent } from './request/request.component'; templateUrl: './app.component.html', styleUrl: './app.component.scss' }) -export class AppComponent { +export class AppComponent implements OnInit{ title = 'NIR'; dateNow?: Date; navShow = false; - + ngOnInit(){ this.dateNow = new Date; + if (environment.production) { + this.googleAnalyticsService.initialize(); + } } constructor( readonly router: Router, readonly viewportScroller: ViewportScroller, + private readonly googleAnalyticsService: GoogleAnalyticsService ) { router.events.subscribe((event) => { diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 5da33bb..7c49e6c 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,14 +1,20 @@ -import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import {ApplicationConfig, importProvidersFrom, provideZoneChangeDetection} from '@angular/core'; import { provideRouter } from '@angular/router'; import { provideHttpClient, withFetch } from '@angular/common/http'; import { routes } from './app.routes'; import { provideClientHydration } from '@angular/platform-browser'; +import {MetrikaModule} from "ng-yandex-metrika"; export const appConfig: ApplicationConfig = { providers: [ - provideZoneChangeDetection({ eventCoalescing: true }), - provideRouter(routes), + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), provideClientHydration(), provideHttpClient(withFetch()), + importProvidersFrom( + MetrikaModule.forRoot([ + { id: 97262465, webvisor: true, accurateTrackBounce:true, clickmap:true, trackLinks:true, } + ]) + ) ] }; diff --git a/src/app/request/request.component.ts b/src/app/request/request.component.ts index ae6f1b8..8bd5a8b 100644 --- a/src/app/request/request.component.ts +++ b/src/app/request/request.component.ts @@ -3,6 +3,7 @@ import { FormsService } from '../_services/forms.service'; import {FormGroup, ReactiveFormsModule, FormBuilder, Validators} from "@angular/forms"; import {ActivatedRoute, Router} from "@angular/router"; import {NgIf} from "@angular/common"; +import {Metrika} from "ng-yandex-metrika"; @Component({ selector: 'request', @@ -22,14 +23,14 @@ export class RequestComponent implements OnInit { public utmContent:string; public utmTerm:string; - constructor(private formBuilder:FormBuilder, private formsService: FormsService, private router: ActivatedRoute) { - this.router.queryParams.subscribe(params =>{ - this.utmSource = params['utm_source']; - this.utmMedium = params['utm_medium']; - this.utmCampaign = params['utm_campaign']; - this.utmContent = params['utm_content']; - this.utmTerm = params['utm_term'] - }) + constructor(private formBuilder:FormBuilder, private formsService: FormsService, private router: ActivatedRoute, private metrika: Metrika) { + this.router.queryParams.subscribe(params =>{ + this.utmSource = params['utm_source']; + this.utmMedium = params['utm_medium']; + this.utmCampaign = params['utm_campaign']; + this.utmContent = params['utm_content']; + this.utmTerm = params['utm_term'] + }) } ngOnInit():void{ @@ -55,6 +56,7 @@ export class RequestComponent implements OnInit { open(){ this.hidden = false; this.success = false; + this.metrika.reachGoal('form-open').then(); this.ngOnInit() } @@ -68,9 +70,10 @@ export class RequestComponent implements OnInit { this.feedbackForm.markAllAsTouched(); if (this.feedbackForm.valid) this.formsService.save('model', 'feedback-form-support', null, this.feedbackForm.value).subscribe(res => { - this.success = true; - }, error => { - console.log(error); - }); + this.metrika.reachGoal('form-sent').then(); + this.success = true; + }, error => { + console.log(error); + }); } } diff --git a/src/assets/favicon.ico b/src/assets/favicon.ico index a14185f..78fe734 100644 Binary files a/src/assets/favicon.ico and b/src/assets/favicon.ico differ diff --git a/src/assets/favicon.png b/src/assets/favicon.png index 0ee467f..1ea167e 100644 Binary files a/src/assets/favicon.png and b/src/assets/favicon.png differ diff --git a/src/assets/favicon.svg b/src/assets/favicon.svg index 9d5bc33..dc331b2 100644 --- a/src/assets/favicon.svg +++ b/src/assets/favicon.svg @@ -1,5 +1,4 @@ - - - + + diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts index f56418b..9d07f81 100644 --- a/src/environments/environment.development.ts +++ b/src/environments/environment.development.ts @@ -4,4 +4,5 @@ export const environment = { clientId: 2, clientSecret: 'YllFbaRHMP0kCJLb0UdCcOfpTzA23ea3AOdIfRMj', domenUrl: 'http://localhost:4200', - }; \ No newline at end of file + googleAnalyticsId: 'G-B8183299MH', + }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 7f343ad..141397d 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,4 +4,5 @@ export const environment = { clientId: 2, clientSecret: 'KIWaOS7ML1ZEUmgByFN5Cf9wf0pHFWjYJ5rmOboX', domenUrl: 'https://nirgroup.ru', + googleAnalyticsId: 'G-B8183299MH', };