Skip to content

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

  1. invoice_created - Triggered when an invoice is created

    1. When creating a new invoice through the createInvoice function
      • Collects information about the invoice amount, payment type, payment method, account ID, currency, and timestamp
  2. payment_processed - Triggered when a payment is completed

    1. Automatically created for specific payment methods:
    • Cash payments (cash)
    1. 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
    1. After manual confirmation of payment status by administrator
      • Collects information about amount, payment type, payment method, account ID, currency, and timestamp
  3. newReservation - Triggered when a new reservation is created

    1. 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
  4. newReservationFailed - Triggered when reservation creation fails

    1. 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
  5. vehicleCreated - Triggered when a new vehicle is created

    1. 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
  6. vehicleUpdated - Triggered when a vehicle is updated

    1. When updating vehicle information through the updateVehicle function
      • Collects the same information as when creating a vehicle
  7. userCreated - Triggered when a new user is created

    1. This event is defined in events.js but currently does not have an implementation in the strategies.js file
  8. reminderCreated - Triggered when a new reminder is created

    1. When a new reminder is created through the createReminder function
  9. reminderUpdated - Triggered when a reminder is updated

    1. When updating reminder information through the updateReminder function

Client-side Events

  1. anagraphic_autofill_triggered - Triggered when autofill is used for anagraphic data

    1. When a user triggers autofill for personal information
      • Collects information about the photo type, account ID, and distinct user ID
  2. export_clients - Triggered when clients are exported

    1. When a user exports client data
      • Collects information about the report type, format, account ID, and distinct user ID
  3. credit_card_checked - Triggered when a credit card is checked

    1. When a credit card verification is performed
      • Collects information about account ID and distinct user ID
  4. signature_request_sent - Triggered when a signature request is sent

    1. 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
  5. send_cargos_report - Triggered when a cargos report is sent

    1. 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
  6. send_order_data_by_email - Triggered when order data is sent by email

    1. When order data is sent via email
      • Collects information about the status (success, error), account ID, and distinct user ID
  7. load_vehicle_balances - Triggered when vehicle balances are loaded

    1. When vehicle balance data is loaded
      • Collects information about account ID and distinct user ID
  8. duplicate_client_flagged - Triggered when a potential duplicate client is identified

    1. When the system flags a potential duplicate client
      • Collects information about the match score, account ID, and distinct user ID

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 strategies

Browser Analytics Tools

The client-side analytics implementation includes special tools for browser integration:

  1. 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
  2. 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
  3. 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

  1. 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',
});
  1. 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):

// @flow
import 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;
  1. 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,
};
  1. 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

  1. 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);
  1. 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;
}
  1. 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;
  1. 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,
};
  1. 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 component
const { trackEvent, companyId, userId } = useAnalytics();
// When you want to track the event
trackEvent({
event: ANALYTICS_EVENTS.myNewClientEvent,
props: {
accountId: companyId,
distinctId: userId,
property1: 'value1',
property2: 42,
},
});
  1. 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.ts or server/events.js)
  • Each category of events should have a separate file in the corresponding analyticsMetrics/ directory
  • The collectAnalytics function 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 collectAnalytics with a specific event, the IDE will provide autocomplete for props properties 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 distinctId property 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