@@ -3,11 +3,66 @@ import Evented from '@ember/object/evented';
33import config from 'ember-get-config' ;
44
55/**
6- * Events Service
6+ * EventsService
77 *
8- * Provides a centralized event tracking system for Fleetbase.
9- * This service emits standardized events on both its own event bus and the universe service,
10- * allowing components, services, and engines to subscribe and react to application events.
8+ * Provides a centralized, standardized event tracking system for Fleetbase.
9+ *
10+ * This service is the single source of truth for all application lifecycle
11+ * events. It emits every event on two buses simultaneously:
12+ *
13+ * 1. Its own Evented bus — for direct service/component listeners:
14+ * this.events.on('user.loaded', handler)
15+ *
16+ * 2. The universe service bus — for cross-engine listeners (recommended
17+ * for use in Ember Engines / extensions):
18+ * this.universe.on('user.loaded', handler)
19+ *
20+ * ─────────────────────────────────────────────────────────────────────────────
21+ * Session Lifecycle Events
22+ * ─────────────────────────────────────────────────────────────────────────────
23+ *
24+ * session.authenticated Fired after a successful login or session restore.
25+ * Payload: (properties)
26+ *
27+ * session.invalidated Fired after the session is destroyed (logout).
28+ * Payload: (duration_seconds, properties)
29+ *
30+ * session.terminated Alias for session.invalidated — provided for
31+ * backward compatibility and semantic clarity.
32+ * Payload: (duration_seconds, properties)
33+ *
34+ * user.loaded Fired after the authenticated user record and
35+ * organization have been fully loaded into the
36+ * currentUser service. This is the canonical event
37+ * for integrations to boot (Intercom, PostHog, etc.)
38+ * Payload: (user, organization, properties)
39+ *
40+ * user.updated Fired when the user record is refreshed in-session
41+ * (e.g. after a profile edit).
42+ * Payload: (user, properties)
43+ *
44+ * user.deauthenticated Fired when the user identity is cleared on logout.
45+ * Semantic alias for session.invalidated — use this
46+ * to shut down integrations cleanly (Intercom, etc.)
47+ * Payload: (duration_seconds, properties)
48+ *
49+ * user.organization_switched Fired when the user switches their active org.
50+ * Payload: (organization, properties)
51+ *
52+ * ─────────────────────────────────────────────────────────────────────────────
53+ * Resource Events
54+ * ─────────────────────────────────────────────────────────────────────────────
55+ *
56+ * resource.created Generic resource creation.
57+ * resource.updated Generic resource update.
58+ * resource.deleted Generic resource deletion.
59+ * resource.imported Bulk import.
60+ * resource.exported Export.
61+ * resource.bulk_action Bulk action (delete, archive, etc.)
62+ * {modelName}.created Model-specific creation (e.g. order.created)
63+ * {modelName}.updated Model-specific update.
64+ * {modelName}.deleted Model-specific deletion.
65+ * {modelName}.exported Model-specific export.
1166 *
1267 * @class EventsService
1368 * @extends Service
@@ -16,6 +71,108 @@ export default class EventsService extends Service.extend(Evented) {
1671 @service universe ;
1772 @service currentUser ;
1873
74+ // =========================================================================
75+ // Session Lifecycle Tracking
76+ // =========================================================================
77+
78+ /**
79+ * Tracks a successful authentication (login or session restore).
80+ *
81+ * Called by SessionService.handleAuthentication().
82+ *
83+ * @param {Object } [props={}] - Additional properties to include
84+ */
85+ trackSessionAuthenticated ( props = { } ) {
86+ const properties = this . #enrichProperties( props ) ;
87+ this . #trigger( 'session.authenticated' , properties ) ;
88+ }
89+
90+ /**
91+ * Tracks when a user session is terminated (logout).
92+ *
93+ * Called by SessionService.handleInvalidation(). Also fires the semantic
94+ * `user.deauthenticated` event so integrations can react to the user
95+ * identity being cleared without needing to know about session internals.
96+ *
97+ * @param {Number|null } duration - Session duration in seconds (null if unknown)
98+ * @param {Object } [props={}] - Additional properties to include
99+ */
100+ trackSessionTerminated ( duration , props = { } ) {
101+ const properties = this . #enrichProperties( {
102+ session_duration : duration ,
103+ ...props ,
104+ } ) ;
105+
106+ // Fire session.invalidated (technical event)
107+ this . #trigger( 'session.invalidated' , duration , properties ) ;
108+
109+ // Fire session.terminated (backward-compatible alias)
110+ this . #trigger( 'session.terminated' , duration , properties ) ;
111+
112+ // Fire user.deauthenticated (semantic event for integrations)
113+ this . #trigger( 'user.deauthenticated' , duration , properties ) ;
114+ }
115+
116+ /**
117+ * Tracks when the current user is loaded (session initialized).
118+ *
119+ * Called by CurrentUserService.setUser() after a successful login or
120+ * session restore. This is the canonical event for integrations to boot.
121+ *
122+ * @param {Object } user - The authenticated user model
123+ * @param {Object } organization - The user's active organization model
124+ * @param {Object } [props={}] - Additional properties to include
125+ */
126+ trackUserLoaded ( user , organization , props = { } ) {
127+ const properties = this . #enrichProperties( {
128+ user_id : user ?. id ,
129+ organization_id : organization ?. id ,
130+ organization_name : organization ?. name ,
131+ ...props ,
132+ } ) ;
133+
134+ this . #trigger( 'user.loaded' , user , organization , properties ) ;
135+ }
136+
137+ /**
138+ * Tracks when the user record is refreshed in-session (e.g. profile edit).
139+ *
140+ * Called by CurrentUserService.refreshUser().
141+ *
142+ * @param {Object } user - The updated user model
143+ * @param {Object } [props={}] - Additional properties to include
144+ */
145+ trackUserUpdated ( user , props = { } ) {
146+ const properties = this . #enrichProperties( {
147+ user_id : user ?. id ,
148+ ...props ,
149+ } ) ;
150+
151+ this . #trigger( 'user.updated' , user , properties ) ;
152+ }
153+
154+ /**
155+ * Tracks when the user switches their active organization.
156+ *
157+ * Called by CurrentUserService.switchOrganization().
158+ *
159+ * @param {Object } organization - The new active organization model
160+ * @param {Object } [props={}] - Additional properties to include
161+ */
162+ trackOrganizationSwitched ( organization , props = { } ) {
163+ const properties = this . #enrichProperties( {
164+ organization_id : organization ?. id ,
165+ organization_name : organization ?. name ,
166+ ...props ,
167+ } ) ;
168+
169+ this . #trigger( 'user.organization_switched' , organization , properties ) ;
170+ }
171+
172+ // =========================================================================
173+ // Resource Event Tracking
174+ // =========================================================================
175+
19176 /**
20177 * Tracks the creation of a resource
21178 *
@@ -131,38 +288,9 @@ export default class EventsService extends Service.extend(Evented) {
131288 this . #trigger( 'resource.bulk_action' , verb , resources , firstResource , properties ) ;
132289 }
133290
134- /**
135- * Tracks when the current user is loaded (session initialized)
136- *
137- * @param {Object } user - The user object
138- * @param {Object } organization - The organization object
139- * @param {Object } [props={}] - Additional properties to include
140- */
141- trackUserLoaded ( user , organization , props = { } ) {
142- const properties = this . #enrichProperties( {
143- user_id : user ?. id ,
144- organization_id : organization ?. id ,
145- organization_name : organization ?. name ,
146- ...props ,
147- } ) ;
148-
149- this . #trigger( 'user.loaded' , user , organization , properties ) ;
150- }
151-
152- /**
153- * Tracks when a user session is terminated
154- *
155- * @param {Number } duration - Session duration in seconds
156- * @param {Object } [props={}] - Additional properties to include
157- */
158- trackSessionTerminated ( duration , props = { } ) {
159- const properties = this . #enrichProperties( {
160- session_duration : duration ,
161- ...props ,
162- } ) ;
163-
164- this . #trigger( 'session.terminated' , duration , properties ) ;
165- }
291+ // =========================================================================
292+ // Generic Event Tracking
293+ // =========================================================================
166294
167295 /**
168296 * Tracks a generic custom event
@@ -190,11 +318,11 @@ export default class EventsService extends Service.extend(Evented) {
190318 // =========================================================================
191319
192320 /**
193- * Triggers an event on both the events service and universe service
321+ * Triggers an event on both the events service and universe service.
194322 *
195323 * This dual event system allows listeners to subscribe to events on either:
196- * - this.events.on('event.name', handler) - Local listeners
197- * - this.universe.on('event.name', handler) - Cross -engine listeners
324+ * - this.events.on('event.name', handler) — local listeners
325+ * - this.universe.on('event.name', handler) — cross -engine listeners
198326 *
199327 * @private
200328 * @param {String } eventName - The event name
0 commit comments