Ga naar hoofdinhoud

CnDashboardPage

Top-level dashboard page component — the dashboard equivalent of CnIndexPage. Assembles a complete dashboard from a widgets definition array and a layout array. Supports custom widgets via scoped slots, Nextcloud Dashboard API widgets, tile widgets, and an optional drag-and-drop edit mode.

Wraps: CnDashboardGrid, CnWidgetWrapper, CnWidgetRenderer, CnTileWidget

Try it

Loading CnDashboardPage playground…

Widget types

TypeHow to use
TileWidget def has type: 'tile' — renders as a quick-access link tile
CustomProvide a #widget-{widgetId} scoped slot (escape hatch — beats every built-in branch when a slot exists)
ChartWidget def has type: 'chart' — declarative apexcharts mount via CnChartWidget; chart inputs ride widgetDef.props
NC Dashboard APIWidget def has itemApiVersions — auto-rendered via CnWidgetRenderer

The dispatcher resolves widgets in that order. The custom-slot branch beats the chart branch so apps that need bespoke apexcharts behaviour outside the manifest contract can fall back to #widget-{id} without losing the rest of the manifest.

Chart widget

const WIDGETS = [{
id: 'sla-trend',
title: 'SLA trend',
type: 'chart',
props: {
chartKind: 'line', // line | bar | donut | area | pie | radialBar
series: [{ name: 'SLA %', data: [82, 88, 91, 93] }],
categories: ['Q1', 'Q2', 'Q3', 'Q4'],
options: { stroke: { width: 3 } }, // deep-merged with CnChartWidget defaults
height: 280,
// Reserved for a future cycle — not read at render time today:
// dataSource: { url: '/index.php/apps/myapp/api/charts/sla' }
// dataSource: { register: 'cases', schema: 'case', groupBy: 'caseType', aggregate: 'count' }
},
}]

Forwarded props keys (everything else is ignored): chartKind (→ type), series, categories, labels, options, colors, toolbar, legend, height, width, unavailableLabel.

Usage

<template>
<CnDashboardPage
title="Dashboard"
:widgets="WIDGETS"
:layout="layout"
:loading="loading"
:allow-edit="true"
@layout-change="saveLayout"
@edit-toggle="onEditToggle">

<!-- Custom widgets -->
<template #widget-kpis="{ item }">
<CnKpiGrid :items="kpiData" />
</template>

<template #widget-cases-chart="{ item }">
<CnChartWidget type="pie" :series="chartSeries" :labels="chartLabels" />
</template>

<!-- Per-widget header actions -->
<template #widget-kpis-actions="{ item }">
<NcButton type="tertiary" @click="refreshKpis">Refresh</NcButton>
</template>

<template #header-actions>
<NcButton @click="addWidget">Add widget</NcButton>
</template>
</CnDashboardPage>
</template>

<script>
const WIDGETS = [
{ id: 'kpis', title: 'Key Metrics', type: 'custom' },
{ id: 'cases-chart', title: 'Cases by status', type: 'custom', iconClass: 'icon-chart' },
]

const DEFAULT_LAYOUT = [
{ id: 1, widgetId: 'kpis', gridX: 0, gridY: 0, gridWidth: 12, gridHeight: 2, showTitle: false },
{ id: 2, widgetId: 'cases-chart', gridX: 0, gridY: 2, gridWidth: 6, gridHeight: 4 },
]
</script>

Use the useDashboardView composable to manage widget state and layout persistence:

import { useDashboardView } from '@conduction/nextcloud-vue'

const { widgets, layout, loading, onLayoutChange } = useDashboardView({
widgets: WIDGETS,
defaultLayout: DEFAULT_LAYOUT,
loadLayout: () => loadFromConfig('dashboard_layout'),
saveLayout: (l) => saveToConfig('dashboard_layout', l),
})

Props

PropTypeDefaultDescription
titleString''Page title
descriptionString''Description shown below the title
widgetsArray[]Widget definition objects (see Widget definition below)
layoutArray[]Grid placement objects (see Layout item below)
loadingBooleanfalseShow loading spinner instead of the grid
allowEditBooleanfalseShow the Edit/Done toggle button
columnsNumber12Number of grid columns
cellHeightNumber80Grid cell height in pixels
gridMarginNumber12Gap between grid items in pixels
editLabelString'Edit'Label for the edit button
doneLabelString'Done'Label for the done button
emptyLabelString'No widgets configured'Empty state message
unavailableLabelString'Widget not available'Fallback for unknown widget IDs

Widget definition

FieldTypeDescription
idStringUnique widget identifier
titleStringWidget title shown in the wrapper header
typeString'custom' (default), 'tile', or 'chart'. 'chart' mounts CnChartWidget; chart inputs ride props
propsObjectFree-form widget-specific props. For chart widgets: { chartKind, series, categories, labels, options, colors, toolbar, legend, height, width, unavailableLabel, dataSource? }
iconUrlStringHeader icon image URL
iconClassStringHeader icon CSS class
titleIconPositionStringPosition of the widget-{id}-title-icon slot: 'left' (before title) or 'right' (after actions, default)
titleIconColorStringCSS color applied to the title-icon slot container (e.g. '#e74c3c')
buttonsArrayFooter buttons: [{ text, link }]
itemApiVersionsNumber[]NC Dashboard API versions — triggers auto-rendering
reloadIntervalNumberAuto-refresh interval in seconds (NC widgets)

Layout item

FieldTypeDescription
idString | NumberUnique placement ID
widgetIdStringReferences a widget id from the widgets array
gridXNumberColumn start (0-based)
gridYNumberRow start (0-based)
gridWidthNumberWidth in grid columns
gridHeightNumberHeight in grid rows
showTitleBooleanWhether to show the wrapper header (default true)
styleConfigObjectStyle overrides passed to CnWidgetWrapper

Events

EventPayloadDescription
layout-changelayout[]Emitted when the user drags or resizes a widget; payload is the full updated layout array
edit-togglebooleanEmitted when the Edit/Done button is clicked; payload is the new editing state

Slots

SlotScopeDescription
header-actionsExtra buttons in the page header (right side)
widget-{widgetId}{ item, widget }Custom content for a specific widget
widget-{widgetId}-actions{ item, widget }Header action buttons for a specific widget
widget-{widgetId}-title-icon{ item, widget }Extra icon in the widget header; position and color controlled by titleIconPosition / titleIconColor on the widget definition
emptyCustom empty state when no layout items exist

Reference (auto-generated)

The tables below are generated from the SFC source via vue-docgen-cli. They reflect what's actually in CnDashboardPage.vue and update automatically whenever the component changes.

Props

NameTypeRequiredDefaultDescription
titlestring''Page title
descriptionstring''Page description (shown below title)
widgetsArray<{ id: string, title: string, type: string, iconUrl: string, iconClass: string, buttons: Array, itemApiVersions: number[], reloadInterval: number, props: object }>[]Widget definitions array. Each widget defines metadata for rendering. Custom widgets: { id: 'my-widget', title: 'My Widget', type: 'custom' } NC API widgets: { id: 'calendar', title: 'Calendar', itemApiVersions: [1,2], ... } Tile widgets: { id: 'tile-files', type: 'tile', title: 'Files', icon: 'M12...', iconType: 'svg', backgroundColor: '#0082c9', textColor: '#fff', linkType: 'app', linkValue: 'files' } Chart widgets: { id: 'sla', type: 'chart', title: 'SLA trend', props: { chartKind: 'line', series: [{ name: 'SLA %', data: [82, 88, 91] }], categories: ['Q1', 'Q2', 'Q3'], options: { stroke: { width: 3 } } } }
layoutunion[]Layout array defining widget positions in the grid. Each item: { id: 'unique-id', widgetId: 'my-widget', gridX: 0, gridY: 0, gridWidth: 4, gridHeight: 3 } Additional properties (showTitle, styleConfig, tile config) are passed through.
loadingbooleanfalseWhether the dashboard is loading
allowEditbooleanfalseWhether to show the edit toggle button
columnsnumber12Number of grid columns
cellHeightnumber80Grid cell height in pixels
gridMarginnumber12Grid margin in pixels
editLabelstring() =&gt; t('nextcloud-vue', 'Edit')Label for the edit button
doneLabelstring() =&gt; t('nextcloud-vue', 'Done')Label for the done button (when editing)
emptyLabelstring() =&gt; t('nextcloud-vue', 'No widgets configured')Label for the empty state
unavailableLabelstring() =&gt; t('nextcloud-vue', 'Widget not available')Label for unavailable widgets

Events

NamePayloadDescription
layout-change
edit-toggle

Slots

NameBindingsDescription
header-actions
actions
empty
'widget-' + item.widgetId + '-title-icon'name, item, widget
'widget-' + item.widgetId + '-actions'name, item, widget
'widget-' + item.widgetIdname, item, widget