Appearance
Collages ​
Overview ​
Collages are cross-folder asset collections that let workspace users group assets from anywhere in the DAM into a named, shareable set. They are distinct from folders — a collage holds references to assets rather than owning them, so the same asset can appear in many collages without duplication.
- Collage listing page: Paginated grid or list of all collages in the workspace, with sorting, preview thumbnails, and per-row actions.
- Collage detail page: Full asset browser scoped to one collage, with grid, list, and mosaic view modes, inner search, filters, bulk selection, and a bulk action bar.
- SaveToCollage dialog: Inline workflow for adding or removing assets from collages without leaving the current page.
- CreateCollageDialog: Shared dialog for creating a new collage or renaming an existing one.
- Permission control: Collage visibility is controlled per-portal via the
PermissionDialog(instance_visibilityarray). Restricted collages show a lock icon on every card and list row.
Architecture ​
The collage feature is built across two pages, a card component, and two shared dialogs. The listing page (pages/_workspace_id/dam/collage/index.vue) owns pagination, sorting, and the top-level collage actions (create, rename, share, download, delete, permission). It fetches data from digital-assets/collection/get-all-by-page directly in page methods rather than through a dedicated Vuex action, writing results into local collagesList state and appending on infinite scroll.
The detail page (pages/_workspace_id/dam/collage/_id/index.vue) is the heavier of the two. It loads collage metadata and its asset list separately, supports three view modes (grid, list, mosaic), and integrates the same search and filter stack used on the main DAM search page via the searchCommonFunctions mixin. Sort preferences, column visibility, and thumbnail size are each cached to localStorage via dedicated mixins so they survive page reloads.
Both pages rely on the dam Vuex store for the shared collectionList (used by SaveToCollage) and for the delete/rename actions. Analytics events are dispatched via the amplitudeAnalytics mixin on every significant user action.
File Structure ​
JavaScript Files (.js) ​
mixins/common-functions.js— Shared page utility mixin$getWorkspaceId()helper for workspace ID resolution$canCreateCollages(),$canDeleteCollage(),$canEditCollageNameDescription(), etc.changedFields()for computing analytics diff payloadsdispatchAnalytics()andtrackActivity()wrappers
mixins/view-mode-cache.js— View mode persistencegetCachedViewModeWithDefault(default)reads fromlocalStoragesetCachedViewMode(mode)writes the current mode- Shared between collage listing page and detail page
mixins/sort-preference-cache.js— Sort persistencegetCachedSort(key)returns{ field, direction }or nullsetCachedSort(key, field, direction)persists across sessions
mixins/column-visibility-cache.js— Column visibility for list viewgetCachedColumns(key)returns saved column arraysetCachedColumns(key, columns)persists the selection
mixins/amplitude-analytics.js— Analytics wrapperdispatchAnalytics(body)sends structured analytics events- Used for collage create, view, update, delete, and download events
store/dam.js— DAM Vuex store (collage-relevant slice)collectionListstate: flat array of all collages (used by SaveToCollage)collectionLoadingstate: loading flag for delete operationsgetCollectionsaction: fetches full list viadigital-assets/collection/get-alladdCollectionaction: prepends a new collage tocollectionListdeleteCollectionaction: calls delete API and removes from local staterenameCollectionaction: updates name incollectionListafter a successful update
Vue Component Files (.vue) ​
pages/_workspace_id/dam/collage/index.vue— Collage listing page- Paginated grid and list views with infinite scroll
- Sort by name, date modified, date added, and asset count
- Dialogs: create, rename, share, delete, permission
- Route middleware:
authCheck,check-workspace-access,checkWorkspace,can-access-dam-module
pages/_workspace_id/dam/collage/_id/index.vue— Collage detail page- Three view modes: grid, list, mosaic with masonry layout
- Inner search and filter bar (reuses DAM search stack)
- Bulk selection with shift-click and marquee drag
BulkActionBarfor multi-asset share, download, add-to-another-collage, tag, and custom fields
components/dam/Collage/index.vue— Collage card component- Multi-image preview thumbnail (1-, 2-, or 3-image mosaic layouts)
- Context menu with Share, Download, Edit, Portals, Delete
- Lock icon when
instance_visibilityis empty - Used in both listing page and search results
components/dam/Collage/FileListingCard.vue— Asset card inside a collage- Grid and mosaic card for individual assets within a collage
- Quick-view, select, rename, remove-from-collage, download, permission actions
components/dam/Dialogs/CreateCollageDialog.vue— Create / rename dialog- Dual purpose: new collage (with
showSubHeading: true) or rename existing - Optional description field controlled by the
isDescriptionprop - Also handles folder creation when
addNewFolder: true
- Dual purpose: new collage (with
components/dam/Dialogs/SaveToCollage.vue— Add-to-collage dialog- Searchable list of all workspace collages with Add/Remove toggles
- Inline create via search input when no match found
- Supports
deferredmode (for upload queue) andselectAllfor bulk operations
SVG Icon Components (Collage-related) ​
components/svg/Collages.vue— Collages sidebar iconcomponents/svg/CollageEmptyIcon.vue— Empty-state illustration
Collage Listing Page ​
File: pages/_workspace_id/dam/collage/index.vue
Route: /:workspace_id/dam/collage
Features ​
- Grid and list view with persisted view mode
- Infinite scroll pagination (50 items per page)
- Sort by name, date modified, date added, or asset count
- Lock icon for collages with restricted portal visibility
- Share, download (single-file or zip), rename, manage portals, delete per row
Key Methods ​
javascript
{
getCollagesList(page = 1) {}, // Fetch paginated collages; appends on page > 1
onScroll(event) {}, // Triggers loadMoreCollages when near bottom
loadMoreCollages() {}, // Increments page and calls getCollagesList
sortCollage(field, isSort) {}, // Toggles sort direction and re-sorts local list
addCollage() {}, // Emits 'openCreateCollage' EventBus event
shareCollage(item) {}, // Opens ShareAssetDialog for the selected collage
downloadCollage(collage) {}, // Single file or multi-file zip download
deleteCollage() {}, // Dispatches dam/deleteCollection
changeCollageName(payload) {}, // PATCH name+description, dispatches dam/renameCollection
openPermissions(item) {}, // Opens PermissionDialog
onPermissionUpdated(collage, e) {} // Updates local instance_visibility after save
}Usage ​
vue
<template>
<!-- Rendered by Nuxt routing — no direct parent usage -->
<!-- Accessed at /:workspace_id/dam/collage -->
</template>Collage Detail Page ​
File: pages/_workspace_id/dam/collage/_id/index.vue
Route: /:workspace_id/dam/collage/:id
Features ​
- Three view modes: grid, list, mosaic (masonry)
- Inner search with 500ms debounce
- Filter bar with tag, file type, uploaded-by, and custom field filters
- Multi-select with shift-click (shiftSelection mixin) and drag marquee (marqueeSelection mixin)
- BulkActionBar for bulk share, download, tag, custom fields, add to another collage
- Sort and column visibility cached to localStorage
Key Methods ​
javascript
{
getCollageDetails() {}, // Loads collage metadata (name, description, etc.)
loadCollageAssets(sortField) {}, // Loads the asset list with server-side sort
performSearch() {}, // Issues search/filter API request
handleSearchInput() {}, // Debounces performSearch via lodash debounce
applyHeaderSort(field) {}, // Column header click sort with direction toggle
handleSortMenuChange(payload) {}, // DisplayPanel sort-menu change handler
openAsset(file) {}, // Opens QuickView or navigates to asset detail
toggleSelect(file, idx, event) {}, // Handles individual and shift-click selection
downloadFile(type) {}, // Single, multi, or full-collage zip download
removeCollage() {}, // Deletes the collage and navigates to listing
deleteFromDrop(flag) {}, // Removes selected assets from collage
openPermissionDialog(item, flag) {} // Opens PermissionDialog for asset or collage
}Collage Card Component ​
File: components/dam/Collage/index.vue
Features ​
- Multi-image preview: 1-image (full), 2-image (60/40 split), 3-image (60 + 40 stacked)
- Context menu driven by a
menuItemscomputed array (Share, Download, Edit, Portals, Delete) - Subscription gate on Portals action (
collection_permission_customisationfeature flag) - SVG icon fallback for non-image asset types
Props ​
javascript
{
collage: {
type: Object,
required: true
// { id, name, description, assets_count, assets_id, preview_images, instance_visibility }
},
downloading: {
type: Boolean,
default: false
},
flag: {
type: String,
default: '' // 'asset-detail' | 'search-page' | ''
}
}Events ​
javascript
{
'share': (collage) => {}, // Request parent to open ShareAssetDialog
'download': (collage) => {}, // Request parent to start download
'permission': (collage) => {}, // Request parent to open PermissionDialog
'delete': (collage) => {}, // Request parent to confirm and delete
'nameChange': () => {}, // Emitted after a successful rename
'opened': () => {} // Emitted when card click navigates to detail
}Usage ​
vue
<template>
<CollageCards
:collage="collage"
:downloading="downloadColgId == collage.id"
@delete="confirmDeleteCollage(collage)"
@permission="openPermissions(collage)"
@share="shareCollage(collage)"
@download="downloadCollage(collage)"
/>
</template>
<script>
export default {
components: {
CollageCards: () => import('~/components/dam/Collage'),
}
}
</script>SaveToCollage Dialog ​
File: components/dam/Dialogs/SaveToCollage.vue
Features ​
- Searchable collage list with Add/Remove toggles per row
- Inline create: when search has no exact match, a "Create new collage" button appears
- Sorted so already-added collages appear at the top
deferredprop for the upload queue — selections are batched and emitted on close rather than applied immediately
Props ​
javascript
{
files: {
type: Array,
default: () => [] // Asset objects or IDs to add/remove
},
dialog: {
type: Boolean,
default: false
},
selectAll: {
type: Boolean,
default: false // When true, passes all selected asset IDs
},
flag: {
type: String,
default: '' // Context flag, e.g. 'collage-details'
},
deferred: {
type: Boolean,
default: false // Batch mode for upload queue
},
queuedCollageIds: {
type: Array,
default: () => [] // Pre-selected collage IDs in deferred mode
}
}Events ​
javascript
{
'close': (false) => {}, // Dialog closed
'reload': () => {}, // Request parent to reload asset list
'queue-complete': (payload) => {} // Deferred mode: { collages, changed }
}Workflows ​
Create Collage ​
1. User clicks "New Collage" button
Page: pages/_workspace_id/dam/collage/index.vue
Action: addCollage() emits EventBus 'openCreateCollage'
↓
2. CreateCollageDialog opens (showSubHeading: true, isDescription: true)
Component: components/dam/Dialogs/CreateCollageDialog.vue
↓
3. User fills name (required, min 3 chars) and optional description
Validation: rejects characters / \ : ? * " < >
↓
4. User submits → submitCollage()
POST /digital-assets/collection/create
Body: { name, description }
↓
5. On success:
- dam/addCollection dispatched → collectionList updated
- EventBus 'collectionUpdate' emitted → listing page calls getCollagesList()
- EventBus 'syncDashboard' emitted with 'collage'
- Analytics event: collage / createdAdd Assets to a Collage ​
1. User selects assets in DAM (any folder, search, or uploaded view)
↓
2. User opens "Add to Collage" via:
- Asset card context menu → SaveToCollage.vue
- BulkActionBar "Add to Collage" button
↓
3. SaveToCollage dialog loads
- dam/getCollections dispatched → collages fetched
- Already-added collages sorted to top and marked is_added: true
↓
4. User clicks "Add" on a collage row → saveCollection(id)
POST /digital-assets/collection/:id/add-assets
Body: { assets_id: [1, 2, 3] }
↓
5. On success:
- Collage row toggles to "Remove"
- EventBus 'updateCollageAssets' emitted with { flag: 'add', collage_id, assetsId }
- Collage detail page updates its file list if currently open
- Analytics event: collage / asset-addedShare a Collage ​
1. User clicks Share from collage card menu or collage detail options menu
Condition: collage.assets_count > 0 (disabled for empty collages)
↓
2. ShareAssetDialog opens
Props: { heading: 'Share Collage', collection: true, collection-assets-id: collage.id }
↓
3. User configures share link (expiry, password, download permissions)
API: POST /digital-assets/share-link/create
↓
4. Share link generated and displayed to user
- Shareable URL returned and shown in dialog
- Analytics event: collage / share (via parent)Delete a Collage ​
1. User clicks Delete from context menu
Action: confirmDeleteCollage(item) sets deleteDialog = true
↓
2. ConfirmationDialog displays warning
Message: 'Are you sure you want to delete this <name> collage?'
↓
3. User confirms → deleteCollage()
Action: dam/deleteCollection(collage.id)
DELETE /digital-assets/collection/delete/:id
↓
4. On success:
- collectionList mutation removes the entry
- Page removes collage from local collagesList
- Detail page navigates back to listing route
- Pending collage queue in localStorage is also cleaned
- Analytics event: collage / deletedAPI Integration ​
Endpoints ​
| Method | Endpoint | Purpose |
|---|---|---|
GET | digital-assets/collection/get-all-by-page | Paginated collage listing |
GET | digital-assets/collection/get-all | Full list for SaveToCollage |
POST | digital-assets/collection/create | Create new collage |
POST | digital-assets/collection/update/:id | Rename / update description |
DELETE | digital-assets/collection/delete/:id | Delete collage |
POST | digital-assets/collection/:id/add-assets | Add assets to collage |
POST | digital-assets/collection/:id/remove-assets | Remove assets from collage |
Get Collage List (Paginated) ​
javascript
// Request
GET /digital-assets/collection/get-all-by-page?page=1&sort_value=modified_at&sort_by=DESC&workspace_id=42
// Response
{
"data": {
"current_page": 1,
"last_page": 3,
"data": [
{
"id": 101,
"name": "Campaign Assets",
"description": "Q2 brand campaign",
"assets_count": 24,
"assets_id": [1, 2, 3],
"preview_images": [
{ "url": "https://cdn.example.com/thumb1.jpg", "file_type": "jpg", "uuid": "abc" },
{ "url": "https://cdn.example.com/thumb2.jpg", "file_type": "png", "uuid": "def" }
],
"instance_visibility": [5, 8],
"modified_at": "2025-07-15T10:30:00Z",
"created_at": "2025-06-01T08:00:00Z"
}
]
}
}Create Collage ​
javascript
// Request
POST /digital-assets/collection/create
{
"name": "Campaign Assets",
"description": "Q2 brand campaign"
}
// Response
{
"message": "Collage created successfully",
"data": {
"id": 101,
"name": "Campaign Assets",
"description": "Q2 brand campaign",
"assets_count": 0,
"assets_id": [],
"preview_images": []
}
}Add Assets to Collage ​
javascript
// Request
POST /digital-assets/collection/101/add-assets
{
"assets_id": [45, 67, 89]
}
// Response
{
"message": "Assets added to collage successfully"
}Component Integration ​
vue
<template>
<div>
<!-- Collage list -->
<v-row no-gutters class="my-0 row-gap-24">
<v-col
v-for="collage in collagesList"
:key="collage.id"
cols="4"
lg="3"
xl="2"
>
<CollageCards
:collage="collage"
:downloading="downloadColgId === collage.id"
@delete="confirmDelete(collage)"
@share="openShare(collage)"
@download="downloadCollage(collage)"
@permission="openPermissions(collage)"
/>
</v-col>
</v-row>
<!-- Dialogs -->
<client-only>
<SaveToCollage
:dialog="saveCollageDialog"
:files="selectedAssets"
@close="saveCollageDialog = false"
@reload="refreshAssets"
/>
<CreateCollageDialog
:dialog="createDialog"
heading="New Collage"
:isDescription="true"
:showSubHeading="true"
@close="createDialog = false"
/>
</client-only>
</div>
</template>
<script>
import commonFunctions from '~/mixins/common-functions'
export default {
components: {
CollageCards: () => import('~/components/dam/Collage'),
SaveToCollage: () => import('~/components/dam/Dialogs/SaveToCollage'),
CreateCollageDialog: () =>
import('~/components/dam/Dialogs/CreateCollageDialog.vue'),
},
mixins: [commonFunctions],
data() {
return {
collagesList: [],
selectedAssets: [],
saveCollageDialog: false,
createDialog: false,
downloadColgId: 0,
}
},
methods: {
confirmDelete(collage) {
// open confirmation dialog...
},
openShare(collage) {
// open ShareAssetDialog...
},
async downloadCollage(collage) {
this.downloadColgId = collage.id
await this.$store.dispatch('dam/downloadMultipleFiles', {
collection_id: collage.id,
download_name: collage.name,
})
},
openPermissions(collage) {
// open PermissionDialog...
},
refreshAssets() {
// reload after add-to-collage
},
},
}
</script>Related Documentation ​
- File Upload — Upload system that feeds assets into collages
- Search — Search results surface collage cards alongside assets
- Portals — Portal permission management referenced by collage
instance_visibility - DAM Store —
collectionList,getCollections,addCollection,deleteCollection,renameCollection - Mixins - Common Functions — Permission helpers (
$canCreateCollages, etc.) - Mixins - View Mode Cache — Grid/list persistence