Appearance
Amplitude Analytics ​
Overview ​
Amplitude is the primary product analytics platform in the Admin-Frontend. It tracks user behaviour across every DAM page — asset views, uploads, searches, bulk actions, image editing, portal configuration, member management, and more.
The implementation has two layers:
plugins/amplitude.js— initialises the SDK, adds a context-enrichment plugin that stamps every event with workspace/page context, and exposes$amplitudeon every componentmixins/amplitude-analytics.js— provides typed tracking helper methods (trackActivity,trackImageEditor,trackFilterChange, etc.) consumed by pages and components
Setup & Initialisation ​
plugins/amplitude.js runs client-side only. On load it:
- Calls
amplitude.init(apiKey, options)with the key from$config.amplitude.apiKeyorAMPLITUDE_API_KEYenv var - Registers a context-enrichment plugin that auto-stamps every event (custom and autocaptured) with:
workspace_id,workspace_name(from live route params)page_name,page_path(fromvue-router)origin_url,environment('admin'or'anonymous')
- Initialises Session Replay once
deviceIdandsessionIdare ready - Watches auth state — calls
identifyFromAuthUseron login,reseton logout
Autocapture settings ​
| Feature | Status | Reason |
|---|---|---|
| Page views | ON | Standard |
| Sessions | ON | Standard |
| Attribution | ON | Standard |
| Element interactions | ON | By product decision — watch quota |
| Frustration interactions | ON | Rage/dead clicks, low volume |
| Web Vitals | ON | Performance |
| Form interactions | OFF | Dialog-heavy UI; too noisy |
| File downloads | OFF | Downloads use StreamSaver/blob, not anchors |
| Network tracking | ON (failures only, own hosts) | API errors; S3 presigned URLs excluded |
Event batching ​
Most events are queued client-side (batch size 10, flush after 5 s). The following events bypass the queue and fire immediately:
user logged-inuser login-failed
Note:
Asset Upload CompletedandPayment Completedappear only in the bypass allowlist inplugins/amplitude.jsbut are never fired from the frontend — they are reserved for future use.
User Identification ​
Called automatically on login via amplitudeAnalytics.identifyFromAuthUser(user).
javascript
// User properties sent to Amplitude (PII hashed before sending)
{
email_hash: '<sha256>', // email hashed — never raw email
name_hash: '<sha256>', // name hashed — never raw name
user_type: 'admin' | 'user',
subscription_user: true | false,
created_at: '...',
workspace_id: 42,
default_workspace_id: 42,
workspace_unique_id: 'abc123',
workspace_name: 'ACME Brand',
workspace_type: 'standard',
is_trial_account: false,
is_suspended: 0,
screen_resolution: '1920x1080'
}PII rule:
nameare hashed with SHA-256 (falls back to a fast integer hash on non-secure origins likehttp://localhost) before being sent to Amplitude. Raw PII never leaves the browser.
Custom Events Reference ​
All custom events are fired via this.$amplitude.track(eventName, properties) — either directly or through the mixin helpers. The context-enrichment plugin adds workspace_id, page_path, page_name, origin_url, and environment to every event automatically.
Naming pattern ​
"{entity} {verb}" → "asset viewed", "folder created"
"{entity} {noun}-{verb}" → "asset tags-added", "collage asset-removed"
"user {verb}" → "user logged-in", "user profile-updated"
"search {verb}" → "search performed", "search result-clicked"
"asset editor-{verb}" → "asset editor-opened", "asset editor-cropped"
"asset custom-fields-{kind}" → "asset custom-fields-added"
"member {verb}" → "member invited", "member deactivated"
"workspace {noun}-{verb}" → "workspace domain-updated"
"portal {noun}-{verb}" → "portal banner-toggled", "portal created"Asset events ​
All fired via trackActivity({ event: 'asset', sub_event: '...' }).
| Event name | Trigger | Key properties |
|---|---|---|
asset viewed | Asset card clicked / quick-view opened / detail page loaded | asset_id, category_id?, collection_id? |
asset downloaded | Asset download button clicked | asset_id, collection_id? |
asset added | Upload completes | asset_id, source |
asset version-added | New version uploaded for an existing asset | asset_id |
asset update | Asset metadata saved | asset_id, fields_changed? |
asset deleted | Asset deleted (single or bulk) | asset_id |
asset archived | Asset archived | asset_id |
asset moved | Asset moved to another folder | asset_id, category_id (destination) |
asset duplicated | Asset duplicated | asset_id |
asset shared | Share link created | asset_id, share_id |
asset embedded | Embed code generated | asset_id, share_id |
asset embed-removed | Embed link removed from sharing management | asset_id, share_id |
asset converted | File format converted | asset_id |
asset tags-added | Tags added to asset | asset_id, tag_id? |
asset tags-removed | Tags removed from asset | asset_id, tag_id? |
asset visibility-updated | Asset portal visibility changed (portals added or removed) | asset_id, portal_id, action ('added'/'removed') |
asset version-previewed | Version previewed in Versions panel | asset_id, version_id |
asset version-downloaded | Specific version downloaded from Versions panel | asset_id, version_id |
asset version-deleted | Asset version deleted | asset_id, version_id |
asset version-restored | Asset version restored | asset_id, version_id |
asset upload-failed | Upload error | asset_id?, source |
Examples:
javascript
// Single asset viewed from a folder
this.trackActivity({
event: 'asset',
sub_event: 'viewed',
asset_id: [123],
category_id: [456],
})
// Bulk delete (3 assets)
this.trackActivity(
{ event: 'asset', sub_event: 'deleted', asset_id: [1, 2, 3] },
{ ...this.selectionMethodFor(3) } // adds selection_method: 'shift-click' etc.
)
// Portal visibility change (via trackVisibilityChange helper)
this.trackVisibilityChange(
{ event: 'asset', sub_event: 'visibility-updated', asset_id: [assetId] },
[{ id: 1 }, { id: 2 }], // portals before
[{ id: 2 }, { id: 3 }, { id: 4 }], // portals after
)
// Fires:
// "asset visibility-updated" { asset_id: ['42'], portal_id: ['3','4'], action: 'added' }
// "asset visibility-updated" { asset_id: ['42'], portal_id: ['1'], action: 'removed' }Collage events ​
| Event name | Trigger | Key properties |
|---|---|---|
collage viewed | Collage detail page loaded | collection_id |
collage downloaded | Collage downloaded | collection_id, asset_id[] |
collage created | New collage created | collection_id, asset_id[]? |
collage update | Collage renamed/updated | collection_id, fields_changed? |
collage deleted | Collage deleted | collection_id |
collage asset-added | Asset added to collage | collection_id, asset_id |
collage asset-removed | Asset removed from collage | collection_id, asset_id |
collage visibility-updated | Collage portal visibility changed | collection_id, portal_id, action |
collage shared | Collage share link created | collection_id, share_id |
Folder events ​
| Event name | Trigger | Key properties |
|---|---|---|
folder viewed | Folder page loaded | category_id |
folder downloaded | Folder assets downloaded | category_id |
folder created | New folder created | category_id |
folder update | Folder renamed/updated | category_id, fields_changed? |
folder deleted | Folder deleted | category_id, assets_affected, subfolders_affected |
folder moved | Folder moved | category_id |
folder shared | Folder share link created | category_id, share_id |
folder visibility-updated | Folder portal visibility changed | category_id, portal_id, action |
javascript
// Bulk folder delete — includes cascade size
const cascade = this.cascadeSize(foldersToDelete)
this.trackActivity(
{ event: 'folder', sub_event: 'deleted', category_id: folderIds },
cascade // adds assets_affected, subfolders_affected
)Shared / anonymous events ​
Used on external share pages. environment is always 'anonymous'.
| Event name | Trigger | Key properties |
|---|---|---|
shared viewed | Anonymous user views shared asset | share_id, asset_id? |
shared downloaded | Anonymous user downloads shared asset | share_id, asset_id? |
shared link-updated | Share link settings updated (expiry, password, etc.) | share_id |
shared password-verified | Share password entered correctly | share_id |
shared password-failed | Share password entered incorrectly | share_id |
Image editor events ​
Tracked via trackImageEditor(verb, properties) — goes directly to $amplitude.track. Event name: "asset editor-{verb}".
| Event name | When fired |
|---|---|
asset editor-opened | User opens the image editor tab on an asset |
asset editor-cropped | User applies a crop |
asset editor-rotated | User rotates the image |
asset editor-flipped | User flips horizontally or vertically |
asset editor-resized | User resizes the image |
asset editor-downloaded | User exports / saves the edited image |
asset editor-reset | User resets to the original |
asset editor-format-changed | User changes the output format |
javascript
// Editor opened
this.trackImageEditor('opened', {
asset_id: this.file.id,
file_type: 'image/jpeg',
})
// After applying a crop
this.trackImageEditor('cropped', { asset_id: this.assetId })
// After rotating
this.trackImageEditor('rotated', { asset_id: this.assetId })Custom fields events ​
Tracked via trackCustomFieldsUpdated(assetIds, fields, isMultiple, forcedKind). Groups changes by kind and fires one event per group.
| Event name | When |
|---|---|
asset custom-fields-added | A custom field value is set for the first time |
asset custom-fields-updated | An existing custom field value is changed |
asset custom-fields-removed | A custom field value is cleared |
javascript
this.trackCustomFieldsUpdated(
[asset.id], // asset IDs
changedFields, // array of field objects from API
false, // isMultiple — true for bulk edits
)
// Fires e.g.:
// "asset custom-fields-added" { asset_id: ['42'], fields_changed: ['Brand'], field_count: 1, field_values: ['Brand=ACME'] }Search events ​
| Event name | When | Properties |
|---|---|---|
search performed | Search executes (on query change or filter update) | query, has_query, result_count_assets, result_count_collages, result_count_folders, result_count_total, has_results, active_filter_types, filter_count, active_tab, trigger |
search result-clicked | User clicks a search result | result_type ('asset'/'folder'/'collage'), rank (1-based position), results_loaded |
search filter-applied | User adds or removes a search filter | filter_type, action ('add'/'remove'), filter_count_after |
javascript
// Fired directly via $amplitude.track in search.vue
this.$amplitude.track('search performed', {
query: 'brand logo',
has_query: true,
result_count_assets: 12,
result_count_collages: 2,
result_count_folders: 1,
result_count_total: 15,
has_results: true,
active_filter_types: ['file_type'],
filter_count: 1,
active_tab: 'assets',
trigger: 'keystroke',
})View mode event ​
| Event name | Properties |
|---|---|
user view-mode-changed | from_mode, to_mode, scope (page identifier) |
javascript
this.trackViewModeChange('list', 'grid', 'folders-detail')
// → "user view-mode-changed" { from_mode: 'grid', to_mode: 'list', scope: 'folders-detail' }The current view mode is also stored as an Amplitude user property (view_mode) via setViewModeProperty.
Trash events ​
| Event name | When | Properties |
|---|---|---|
trash permanently-deleted | User empties trash or permanently deletes selected items | — |
trash restored | User restores items from trash | — |
User / auth events ​
Fired via direct $amplitude.track calls on auth pages and profile settings.
| Event name | When | Source |
|---|---|---|
user logged-in | Successful login (password, social, or external verify) | pages/index.vue, pages/social-login.vue, pages/.../external/verify.vue |
user login-failed | Failed login attempt | same |
user password-reset-requested | Forgot-password form submitted | pages/forgot-password.vue |
user password-reset-request-failed | Forgot-password request failed | same |
user password-set | Password set on the password-setup page | pages/password-setup.vue |
user invitation-accepted | User accepts an invitation via generate-password | pages/generate-password.vue |
user invitation-accept-failed | Invitation accept failed | same |
user access-requested | External user submits access request | pages/.../external/request-access.vue |
user access-request-failed | Access request failed | same |
user profile-updated | User updates their profile settings | components/dam/Profile/ProfileSetting.vue |
user password-changed | User changes their password from profile | same |
user profile-image-updated | User uploads a new profile photo | components/dam/Dialogs/Org-Settings/LogoEditDialog.vue |
user notification-pref-updated | User changes notification preferences | components/dam/Profile/NotificationSetting.vue |
user passkey-added | Passkey registered | components/dam/Profile/ProfileSetting.vue |
user passkey-add-failed | Passkey registration failed | same |
user passkey-removed | Passkey removed | same |
user support-pin-generated | Support PIN generated | same |
user support-session-terminated | Support session terminated | same |
user support-sessions-cleared | All support sessions cleared | same |
user view-mode-changed | User switches grid / list / mosaic | mixins/amplitude-analytics.js |
Member management events ​
Fired from workspace user-management pages and dialogs.
| Event name | When | Source |
|---|---|---|
member added | Admin adds a new member directly | components/dam/Dialogs/Org-Settings/AdminUserDialog.vue |
member invited | Admin sends an email invitation | same |
member updated | Admin updates member role or details | same |
member activated | Admin re-activates a suspended member | pages/.../workspace-settings/user/list.vue, ActivateUserDialog.vue |
member deactivated | Admin deactivates a member | pages/.../workspace-settings/user/list.vue |
member invite-resent | Admin resends a pending invitation | same |
member password-reset | Admin triggers a password reset for a member | same |
Workspace management events ​
Fired from workspace settings pages.
| Event name | When | Source |
|---|---|---|
workspace created | New workspace created | pages/create-workspace.vue |
workspace settings-updated | General workspace settings saved | pages/.../workspace-settings/index.vue |
workspace instance-settings-updated | DAM instance settings saved | same |
workspace domain-updated | Custom domain saved | same |
workspace domain-verified | Domain verified successfully | same |
workspace domain-verify-failed | Domain verification failed | same |
workspace custom-field-toggled | Custom field enabled or disabled | pages/.../workspace-settings/custom-fields/index.vue |
workspace custom-field-deleted | Custom field deleted | same |
workspace notifications-toggled | Global notification setting toggled | pages/.../workspace-settings/dam/_instance_id/notifications/index.vue |
workspace notification-prefs-updated | Notification preferences saved | same |
workspace logo-updated | Workspace logo uploaded | components/dam/Dialogs/Org-Settings/LogoEditDialog.vue |
workspace favicon-updated | Workspace favicon uploaded | same |
workspace owner-changed | Workspace ownership transferred | components/dam/Dialogs/Org-Settings/WorkspaceOwnerDialog.vue |
guest url-copied | External guest URL copied from user list | pages/.../workspace-settings/external-user/list.vue |
feedback submitted | User submits feedback | pages/.../feedback/index.vue |
subscription canceled | Trial account or subscription canceled | components/dam/Dialogs/subscription/DeleteTrialAccountDialog.vue |
Portal management events ​
All fired via direct $amplitude.track — portal config is not asset transaction activity.
Portal lifecycle:
| Event name | When | Source |
|---|---|---|
portal created | New portal created | components/dam/Dialogs/CreatePortalDialog.vue |
portal branding-updated | Portal branding settings saved | pages/.../workspace-settings/dam/_instance_id/index.vue |
portal settings-updated | General portal settings saved | components/dam/Settings/CollageSettings.vue |
portal theme-updated | Portal colour theme changed | same |
portal logo-updated | Portal logo uploaded | same |
portal favicon-updated | Portal favicon uploaded | same |
portal visibility-changed | Portal published/unpublished | same |
Portal users:
| Event name | When | Source |
|---|---|---|
portal invite-resent | Portal user invitation resent | pages/.../workspace-settings/portals/users/index.vue |
portal user-deactivated | Portal user deactivated | same |
portal invite-deleted | Portal invitation deleted | same |
portal user-activated | Portal user reactivated | same |
portal user-password-reset | Portal user password reset by admin | same |
Portal banners:
| Event name | When | Properties |
|---|---|---|
portal banner-created | New banner added | instance_id, banner_id |
portal banner-updated | Banner edited | instance_id, banner_id |
portal banner-toggled | Banner activated or deactivated | instance_id, banner_id, is_active |
portal banner-deleted | Banner deleted | instance_id, banner_id, banners_remaining |
portal banners-reordered | Admin drags to reorder banners | instance_id, banner_count |
Portal tiles:
| Event name | When | Properties |
|---|---|---|
portal tile-created | New tile added | instance_id, tile_id |
portal tile-updated | Tile edited | instance_id, tile_id |
portal tile-toggled | Tile activated or deactivated | instance_id, tile_id, is_active |
portal tile-deleted | Tile deleted | instance_id, tile_id, tiles_remaining |
portal tiles-reordered | Admin drags to reorder tiles | instance_id, tile_count |
portal slider-toggled | Slider mode enabled or disabled | instance_id, is_slider |
portal banners-reorderedandportal tiles-reorderedonly fire on genuine user drags — reorders triggered as a side effect of add/delete are suppressed.
Mixin Methods Reference ​
Registered globally via plugins/branding.js. Can also be mixed in locally: mixins: [amplitudeAnalytics].
| Method | Signature | Purpose |
|---|---|---|
trackActivity | (payload, extraProperties?) | Main tracking method — constructs event name from payload.event + payload.sub_event |
trackImageEditor | (verb, properties?) | Tracks image editor actions as "asset editor-{verb}" |
trackCustomFieldsUpdated | (assetIds, fields, isMultiple?, forcedKind?) | Tracks custom field add/update/remove grouped by kind |
trackViewModeChange | (mode, previous?, identifier?) | Fires "user view-mode-changed" and updates user property |
trackSearchResultClick | (resultType, item, list) | Fires "search result-clicked" with position rank |
trackFilterChange | (nextList) | Fires "search filter-applied" on each filter type delta |
trackVisibilityChange | (activity, before, after, extra?) | Fires visibility event split by portal IDs added/removed |
recordSelectionMethod | (method) | Records how items were selected ('shift-click', 'checkbox', etc.) |
selectionMethodFor | (count) → {} or { selection_method } | Returns selection_method property for bulk events |
changedFields | (pairs) → string[] | Returns keys where before !== after — used to build fields_changed properties |
cascadeSize | (items) → { assets_affected, subfolders_affected } | Computes cascade counts for folder delete/move events |
syncViewMode | (mode) | Updates Amplitude user property only when mode actually changed |
Property Allowlist ​
trackActivity passes the payload through an allowlist — only relevant entity IDs are forwarded per event type.
event value | Allowed keys from payload |
|---|---|
asset | asset_id, category_id, collection_id, version_id, share_id, tag_id |
collage | collection_id, asset_id, share_id, category_id |
folder | category_id, asset_id, share_id |
shared | share_id, asset_id, category_id, collection_id |
| All | source (common key) |
All IDs are normalised to strings by the enrichment plugin (normalizeIdProperties) to prevent the same ID arriving as 42 in one event and "42" in the next.
Session Replay ​
Amplitude Session Replay is initialised after the main SDK is ready (plugins/amplitude-session-replay.js). Sampling rate is controlled by AMPLITUDE_SESSION_REPLAY_SAMPLE_RATE env var (0 = disabled). The session ID is re-synced after every navigation via router.afterEach.
Related Documentation ​
- Analytics & Insights Feature — backend transaction analytics (dispatchAnalytics)
- amplitude-analytics Mixin — mixin registration
- amplitude Plugin — plugin setup