diff --git a/web/src/app/app.module.ts b/web/src/app/app.module.ts index 36d6356e8..00e4b40be 100644 --- a/web/src/app/app.module.ts +++ b/web/src/app/app.module.ts @@ -32,6 +32,10 @@ import { getFunctions, provideFunctions, } from '@angular/fire/functions'; +import { + getAnalytics, + provideAnalytics, +} from '@angular/fire/analytics'; import { getRemoteConfig, provideRemoteConfig, @@ -83,6 +87,9 @@ import { environment } from 'environments/environment'; } return functions; }), + ...(environment.firebase?.measurementId + ? [provideAnalytics(() => getAnalytics())] + : []), provideRemoteConfig(() => getRemoteConfig()), provideStorage(() => { const storage = getStorage(); diff --git a/web/src/app/services/analytics/analytics.service.ts b/web/src/app/services/analytics/analytics.service.ts new file mode 100644 index 000000000..57f41529f --- /dev/null +++ b/web/src/app/services/analytics/analytics.service.ts @@ -0,0 +1,30 @@ +/** + * Copyright 2026 The Ground Authors. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable, Optional } from '@angular/core'; +import { Analytics, logEvent } from '@angular/fire/analytics'; + +@Injectable({ + providedIn: 'root', +}) +export class AnalyticsService { + constructor(@Optional() private analytics: Analytics) {} + + logEvent(eventName: string, params?: Record): void { + if (!this.analytics) return; + logEvent(this.analytics, eventName, params); + } +} diff --git a/web/src/app/services/survey/survey.service.ts b/web/src/app/services/survey/survey.service.ts index 60b8a7669..cd4ed5f6d 100644 --- a/web/src/app/services/survey/survey.service.ts +++ b/web/src/app/services/survey/survey.service.ts @@ -20,6 +20,7 @@ import { Observable, firstValueFrom, of } from 'rxjs'; import { Role } from 'app/models/role.model'; import { DataSharingType, Survey, SurveyState } from 'app/models/survey.model'; +import { AnalyticsService } from 'app/services/analytics/analytics.service'; import { AuthService } from 'app/services/auth/auth.service'; import { DataStoreService } from 'app/services/data-store/data-store.service'; @@ -29,7 +30,8 @@ import { DataStoreService } from 'app/services/data-store/data-store.service'; export class SurveyService { constructor( private dataStore: DataStoreService, - private authService: AuthService + private authService: AuthService, + private analyticsService: AnalyticsService ) {} loadSurvey$(id: string): Observable { @@ -125,6 +127,7 @@ export class SurveyService { description ?? '', user ); + this.analyticsService.logEvent('survey_created'); return Promise.resolve(surveyId); } diff --git a/web/src/environments/environment.test.ts b/web/src/environments/environment.test.ts index 9fa9be6d4..41bac1fb9 100644 --- a/web/src/environments/environment.test.ts +++ b/web/src/environments/environment.test.ts @@ -19,9 +19,11 @@ // `environment.test.ts`. // The list of file replacements can be found in `angular.json`. +import { FirebaseOptions } from '@angular/fire/app'; import { Env } from 'environments/environment-enums'; +import { Environment } from './environment-interface'; -export const environment = { +export const environment: Environment = { production: false, googleMapsApiKey: '', cloudFunctionsUrl: '', @@ -34,5 +36,5 @@ export const environment = { storageBucket: 'mock-storage-bucket', messagingSenderId: 'mock-messaging-sender-id', appId: 'mock-app-id', - }, + } as FirebaseOptions, };