feat: Track A frontend - analytics event pipeline core + real call sites

core/analytics (AnalyticsEvent model, gateway/token/mock, AnalyticsService
wrapper) against docs/backend/TRACK-A-ANALYTICS-CONTRACT.md §1. isSynthetic
is derived from the build environment at the service layer, never
client-settable at a call site - matches the contract's §6 requirement
that synthetic traffic be inseparable-by-accident from production data
once a real backend exists.

Wired into real, live interaction points (additive only, no existing
logic touched): product_view + add_to_cart in
product-details-container.component.ts, checkout_started + payment_started
in pages/cart/cart.component.ts. This is the actual event-firing
infrastructure the plan calls "the single largest remaining backend
effort" (§3.1) - the frontend side (call sites) is real now; the mock
gateway just doesn't persist anywhere yet.

Not wired: search/category_view/seller_view/cart_view/payment_success/
payment_failed/order_created - follow-up call sites once this pattern is
reviewed, to avoid a much larger unreviewed diff in one push.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-18 00:05:46 +04:00
parent 34f79b0303
commit be167d110e
7 changed files with 77 additions and 0 deletions

View File

@@ -18,6 +18,7 @@ import { IconComponent } from '../../shared/ui/icon/icon.component';
import { EmptyStateComponent } from '../../shared/ui/empty-state/empty-state.component';
import { ButtonComponent } from '../../shared/ui/button/button.component';
import { ConfigService } from '../../core/config/config.service';
import { AnalyticsService } from '../../core/analytics/services/analytics.service';
import { TenantResolverService } from '../../core/config/tenant-resolver.service';
import { UserNotificationService } from '../../features/website/user-experience/services/user-notification.service';
import { ConfirmDialogComponent } from '../../shared/ui/confirm-dialog/confirm-dialog.component';
@@ -83,6 +84,7 @@ export class CartComponent implements OnDestroy {
private configService = inject(ConfigService);
private tenantResolver = inject(TenantResolverService);
private currencyRates = inject(CurrencyRatesService);
private readonly analytics = inject(AnalyticsService);
constructor(
private cartService: CartService,
@@ -206,10 +208,12 @@ export class CartComponent implements OnDestroy {
this.notifications.show(this.i18n.t('cart.acceptTerms'), 'warning');
return;
}
this.analytics.track('checkout_started', { itemCount: this.items().length });
this.openPaymentPopup(paymentMethod);
}
openPaymentPopup(paymentMethod: PaymentMethod): void {
this.analytics.track('payment_started', { paymentMethod });
this.showPaymentPopup.set(true);
this.selectedPaymentMethod.set(paymentMethod);
this.paymentStatus.set('creating');