Analytics in Power Rent
Main doc: https://billionrent.notion.site/Metrics-tree-223e1e0fbc3280c282d3f7417f0af7cd
Architecture Overview
Analytics in Power Rent is divided into two main parts:
- Client-side analytics: For tracking user interactions in the browser
- Server-side analytics: For tracking system events and server-side operations
Both parts use Mixpanel as the analytics platform for collecting and analyzing user behavior data.
Analytical Events
Server-side Events
-
invoice_created - Triggered when an invoice is created
- When creating a new invoice through the createInvoice function
- Collects information about the invoice amount, payment type, payment method, account ID, currency, and timestamp
- When creating a new invoice through the createInvoice function
-
payment_processed - Triggered when a payment is completed
- Automatically created for specific payment methods:
- Cash payments (cash)
- Upon successful completion of online payments:
- Successful completion of Stripe payment intent: A Payment Intent is successfully confirmed and the payment is completed. * For example, when a customer successfully completes a 3D Secure authentication process.
- Successful completion of Checkout payment session: handlePaymentIntentSucceeded * For example, when a customer finishes a purchase through a Stripe Checkout page from a payment link
- Successful processing of payment through Charge API
- After manual confirmation of payment status by administrator
- Collects information about amount, payment type, payment method, account ID, currency, and timestamp
-
newReservation - Triggered when a new reservation is created
- Upon successful creation of a new reservation through the createOrder function
- Collects information about the total price, total profit, dates, location information (countries and cities), vehicle ID, account ID, vehicle details (brand, model, year, category, color, seats), and distinct user ID
- Upon successful creation of a new reservation through the createOrder function
-
newReservationFailed - Triggered when reservation creation fails
- When an error occurs during the reservation creation process
- If props are provided, collects the same information as newReservation
- If props are not provided, no additional data is collected
- When an error occurs during the reservation creation process
-
vehicleCreated - Triggered when a new vehicle is created
- Upon successful creation of a new vehicle through the createVehicle function
- Collects information about vehicle ID, account ID, company name, brand, model, year, category, color, seats, and distinct user ID
- Upon successful creation of a new vehicle through the createVehicle function
-
vehicleUpdated - Triggered when a vehicle is updated
- When updating vehicle information through the updateVehicle function
- Collects the same information as when creating a vehicle
- When updating vehicle information through the updateVehicle function
-
userCreated - Triggered when a new user is created
- This event is defined in events.js but currently does not have an implementation in the strategies.js file
-
reminderCreated - Triggered when a new reminder is created
- When a new reminder is created through the createReminder function
-
reminderUpdated - Triggered when a reminder is updated
- When updating reminder information through the updateReminder function
Client-side Events
-
anagraphic_autofill_triggered - Triggered when autofill is used for anagraphic data
- When a user triggers autofill for personal information
- Collects information about the photo type, account ID, and distinct user ID
- When a user triggers autofill for personal information
-
export_clients - Triggered when clients are exported
- When a user exports client data
- Collects information about the report type, format, account ID, and distinct user ID
- When a user exports client data
-
credit_card_checked - Triggered when a credit card is checked
- When a credit card verification is performed
- Collects information about account ID and distinct user ID
- When a credit card verification is performed
-
signature_request_sent - Triggered when a signature request is sent
- When a signature request is dispatched to a client
- Collects information about the dispatch type (email, phone, whatsapp, copy_link), account ID, and distinct user ID
- When a signature request is dispatched to a client
-
send_cargos_report - Triggered when a cargos report is sent
- When a cargos report action is performed
- Collects information about the action type (send, view, invalid_credentials, missing_driver), account ID, and distinct user ID
- When a cargos report action is performed
-
send_order_data_by_email - Triggered when order data is sent by email
- When order data is sent via email
- Collects information about the status (success, error), account ID, and distinct user ID
- When order data is sent via email
-
load_vehicle_balances - Triggered when vehicle balances are loaded
- When vehicle balance data is loaded
- Collects information about account ID and distinct user ID
- When vehicle balance data is loaded
-
duplicate_client_flagged - Triggered when a potential duplicate client is identified
- When the system flags a potential duplicate client
- Collects information about the match score, account ID, and distinct user ID
- When the system flags a potential duplicate client
Technical Details
Technology Used
Analytics in Power Rent is collected using Mixpanel - a platform for user behavior analytics. Mixpanel allows tracking user actions and creating reports based on collected data.
Structure of lib/analytics Directory
lib/analytics/├── client/ # Client-side analytics implementation│ ├── analyticsBrowserTools/ # Browser-specific tools for analytics│ │ ├── AnalyticsBrowser.ts # Browser analytics initialization│ │ ├── AnalyticsContext.ts # React context for analytics│ │ └── AnalyticsProvider.jsx # React provider for analytics context│ ├── analyticsMetrics/ # Client-side metrics implementations│ │ ├── anagraphicAutofillTriggeredMetrics.ts│ │ ├── creditCardCheckedMetrics.ts│ │ ├── duplicateClientFlaggedMetrics.ts│ │ ├── exportClientsMetrics.ts│ │ ├── loadVehicleBalancesMetrics.ts│ │ ├── sendCargosReportMetrics.ts│ │ ├── sendOrderDataByEmailMetrics.ts│ │ └── signatureRequestSentMetrics.ts│ ├── events.ts # Client-side event definitions│ ├── index.ts # Main client-side analytics function│ ├── strategies.ts # Client-side event processing strategies│ └── types.ts # TypeScript types for client-side analytics├── crypt/ # Encryption utilities for analytics│ ├── cryptId.d.ts│ ├── cryptId.js│ └── cryptId.test.js└── server/ # Server-side analytics implementation ├── analyticsMetrics/ # Server-side metrics implementations │ ├── billingMetrics.js # Metrics for billing (invoices, payments) │ ├── reminderMetrics.js # Metrics for reminders │ ├── reservationMetrics.js # Metrics for reservations │ └── vehicleMetrics.js # Metrics for vehicles ├── events.js # Server-side event definitions ├── index.js # Main server-side analytics function └── strategies.js # Server-side event processing strategiesBrowser Analytics Tools
The client-side analytics implementation includes special tools for browser integration:
-
AnalyticsBrowser.ts: Initializes Mixpanel in the browser with appropriate configuration:
- Uses localStorage for persistence
- Configures autocapture settings (currently only page views are captured automatically)
- Handles initialization to ensure Mixpanel is only initialized once
-
AnalyticsContext.ts: Provides a React context to make analytics available throughout the application:
- Includes the Mixpanel instance
- Stores company ID and user ID for easy access
-
AnalyticsProvider.jsx: React provider component that initializes the analytics context:
- Retrieves company ID and user ID from GraphQL data
- Creates and configures the Mixpanel instance
- Provides the analytics context to child components
How to Add a New Event
For Server-side Events
- Step 1: Add a new event in
lib/analytics/server/events.js
type AnalyticsEvents = { // Existing events newReservation: 'new_reservation', // ... // New event myNewEvent: 'my_new_event',};
const ANALYTICS_EVENTS = Object.freeze({ // Existing events newReservation: 'new_reservation', // ... // New event myNewEvent: 'my_new_event',});- Step 2: Define the data type for the event
If the event belongs to an existing category (reservation, billing, vehicles), use the corresponding file in the server/analyticsMetrics/ directory. If a new category is required, create a new file.
Example for a new category (server/analyticsMetrics/newCategoryMetrics.js):
// @flowimport type { Mixpanel } from 'mixpanel';import { encrypt } from '@power-rent/lib/crypt';import ANALYTICS_EVENTS from '../events';
export type NewCategoryProps = {| // Define properties for your event property1: string, property2: number, // distinctId - user who triggered the event distinctId: string,|};
export type NewCategoryEvent = typeof ANALYTICS_EVENTS.myNewEvent;
export type Props = {| mixpanel: Mixpanel, event: NewCategoryEvent, props: NewCategoryProps,|};
const collectNewCategoryMetrics = ({ mixpanel, event, props }: Props): void => { mixpanel.track(event, { // Data sent to Mixpanel property1: props.property1, property2: props.property2, distinct_id: props.distinctId, timestamp: new Date().toISOString(), });};
export default collectNewCategoryMetrics;- Step 3: Update strategies in
lib/analytics/server/strategies.js
import collectNewCategoryMetrics from './analyticsMetrics/newCategoryMetrics';import type { Props as NewCategoryProps } from './analyticsMetrics/newCategoryMetrics';
export type AnalyticsProps = {| // Existing events new_reservation: ReservationProps, // ... // New event my_new_event: NewCategoryProps,|};
export type AnalyticsStrategies = {| // Existing events new_reservation: (props: ReservationProps) => void, // ... // New event my_new_event: (props: NewCategoryProps) => void,|};
const analyticsStrategies: AnalyticsStrategies = { // Existing events [ANALYTICS_EVENTS.newReservation]: collectReservationMetrics, // ... // New event [ANALYTICS_EVENTS.myNewEvent]: collectNewCategoryMetrics,};- Step 4: Use the event in server-side code
import collectAnalytics, { ANALYTICS_EVENTS } from '@power-rent/lib/analytics/server';
// ...
collectAnalytics({ analytics: mixpanelInstance, event: ANALYTICS_EVENTS.myNewEvent, props: { property1: 'value1', property2: 42, distinctId: userId, },});For Client-side Events
- Step 1: Add a new event in
lib/analytics/client/events.ts
const ANALYTICS_EVENTS = Object.freeze({ // Existing events anagraphicAutofillTriggered: 'anagraphic_autofill_triggered', // ... // New event myNewClientEvent: 'my_new_client_event',} as const);- Step 2: Define the data type for the event in
lib/analytics/client/types.ts
export interface MyNewClientEventProps extends BaseAnalyticsProps { // Define properties for your event property1: string; property2: number;}
export type EventPropsMap = { // Existing events 'anagraphicAutofillTriggered': AnagraphicAutofillTriggeredProps; // ... // New event 'myNewClientEvent': MyNewClientEventProps;}- Step 3: Create a metrics implementation file in
lib/analytics/client/analyticsMetrics/myNewClientEventMetrics.ts
import type { Mixpanel } from 'mixpanel-browser';import type { BaseAnalyticsEvent } from '../types';import type { AnalyticsEventKey } from '../events';
type Props = BaseAnalyticsEvent<'myNewClientEvent', { accountId: string; distinctId: string; property1: string; property2: number;}>;
const collectMyNewClientEventMetrics = (props: Props): void => { const { analytics, props: eventProps } = props;
analytics.track(props.event, { account_id: eventProps.accountId, distinct_id: eventProps.distinctId, property1: eventProps.property1, property2: eventProps.property2, timestamp: new Date().toISOString(), });};
export default collectMyNewClientEventMetrics;- Step 4: Update strategies in
lib/analytics/client/strategies.ts
import collectMyNewClientEventMetrics from './analyticsMetrics/myNewClientEventMetrics';import type { AnalyticsStrategy } from './types';import ANALYTICS_EVENTS from './events';
const analyticsStrategies: Record<string, AnalyticsStrategy<any>> = { // Existing strategies [ANALYTICS_EVENTS.anagraphicAutofillTriggered]: collectAnagraphicAutofillTriggeredMetrics, // ... // New strategy [ANALYTICS_EVENTS.myNewClientEvent]: collectMyNewClientEventMetrics,};- Step 5: Use the event in client-side code with React hook
import { useAnalytics } from '@power-rent/hooks/useAnalytics';import { ANALYTICS_EVENTS } from '@power-rent/lib/analytics/client';
// Inside your componentconst { trackEvent, companyId, userId } = useAnalytics();
// When you want to track the eventtrackEvent({ event: ANALYTICS_EVENTS.myNewClientEvent, props: { accountId: companyId, distinctId: userId, property1: 'value1', property2: 42, },});- Step 6: Update documentation
Add information about the new event to this file, describing when it is triggered and what data it collects.
Data Types for Existing Events
Server-side Events
Billing Events (invoice_created, payment_processed)
export type BillingProps = {| // invoice total or transaction value, string - is rounded to 2 decimal places amount: number | string, // payment type. eg: 'prepayment', 'payment', 'deposit' paymentType: string, // payment method. eg: 'stripe', 'paypal', 'bank_transfer' paymentMethod: string, // accountId hashed - companyId - for easy grouping in reporting accountId: string, // from company settings currency: string, // timestamp of the event timestamp?: string,|};Reservation Events (newReservation, newReservationFailed)
export type ReservationProps = {| totalPrice: number, totalProfit: number, dateFrom: string, dateTo: string, startCountry: string, startCity: string, endCountry: string, endCity: string, vehicleId: string, // accountId hashed - companyId - for easy grouping in reporting accountId: string, brand: string, model: string, year: number, category: string[], color: string, seats: number, // distinctId - user who triggered the event distinctId: string,|};Vehicle Events (vehicleCreated, vehicleUpdated)
export type VehicleProps = {| vehicleId: string, // accountId hashed - companyId - for easy grouping in reporting accountId: string, // pass company name where internal information is not exposed companyName: string, brand: string, model: string, year: number, category: string[], // for breakdowns color: string, seats: number, // distinctId - user who triggered the event distinctId: string,|};Client-side Events
All client-side events extend the BaseAnalyticsProps interface:
export interface BaseAnalyticsProps { // accountId hashed - companyId - for easy grouping in reporting accountId: string; // distinctId - user who triggered the event distinctId: string;}Export Clients Event
export interface ExportClientsProps extends BaseAnalyticsProps { reportType: string; format: string;}Anagraphic Autofill Triggered Event
export interface AnagraphicAutofillTriggeredProps extends BaseAnalyticsProps { photoType: string;}Credit Card Checked Event
export interface CreditCardCheckedProps extends BaseAnalyticsProps { // No additional fields. Expand in the future if needed.}Signature Request Sent Event
export interface SignatureRequestSentProps extends BaseAnalyticsProps { dispatchType: 'email' | 'phone' | 'whatsapp' | 'copy_link';}Send Cargos Report Event
export interface SendCargosReportProps extends BaseAnalyticsProps { action: 'send' | 'view' | 'invalid_credentials' | 'missing_driver';}Send Order Data By Email Event
export interface SendOrderDataByEmailProps extends BaseAnalyticsProps { status: 'success' | 'error';}Load Vehicle Balances Event
export interface LoadVehicleBalancesProps extends BaseAnalyticsProps { // No additional fields. Expand in the future if needed.}Duplicate Client Flagged Event
export interface DuplicateClientFlaggedProps extends BaseAnalyticsProps { matchScore: number;}Notes
- Analytics is divided into client-side (TypeScript) and server-side (Flow) implementations
- Client-side analytics uses React Context for easy access throughout the application
- Server-side analytics is used for system events and operations
- All analytical events should be defined in the appropriate events file (
client/events.tsorserver/events.js) - Each category of events should have a separate file in the corresponding
analyticsMetrics/directory - The
collectAnalyticsfunction automatically selects the event processing strategy based on its type - If an error occurs during analytics sending, it will be logged in Sentry
- Thanks to strict typing using TypeScript/Flow, when calling
collectAnalyticswith a specific event, the IDE will provide autocomplete forpropsproperties corresponding to that event - Typing also ensures verification of the correctness of transmitted data at compilation time, which increases code reliability
- For consistent error handling, all analytics collection calls should be wrapped in try-catch blocks
- The
distinctIdproperty is used to identify the user who triggered the event - Location data is standardized (e.g., “Italia” is converted to “Italy”) for consistency in reporting
- Browser analytics is initialized with appropriate settings for development vs. production environments