Appearance
Insights (Weekly Usage Graphs) ​
Overview ​
- Dashboard Widget: A "Last 7 Days" chart panel on the workspace dashboard that renders bar graphs of asset activity — views, downloads, uploads — for the trailing seven calendar days.
- Tabbed Metrics: The
InsightsTabGraphcomponent renders one tab per metric type returned by the API, allowing admins to switch between activity categories without navigating away. - Plan Gating: Metric tabs can be individually locked behind subscription tiers. When a metric returns a 402 code, the tab is disabled and shows an upgrade prompt rather than graph data.
- Access Control: An additional environment-level gate (
INSIGHTS_ACCESS_PERSONS) restricts full insights visibility to a comma-separated email allowlist. - Chart Library: Powered by Chart.js 2.9 via the
vue-chartjs3.x wrapper, rendered through a sharedBar.vueplugin component using thereactivePropmixin for reactive data updates.
Architecture ​
The insights system is anchored entirely on the dashboard page (pages/_workspace_id/dam/dashboard/index.vue). On mount, the page calls fetchWeeklyInsights(), which POSTs to /digital-assets/new-dashboard/weekly-insights with the current date and the date six days prior. The API response is a flat object whose keys are metric names (views, downloads, uploads, etc.) and whose values each contain a graph_data object mapping date strings to integer counts.
The dashboard page transforms this raw response into Chart.js-compatible datasets before passing the data down to the InsightsTabGraph component. Each metric gets a cloned copy of the base InsightsBarData template, with labels set to the date keys and two datasets: a bar series for the raw counts and a scatter overlay for highlighting bars whose value falls below 20% of the maximum — useful for visually flagging near-zero days. Chart options are defined inline on the page and passed as props, including a fully custom tooltip renderer that positions an absolutely-placed DOM element rather than relying on Chart.js's built-in tooltip.
The InsightsTabGraph component handles the tab-level rendering, iterating over the metric object to build one v-tab and one v-tab-item per key. If a metric has a 402 error code in its response, the tab is rendered in a disabled state with a v-tooltip instructing the admin to upgrade. The companion InsightsGraph component is a simpler variant without tabs — it renders each metric as a stacked card in a vertical list and is available for layouts that do not require tabbed switching.
The Vuex analytics store (store/analytics/index.js) holds date range state and a deprecated insightsTransactionAdd action that was used to record anonymous tracking events (base64-encoded, reversed). It remains in the codebase but is marked for removal.
File Structure ​
JavaScript Files (.js) ​
store/analytics/index.js— Analytics Vuex moduleInsightsTransactionandInsightsTransactionSharestate for tracking response storage- Date range state (
dStartDate,dEndDate,lastDate,activeDate) for filter coordination insightsTransactionAddaction for sending encoded transaction activity (deprecated)- Getters exposing date state to components via
getLastDate,getStartDate,getEndDate
Vue Component Files (.vue) ​
components/plugins/Bar.vue— Chart.js bar chart plugin wrapper- Extends
vue-chartjsBarwithreactivePropmixin for reactive dataset updates - Accepts
optionsandpluginsprops forwarded torenderChart - Re-renders automatically when
chartDataprop changes via Vue reactivity
- Extends
components/dam/Insights/InsightsTabGraph.vue— Tabbed insights card- Renders one
v-tabper metric returned by the weekly-insights API - Disables tabs for locked metrics (402 response) with a subscription upgrade tooltip
- Delegates chart rendering to
Bar.vue(InsightsBar) for each tab item - Accepts
dataLoading,weeklyInsightList,barOptions, andbarPluginprops
- Renders one
components/dam/Insights/InsightsGraph.vue— Stacked insights card (non-tabbed)- Renders each metric as a separate 75px-height card stacked vertically
- Uses the same
Bar.vueplugin asInsightsTabGraph - Contains a
hasAccesscomputed property gated onINSIGHTS_ACCESS_PERSONSenv variable - Shows skeleton loaders when
dataLoadingis true orweeklyInsightListis empty
pages/_workspace_id/dam/dashboard/index.vue— Dashboard page (insights host)- Calls
fetchWeeklyInsights()onmountedto load the trailing 7-day window - Holds
InsightsBarDatatemplate object,InsightsBarOptionsconfig, andInsightBarPluginarray - Transforms raw API response into Chart.js datasets (bar series + scatter overlay)
- Passes composed data to
WeeklyInsights(alias forInsightsTabGraph) as props
- Calls
SVG Icon Components ​
components/svg/CollageInsightIcon.vue— Insight/analytics icon used in navigationcomponents/svg/CollageBarChartIcon.vue— Bar chart icon for UI affordances
InsightsTabGraph Component ​
Features ​
- Tab per metric with label derived from the API response key
- Disabled tab state with upgrade tooltip for 402-gated metrics
- Active tab index tracked in
activeTablocal data - Skeleton loader for both the title area and the chart area while loading
- Empty-state skeleton when
weeklyInsightListis an empty object
Props ​
javascript
{
mainClass: {
type: String,
default: 'boxview collageTabs weekly-insights-tab h-auto mb-4'
},
heading: {
type: String,
default: 'Last 7 Days'
},
dataLoading: {
type: Boolean,
default: false
},
weeklyInsightList: {
type: Object,
default: () => ({})
},
barOptions: {
type: Object,
default: () => ({})
},
barPlugin: {
type: Array,
default: () => []
}
}Events ​
javascript
{
// InsightsTabGraph emits no events — it is a pure display component
}Methods ​
javascript
{
// No public methods — state is managed via props and local activeTab data
}Usage ​
vue
<template>
<WeeklyInsights
mainClass="boxview collageTabs weekly-insights-tab h-auto mb-4"
heading="Last 7 Days"
:dataLoading="weeklyInsightLoading"
:weeklyInsightList="weeklyInsightList"
:barOptions="InsightsBarOptions"
:barPlugin="InsightBarPlugin"
/>
</template>
<script>
export default {
components: {
WeeklyInsights: () =>
import('~/components/dam/Insights/InsightsTabGraph.vue'),
},
data() {
return {
weeklyInsightLoading: false,
weeklyInsightList: {},
InsightsBarOptions: {
maintainAspectRatio: false,
responsive: true,
legend: { display: false },
},
InsightBarPlugin: [],
}
},
}
</script>InsightsGraph Component ​
Features ​
- Stacked card layout — one card per metric, fixed 75px height per card
- Bar chart rendered inside each card with
InsightsBar(Bar.vue) - Access guard computed property checks
INSIGHTS_ACCESS_PERSONSenv list - Skeleton loader while data loads or when metric list is empty
Props ​
javascript
{
mainClass: {
type: String,
default: 'boxview weekly-insights h-auto mb-4'
},
heading: {
type: String,
default: 'Last 7 Days'
},
dataLoading: {
type: Boolean,
default: false
},
weeklyInsightList: {
type: Object,
default: () => ({})
},
barOptions: {
type: Object,
default: () => ({})
},
barPlugin: {
type: Array,
default: () => []
}
}Events ​
javascript
{
// No events emitted — pure display component
}Methods ​
javascript
{
// No public methods
}Usage ​
vue
<template>
<InsightsGraph
heading="Last 7 Days"
:dataLoading="loading"
:weeklyInsightList="weeklyInsightList"
:barOptions="barOptions"
:barPlugin="[]"
/>
</template>
<script>
export default {
components: {
InsightsGraph: () =>
import('~/components/dam/Insights/InsightsGraph.vue'),
},
}
</script>Workflows ​
Fetch and Render Insights ​
1. Dashboard page mounts
Page: pages/_workspace_id/dam/dashboard/index.vue
↓
2. mounted() calls fetchWeeklyInsights()
Computes date range: today minus 6 days → today (YYYY-MM-DD)
Sets weeklyInsightLoading: true
↓
3. POST /digital-assets/new-dashboard/weekly-insights
Body: { date_type: 'week', from_date, to_date }
Response: { views: {...}, downloads: {...}, uploads: {...} }
↓
4. Transform response into Chart.js datasets
For each metric key in response:
a. Check for 402 error — if present, store code + message, skip chart
b. Clone base InsightsBarData template (deep clone)
c. Set labels = Object.keys(insight.graph_data) (date strings)
d. Set bar dataset data = Object.values(insight.graph_data) (counts)
e. Set scatter dataset data = map values to { x, y, show } objects
→ show = true when value < 20% of max (highlight near-zero bars)
f. Attach card_label = metric key name
↓
5. weeklyInsightList assigned (triggers prop reactivity)
Component: InsightsTabGraph.vue re-renders
↓
6. InsightsTabGraph renders v-tabs
One tab per metric key
Disabled tab + tooltip for any metric with code 402
↓
7. User clicks a tab
activeTab data updates
Corresponding v-tab-item with InsightsBar renders the chart
↓
8. Chart.js renders the bar + scatter overlay
Custom tooltip DOM element positioned absolutely on hoverPlan-Gated Metric Handling ​
1. API returns metric with error shape
{ original: { code: 402, message: 'Upgrade required' } }
↓
2. fetchWeeklyInsights detects original.code == 402
Sets insight.code = 402
Sets insight.message = original.message
Skips chart data transformation
↓
3. InsightsTabGraph renders disabled v-tab
v-tooltip wraps the tab: "Please upgrade your subscription plan..."
v-tab-item shows the message string instead of InsightsBarAPI Integration ​
Endpoints ​
| Method | Path | Description |
|---|---|---|
POST | /digital-assets/new-dashboard/weekly-insights | Fetch 7-day usage metrics per activity type |
Weekly Insights Request ​
json
POST /digital-assets/new-dashboard/weekly-insights
{
"date_type": "week",
"from_date": "2026-08-04",
"to_date": "2026-08-10"
}Weekly Insights Response (success) ​
json
{
"data": {
"views": {
"card_label": "views",
"graph_data": {
"Aug 4": 12,
"Aug 5": 0,
"Aug 6": 34,
"Aug 7": 8,
"Aug 8": 21,
"Aug 9": 5,
"Aug 10": 17
}
},
"downloads": {
"card_label": "downloads",
"graph_data": {
"Aug 4": 3,
"Aug 5": 0,
"Aug 6": 7,
"Aug 7": 2,
"Aug 8": 9,
"Aug 9": 1,
"Aug 10": 4
}
},
"uploads": {
"card_label": "uploads",
"graph_data": {
"Aug 4": 5,
"Aug 5": 2,
"Aug 6": 11,
"Aug 7": 0,
"Aug 8": 3,
"Aug 9": 6,
"Aug 10": 2
}
}
}
}Weekly Insights Response (plan-gated metric) ​
json
{
"data": {
"views": {
"original": {
"code": 402,
"message": "Please upgrade your plan to access view analytics."
}
}
}
}Component Integration ​
Full example showing how the dashboard page composes insights with the chart plugin:
vue
<template>
<div class="collage-body">
<WeeklyInsights
mainClass="boxview collageTabs weekly-insights-tab h-auto mb-4"
heading="Last 7 Days"
:dataLoading="weeklyInsightLoading"
:weeklyInsightList="weeklyInsightList"
:barOptions="InsightsBarOptions"
:barPlugin="InsightBarPlugin"
/>
</div>
</template>
<script>
import { cloneDeep } from 'lodash'
import moment from 'moment'
export default {
layout: 'damLayout',
components: {
WeeklyInsights: () =>
import('~/components/dam/Insights/InsightsTabGraph.vue'),
},
data() {
return {
weeklyInsightLoading: true,
weeklyInsightList: {},
InsightsBarData: {
labels: [],
datasets: [
{
label: 'Graph Data',
data: [],
backgroundColor: '#A8A8F0',
borderColor: '#A8A8F0',
hoverBackgroundColor: '#075850',
hoverBorderColor: '#075850',
borderWidth: 1,
fill: true,
},
{
type: 'scatter',
label: 'Points',
data: [],
showLine: false,
pointBackgroundColor: '#075850',
pointBorderColor: '#075850',
pointRadius: 0,
pointHoverRadius: (ctx) => {
const pointData = ctx.dataset.data[ctx.dataIndex]
return pointData && pointData.show ? 5 : 0
},
},
],
},
InsightsBarOptions: {
maintainAspectRatio: false,
responsive: true,
legend: { display: false },
tooltips: { enabled: false },
},
InsightBarPlugin: [],
}
},
async mounted() {
await this.fetchWeeklyInsights()
},
methods: {
async fetchWeeklyInsights() {
try {
this.weeklyInsightLoading = true
const pastDate = new Date()
pastDate.setDate(pastDate.getDate() - 6)
const to_date = moment(new Date()).format('YYYY-MM-DD')
const from_date = moment(pastDate).format('YYYY-MM-DD')
const { data } = await this.$axios.$post(
'/digital-assets/new-dashboard/weekly-insights',
{ date_type: 'week', to_date, from_date }
)
this.weeklyInsightList = data
Object.keys(this.weeklyInsightList).forEach((key) => {
const insight = this.weeklyInsightList[key]
if (insight?.original?.code === 402) {
insight.code = insight.original.code
insight.message = insight.original.message
insight.card_label = key
} else if (insight && typeof insight === 'object') {
insight.card_label = key
insight.bar_data = cloneDeep(this.InsightsBarData)
insight.bar_data.labels = Object.keys(insight.graph_data)
insight.bar_data.datasets[0].card_label = key
insight.bar_data.datasets[0].data = Object.values(insight.graph_data)
insight.bar_data.datasets[1].card_label = key
const maxValue = Math.max(...Object.values(insight.graph_data))
insight.bar_data.datasets[1].data = Object.values(insight.graph_data).map(
(value, index) => ({
x: index + 0.5,
y: value,
show: value <= 0.2 * maxValue,
})
)
}
})
} catch (error) {
this.weeklyInsightList = {}
this.$snackbar.error(this.$getErrorMessage(error))
} finally {
this.weeklyInsightLoading = false
}
},
},
}
</script>Related Documentation ​
- Dashboard — The host page for the weekly insights widget
- File Upload — Upload system that generates the activity tracked in insights
- Analytics Store — Vuex module for date range state and transaction tracking
- Chart.js Integration — Bar chart plugin via vue-chartjs