Appearance
Search & Discovery
Overview
The Search System provides comprehensive search functionality across all content types in the Admin Frontend application. It integrates with Typesense for high-performance full-text search, supports multi-collection searching (assets, folders, collages), advanced filtering, and provides a unified search interface with real-time results.
Architecture
System Components
- Search Bar: Main search input with Typesense integration
- Search Results: Display results for assets, folders, and collages
- Search Filters: Advanced filtering with tags, custom fields, file types, dates, etc.
- Search Tabs: Switch between different result types
- Bulk Actions: Operations on search results
- Search API: Typesense integration for multi-collection search
File Structure
Core Search Components
Vue Component Files (.vue)
components/dam/SearchBarTypeSense.vue- Main search bar component- Typesense search integration
- Real-time search suggestions
- Search query input
- Search execution
- Autocomplete functionality
components/dam/SearchAssets.vue- Search assets component- Display asset search results
- Asset result formatting
- Asset actions (view, download, share)
- Pagination for asset results
components/dam/SearchFolders.vue- Search folders component- Display folder search results
- Folder result formatting
- Folder navigation
- Pagination for folder results
components/dam/SearchCollages.vue- Search collages component- Display collage search results
- Collage result formatting
- Collage actions
- Pagination for collage results
components/dam/SearchTabs.vue- Search tabs component- Switch between search result types
- Tab navigation (All, Assets, Folders, Collages)
- Active tab indication
- Result count per tab
components/dam/SearchBreadCrumbs.vue- Search breadcrumbs component- Navigation breadcrumbs for search
- Search context display
- Back navigation
SVG Icon Components
components/svg/CollageSearchIcon.vue- Search icon- Used in navigation and UI elements
- Consistent iconography for search
Search API & Backend
JavaScript Files (.js)
api/typesense.js- Typesense search API integration- Multi-search functionality
- Collection search coordination
- Search query building
- Result formatting
- Error handling
mixins/search-common-functions.js- Common search functions mixin- Filter building functions
- Tag filter construction (
buildTagFilter) - Custom field filter construction (
buildCustomFieldFilter) - Date range filters
- File type filters
- User filters
- Search query utilities
Search Pages
Vue Page Files (.vue)
pages/_workspace_id/dam/search.vue- Main search results page- Search interface
- Results display
- Filter management
- Tab navigation
- Pagination
- Sorting options
Route: /:workspace_id/dam/search
Search Filters
Vue Component Files (.vue)
components/dam/Search/SearchFilter.vue- Search filter component- Filter selection interface
- Tag filters
- Custom field filters
- File type filters
- Date range filters
- User filters
- Filter application
components/dam/Search/ListLevelThree.vue- Third level filter list component- Nested filter options
- Filter category display
- Filter value selection
- Multi-select filters
components/dam/Search/AddedFilterOption.vue- Added filter option component- Display active filters
- Filter removal
- Filter editing
- Filter value display
Search Results Display
Vue Component Files (.vue)
components/dam/SearchTableHeader.vue- Search results table header- Column headers
- Sorting controls
- View mode toggle
- Bulk selection checkbox
components/dam/SearchBulkActions.vue- Bulk actions component- Bulk operations on search results
- Tag bulk operations
- Custom field bulk operations
- Delete bulk operations
- Download bulk operations
Search Skeleton Loaders
Vue Component Files (.vue)
components/dam/SearchListSkeleton.vue- List view skeleton loader- Loading state for list view
- Skeleton UI during search
components/dam/SearchGridSkeleton.vue- Grid view skeleton loader- Loading state for grid view
- Skeleton UI during search
components/dam/SearchGridAssetsSkeleton.vue- Grid assets skeleton loader- Loading state for asset grid
- Skeleton UI for asset results
Search Integration in Other Pages
Vue Component Files (.vue)
pages/_workspace_id/dam/collage_id/index.vue- Collage page- Contains search functionality for collage assets
- Filter assets within collage
- Search within collage context
pages/_workspace_id/dam/folders_id/index.vue- Folder details page- Contains search functionality for folder assets
- Filter assets within folder
- Search within folder context
pages/_workspace_id/dam/dashboard/index.vue- Dashboard page- May include search functionality
- Quick search access
components/theme/global/CollageHeader.vue- Header component- May include search bar
- Global search access
Search Bar Component
File: components/dam/SearchBarTypeSense.vue
Main search bar component with Typesense integration.
Features
- Real-time search input
- Autocomplete suggestions
- Search execution
- Query debouncing
- Search history (optional)
- Keyboard shortcuts (Ctrl+K)
Props
javascript
{
placeholder: {
type: String,
default: 'Search assets, folders, collages...'
},
workspaceId: {
type: [String, Number],
required: true
},
autoFocus: {
type: Boolean,
default: false
},
debounce: {
type: Number,
default: 300
}
}Methods
javascript
// Execute search
async executeSearch(query) {
// Build search query
// Call Typesense API
// Handle results
// Navigate to search page
}
// Get suggestions
async getSuggestions(query) {
// Call Typesense autocomplete
// Return suggestions
}
// Handle search input
handleInput(value) {
// Debounce input
// Update query
// Show suggestions
}Events
javascript
{
'search': (query) => {}, // Search executed
'suggestion-selected': (item) => {}, // Suggestion clicked
'clear': () => {} // Search cleared
}Usage
vue
<template>
<SearchBarTypeSense
:workspace-id="workspaceId"
placeholder="Search..."
@search="handleSearch"
/>
</template>
<script>
import SearchBarTypeSense from '~/components/dam/SearchBarTypeSense.vue'
export default {
components: {
SearchBarTypeSense
},
data() {
return {
workspaceId: this.$route.params.workspace_id
}
},
methods: {
handleSearch(query) {
this.$router.push({
path: `/${this.workspaceId}/dam/search`,
query: { q: query }
})
}
}
}
</script>Search Results Components
Search Assets Component
File: components/dam/SearchAssets.vue
Displays asset search results.
Features
- Asset result list/grid display
- Asset actions (view, download, share)
- Pagination
- Sorting options
- View mode toggle (list/grid)
Props
javascript
{
results: {
type: Array,
required: true
// Array of asset objects
},
loading: {
type: Boolean,
default: false
},
total: {
type: Number,
default: 0
},
page: {
type: Number,
default: 1
},
perPage: {
type: Number,
default: 20
}
}Search Folders Component
File: components/dam/SearchFolders.vue
Displays folder search results.
Features
- Folder result list display
- Folder navigation
- Folder metadata display
- Pagination
Search Collages Component
File: components/dam/SearchCollages.vue
Displays collage search results.
Features
- Collage result display
- Collage preview
- Collage actions
- Pagination
Search Tabs Component
File: components/dam/SearchTabs.vue
Tab navigation for switching between result types.
Features
- Tab switching (All, Assets, Folders, Collages)
- Active tab indication
- Result count per tab
- Tab click handling
Props
javascript
{
activeTab: {
type: String,
default: 'all'
// 'all', 'assets', 'folders', 'collages'
},
counts: {
type: Object,
default: () => ({
all: 0,
assets: 0,
folders: 0,
collages: 0
})
}
}Events
javascript
{
'tab-change': (tab) => {} // Tab changed
}Search Filter Components
Search Filter Component
File: components/dam/Search/SearchFilter.vue
Main search filter component for applying filters.
Features
- Filter category selection
- Tag filters
- Custom field filters
- File type filters
- Date range filters
- User filters
- Filter application
- Filter removal
Props
javascript
{
filters: {
type: Object,
default: () => ({})
// Current active filters
},
availableFilters: {
type: Object,
default: () => ({})
// Available filter options
}
}Methods
javascript
// Apply filter
applyFilter(category, filter) {
// Add filter to active filters
// Emit filter-applied event
// Update search
}
// Remove filter
removeFilter(category, filterId) {
// Remove from active filters
// Emit filter-removed event
// Update search
}
// Clear all filters
clearFilters() {
// Clear all active filters
// Emit filters-cleared event
// Update search
}List Level Three Component
File: components/dam/Search/ListLevelThree.vue
Third level filter list for nested filter options.
Features
- Nested filter display
- Filter category grouping
- Multi-select filters
- Filter value selection
Added Filter Option Component
File: components/dam/Search/AddedFilterOption.vue
Displays and manages active filters.
Features
- Display active filters
- Filter value display
- Remove filter button
- Edit filter option
Props
javascript
{
filter: {
type: Object,
required: true
// Filter object with category, value, etc.
},
removable: {
type: Boolean,
default: true
}
}Typesense API Integration
File: api/typesense.js
Typesense search API integration and multi-search functionality.
Multi-Search Function
javascript
async multiSearch(query, filters = {}, options = {}) {
// Build multi-search query
// Search across collections:
// - digital_assets
// - digital_assets_categories (folders)
// - dam_collections (collages)
// Apply filters
// Return formatted results
}Search Collections
- digital_assets - Digital assets collection
- digital_assets_categories - Folders/categories collection
- dam_collections - Collages collection
Search Parameters
javascript
{
q: 'search query',
query_by: 'display_file_name,description',
filter_by: 'workspace_id:=123 AND file_type:=image',
sort_by: 'created_at:desc',
per_page: 20,
page: 1,
facet_by: 'file_type,created_by',
max_facet_values: 10
}Usage
javascript
import { multiSearch } from '~/api/typesense'
// Execute search
const results = await multiSearch(
'summer campaign',
{
workspace_id: 123,
file_type: 'image',
tags: [1, 2, 3]
},
{
per_page: 20,
page: 1
}
)
// Results structure
{
assets: {
hits: [...],
found: 45,
page: 1
},
folders: {
hits: [...],
found: 12,
page: 1
},
collages: {
hits: [...],
found: 8,
page: 1
}
}Search Common Functions Mixin
File: mixins/search-common-functions.js
Common search functions for building filters and queries.
Key Functions
javascript
// Build tag filter
buildTagFilter(tagIds, operator = 'in') {
// Construct tag filter query
// Return filter object
}
// Build custom field filter
buildCustomFieldFilter(fieldKey, fieldType, value, operator = 'equals') {
// Construct custom field filter query
// Return filter object
}
// Build date range filter
buildDateRangeFilter(field, startDate, endDate) {
// Construct date range filter
// Return filter object
}
// Build file type filter
buildFileTypeFilter(fileTypes) {
// Construct file type filter
// Return filter object
}
// Build user filter
buildUserFilter(userIds) {
// Construct user filter
// Return filter object
}
// Combine filters
combineFilters(filters) {
// Combine multiple filters with AND/OR logic
// Return combined filter string
}Usage
vue
<script>
import searchCommonFunctions from '~/mixins/search-common-functions'
export default {
mixins: [searchCommonFunctions],
methods: {
applyFilters() {
const filters = []
// Add tag filter
if (this.selectedTags.length > 0) {
filters.push(this.buildTagFilter(this.selectedTags, 'in'))
}
// Add custom field filter
if (this.customFieldValue) {
filters.push(this.buildCustomFieldFilter(
'project_name',
'text',
this.customFieldValue
))
}
// Combine filters
const filterString = this.combineFilters(filters)
this.executeSearch(this.query, filterString)
}
}
}
</script>Search Page
File: pages/_workspace_id/dam/search.vue
Route: /:workspace_id/dam/search
Main search results page.
Features
- Search query input
- Search results display
- Tab navigation (All, Assets, Folders, Collages)
- Filter management
- Pagination
- Sorting options
- View mode toggle
- Bulk actions
Page Structure
vue
<template>
<div class="search-page">
<!-- Search Bar -->
<SearchBarTypeSense
:workspace-id="workspaceId"
:query="searchQuery"
@search="handleSearch"
/>
<!-- Search Tabs -->
<SearchTabs
:active-tab="activeTab"
:counts="resultCounts"
@tab-change="handleTabChange"
/>
<!-- Search Filters -->
<SearchFilter
:filters="activeFilters"
:available-filters="availableFilters"
@filter-applied="applyFilter"
@filter-removed="removeFilter"
/>
<!-- Active Filters -->
<div class="active-filters">
<AddedFilterOption
v-for="filter in activeFilters"
:key="filter.id"
:filter="filter"
@remove="removeFilter"
/>
</div>
<!-- Search Results -->
<div v-if="loading" class="results-loading">
<SearchListSkeleton v-if="viewMode === 'list'" />
<SearchGridSkeleton v-else />
</div>
<div v-else class="search-results">
<SearchAssets
v-if="activeTab === 'all' || activeTab === 'assets'"
:results="assetResults"
:loading="loading"
:total="assetTotal"
:page="currentPage"
@page-change="handlePageChange"
/>
<SearchFolders
v-if="activeTab === 'all' || activeTab === 'folders'"
:results="folderResults"
:loading="loading"
:total="folderTotal"
/>
<SearchCollages
v-if="activeTab === 'all' || activeTab === 'collages'"
:results="collageResults"
:loading="loading"
:total="collageTotal"
/>
</div>
<!-- Bulk Actions -->
<SearchBulkActions
v-if="selectedItems.length > 0"
:selected-items="selectedItems"
@bulk-action="handleBulkAction"
/>
</div>
</template>Search Workflows
Basic Search Workflow
1. User enters search query
Component: SearchBarTypeSense.vue
- Type in search input
- See autocomplete suggestions
↓
2. Execute search
- Press Enter or click search
- Navigate to search page
Route: /:workspace_id/dam/search?q=query
↓
3. Load search page
Component: pages/_workspace_id/dam/search.vue
- Parse query from URL
- Initialize search state
↓
4. Call Typesense API
API: /typesense/search
Mixin: api/typesense.js
- Multi-search across collections
- Apply default filters (workspace_id)
↓
5. Receive results
- Assets results
- Folders results
- Collages results
↓
6. Display results
Components: SearchAssets, SearchFolders, SearchCollages
- Show results in active tab
- Display result counts
- Show pagination if needed
↓
7. User interacts with results
- Click result to view details
- Apply filters
- Change tabs
- Change pageAdvanced Search with Filters
1. User navigates to search page
Route: /:workspace_id/dam/search
↓
2. Enter search query
Component: SearchBarTypeSense.vue
- Type query
- Execute search
↓
3. Open search filters
Component: SearchFilter.vue
- Click filter button
- Filter panel opens
↓
4. Apply filters
- Select tag filter
Mixin: buildTagFilter()
- Select custom field filter
Mixin: buildCustomFieldFilter()
- Select file type filter
- Select date range filter
↓
5. Combine filters
Mixin: combineFilters()
- All filters combined with AND logic
- Filter string created
↓
6. Execute filtered search
API: /typesense/search
Query: { q: query, filter_by: filterString }
↓
7. Display filtered results
- Results match all filters
- Active filters shown in AddedFilterOption components
- Can remove individual filters
↓
8. Update results
- Results update as filters change
- Result counts update
- Pagination resetsSearch with Tabs
1. User executes search
- Search returns results for all types
↓
2. View results by tab
Component: SearchTabs.vue
- Default: "All" tab (shows all types)
- Click "Assets" tab → Show only assets
- Click "Folders" tab → Show only folders
- Click "Collages" tab → Show only collages
↓
3. Tab-specific results
- Each tab shows relevant component
- Result counts per tab
- Tab-specific pagination
↓
4. Switch between tabs
- Results update instantly
- No new API call (results cached)
- Or new API call for tab-specific searchBulk Actions on Search Results
1. User selects multiple results
- Use checkboxes in results
- Or select all
↓
2. Bulk actions toolbar appears
Component: SearchBulkActions.vue
- Shows selected count
- Displays action buttons
↓
3. Choose bulk action
- Add Tags
- Remove Tags
- Update Custom Fields
- Download
- Delete
↓
4. Execute bulk action
- Confirm action (if destructive)
- Call bulk API
- Show progress
↓
5. Action complete
- Refresh search results
- Show success message
- Clear selectionAPI Integration
Typesense Search Endpoint
Endpoint: POST /typesense/search
Request Body:
json
{
"searches": [
{
"collection": "digital_assets",
"q": "summer campaign",
"query_by": "display_file_name,description",
"filter_by": "workspace_id:=123 AND file_type:=image",
"sort_by": "created_at:desc",
"per_page": 20,
"page": 1,
"facet_by": "file_type,created_by",
"max_facet_values": 10
},
{
"collection": "digital_assets_categories",
"q": "summer campaign",
"query_by": "name,description",
"filter_by": "workspace_id:=123",
"per_page": 20,
"page": 1
},
{
"collection": "dam_collections",
"q": "summer campaign",
"query_by": "name,description",
"filter_by": "workspace_id:=123",
"per_page": 20,
"page": 1
}
]
}Response:
json
{
"results": [
{
"collection": "digital_assets",
"hits": [
{
"document": {
"id": 456,
"display_file_name": "summer-campaign-2024.jpg",
"file_type": "image",
"workspace_id": 123,
"created_at": 1705320000
},
"highlights": [
{
"field": "display_file_name",
"snippet": "summer <mark>campaign</mark> 2024.jpg"
}
]
}
],
"found": 45,
"page": 1,
"search_time_ms": 12
},
{
"collection": "digital_assets_categories",
"hits": [...],
"found": 12,
"page": 1
},
{
"collection": "dam_collections",
"hits": [...],
"found": 8,
"page": 1
}
]
}Search Collections
digital_assets - Digital assets
- Fields: display_file_name, description, file_type, tags, custom_fields, etc.
digital_assets_categories - Folders/categories
- Fields: name, description, workspace_id, etc.
dam_collections - Collages
- Fields: name, description, workspace_id, etc.
Component Integration
Using Search in Page
vue
<template>
<div>
<!-- Search Bar in Header -->
<SearchBarTypeSense
:workspace-id="workspaceId"
@search="handleSearch"
/>
<!-- Search Results Page -->
<div v-if="isSearchPage">
<SearchTabs
:active-tab="activeTab"
:counts="resultCounts"
@tab-change="handleTabChange"
/>
<SearchAssets
v-if="activeTab === 'assets'"
:results="assetResults"
:loading="loading"
/>
</div>
</div>
</template>
<script>
import SearchBarTypeSense from '~/components/dam/SearchBarTypeSense.vue'
import SearchTabs from '~/components/dam/SearchTabs.vue'
import SearchAssets from '~/components/dam/SearchAssets.vue'
import { multiSearch } from '~/api/typesense'
import searchCommonFunctions from '~/mixins/search-common-functions'
export default {
components: {
SearchBarTypeSense,
SearchTabs,
SearchAssets
},
mixins: [searchCommonFunctions],
data() {
return {
workspaceId: this.$route.params.workspace_id,
searchQuery: this.$route.query.q || '',
activeTab: 'all',
assetResults: [],
loading: false,
resultCounts: {
all: 0,
assets: 0,
folders: 0,
collages: 0
}
}
},
computed: {
isSearchPage() {
return this.$route.name === 'workspace_id-dam-search'
}
},
async mounted() {
if (this.searchQuery) {
await this.executeSearch()
}
},
watch: {
'$route.query.q'(newQuery) {
this.searchQuery = newQuery
this.executeSearch()
}
},
methods: {
handleSearch(query) {
this.$router.push({
path: `/${this.workspaceId}/dam/search`,
query: { q: query }
})
},
async executeSearch() {
this.loading = true
try {
const filters = this.buildFilters()
const results = await multiSearch(
this.searchQuery,
filters,
{ per_page: 20, page: 1 }
)
this.assetResults = results.assets.hits
this.resultCounts = {
all: results.assets.found + results.folders.found + results.collages.found,
assets: results.assets.found,
folders: results.folders.found,
collages: results.collages.found
}
} catch (error) {
this.$toast.error('Search failed')
} finally {
this.loading = false
}
},
buildFilters() {
const filters = {
workspace_id: this.workspaceId
}
// Add active filters
if (this.activeFilters.tags) {
filters.tags = this.activeFilters.tags
}
return filters
},
handleTabChange(tab) {
this.activeTab = tab
// Update displayed results
}
}
}
</script>Using Search Filters
vue
<template>
<div>
<SearchFilter
:filters="activeFilters"
:available-filters="availableFilters"
@filter-applied="applyFilter"
/>
<div class="active-filters">
<AddedFilterOption
v-for="filter in activeFiltersList"
:key="filter.id"
:filter="filter"
@remove="removeFilter"
/>
</div>
</div>
</template>
<script>
import SearchFilter from '~/components/dam/Search/SearchFilter.vue'
import AddedFilterOption from '~/components/dam/Search/AddedFilterOption.vue'
import searchCommonFunctions from '~/mixins/search-common-functions'
export default {
components: {
SearchFilter,
AddedFilterOption
},
mixins: [searchCommonFunctions],
data() {
return {
activeFilters: {},
availableFilters: {
tags: [],
customFields: [],
fileTypes: ['image', 'video', 'document'],
dateRanges: []
}
}
},
computed: {
activeFiltersList() {
// Convert filters object to array
return Object.values(this.activeFilters)
}
},
methods: {
applyFilter(category, filter) {
this.$set(this.activeFilters, category, filter)
this.executeSearch()
},
removeFilter(filterId) {
const category = this.getFilterCategory(filterId)
this.$delete(this.activeFilters, category)
this.executeSearch()
}
}
}
</script>Search Integration in Other Pages
Collage Page Search
File: pages/_workspace_id/dam/collage_id/index.vue
Search functionality within collage context.
- Search assets within collage
- Filter collage assets
- Search within collage scope
Folder Details Page Search
File: pages/_workspace_id/dam/folders_id/index.vue
Search functionality within folder context.
- Search assets within folder
- Filter folder assets
- Search within folder scope
Header Search
File: components/theme/global/CollageHeader.vue
Global search bar in header.
- Quick search access
- Search from any page
- Navigate to search results
Related Documentation
- Global Search Functionality - Global search overview
- Typesense Search Integration - Typesense setup and configuration
- Advanced Search Filters - Advanced filtering capabilities
- Tags System - Tag-based search filtering
- Custom Fields System - Custom field search filtering
- Typesense Search API - Search API documentation