@conduction/nextcloud-vue 0.1.0-beta.2 → 0.1.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -226
- package/css/index.css +5 -0
- package/dist/nextcloud-vue.cjs.js +60455 -8755
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.css +2062 -528
- package/dist/nextcloud-vue.esm.js +60411 -8731
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/package.json +75 -61
- package/src/components/CnActionsBar/CnActionsBar.vue +235 -225
- package/src/components/CnActionsBar/index.js +1 -1
- package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +579 -0
- package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -0
- package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -0
- package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +418 -0
- package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -0
- package/src/components/CnAdvancedFormDialog/index.js +1 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +152 -152
- package/src/components/CnCardGrid/index.js +1 -1
- package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -132
- package/src/components/CnCellRenderer/index.js +1 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +320 -0
- package/src/components/CnChartWidget/index.js +1 -0
- package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -77
- package/src/components/CnConfigurationCard/index.js +1 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +225 -0
- package/src/components/CnDashboardGrid/index.js +1 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +390 -0
- package/src/components/CnDashboardPage/index.js +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +349 -349
- package/src/components/CnDataTable/index.js +1 -1
- package/src/components/CnDetailCard/CnDetailCard.vue +214 -0
- package/src/components/CnDetailCard/index.js +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +281 -0
- package/src/components/CnDetailPage/index.js +1 -0
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +231 -223
- package/src/components/CnFacetSidebar/index.js +1 -1
- package/src/components/CnFilterBar/CnFilterBar.vue +152 -152
- package/src/components/CnFilterBar/index.js +1 -1
- package/src/components/CnIcon/CnIcon.vue +89 -89
- package/src/components/CnIcon/index.js +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +874 -816
- package/src/components/CnIndexPage/index.js +1 -1
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +503 -484
- package/src/components/CnIndexSidebar/index.js +1 -1
- package/src/components/CnItemCard/CnItemCard.vue +132 -0
- package/src/components/CnItemCard/index.js +1 -0
- package/src/components/CnKpiGrid/CnKpiGrid.vue +89 -89
- package/src/components/CnKpiGrid/index.js +1 -1
- package/src/components/CnMassActionBar/CnMassActionBar.vue +160 -160
- package/src/components/CnMassActionBar/index.js +1 -1
- package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +320 -320
- package/src/components/CnMassCopyDialog/index.js +1 -1
- package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +238 -238
- package/src/components/CnMassDeleteDialog/index.js +1 -1
- package/src/components/CnMassExportDialog/CnMassExportDialog.vue +190 -190
- package/src/components/CnMassExportDialog/index.js +1 -1
- package/src/components/CnMassImportDialog/CnMassImportDialog.vue +491 -491
- package/src/components/CnMassImportDialog/index.js +1 -1
- package/src/components/CnNoteCard/CnNoteCard.vue +149 -0
- package/src/components/CnNoteCard/index.js +1 -0
- package/src/components/CnNotesCard/CnNotesCard.vue +413 -0
- package/src/components/CnNotesCard/index.js +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +292 -292
- package/src/components/CnObjectCard/index.js +1 -1
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +876 -0
- package/src/components/CnObjectSidebar/index.js +1 -0
- package/src/components/CnPageHeader/CnPageHeader.vue +57 -57
- package/src/components/CnPageHeader/index.js +1 -1
- package/src/components/CnPagination/CnPagination.vue +252 -252
- package/src/components/CnPagination/index.js +1 -1
- package/src/components/CnRowActions/CnRowActions.vue +73 -73
- package/src/components/CnRowActions/index.js +1 -1
- package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -0
- package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +787 -0
- package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -0
- package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -0
- package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -0
- package/src/components/CnSchemaFormDialog/index.js +1 -0
- package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -92
- package/src/components/CnSettingsCard/index.js +1 -1
- package/src/components/CnSettingsSection/CnSettingsSection.vue +266 -266
- package/src/components/CnSettingsSection/index.js +1 -1
- package/src/components/CnStatsBlock/CnStatsBlock.vue +420 -366
- package/src/components/CnStatsBlock/index.js +1 -1
- package/src/components/CnStatusBadge/CnStatusBadge.vue +77 -77
- package/src/components/CnStatusBadge/index.js +1 -1
- package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +540 -0
- package/src/components/CnTabbedFormDialog/index.js +1 -0
- package/src/components/CnTasksCard/CnTasksCard.vue +373 -0
- package/src/components/CnTasksCard/index.js +1 -0
- package/src/components/CnTileWidget/CnTileWidget.vue +159 -0
- package/src/components/CnTileWidget/index.js +1 -0
- package/src/components/CnTimelineStages/CnTimelineStages.vue +292 -0
- package/src/components/CnTimelineStages/index.js +1 -0
- package/src/components/CnUserActionMenu/CnUserActionMenu.vue +435 -0
- package/src/components/CnUserActionMenu/index.js +1 -0
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +312 -312
- package/src/components/CnVersionInfoCard/index.js +1 -1
- package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -0
- package/src/components/CnWidgetRenderer/index.js +1 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +211 -0
- package/src/components/CnWidgetWrapper/index.js +1 -0
- package/src/components/index.js +43 -29
- package/src/composables/index.js +4 -3
- package/src/composables/useDashboardView.js +240 -0
- package/src/composables/useDetailView.js +289 -132
- package/src/composables/useListView.js +363 -153
- package/src/composables/useSubResource.js +142 -142
- package/src/constants/metadata.js +30 -30
- package/src/css/CnSchemaFormDialog.css +546 -0
- package/src/css/__sample_nextcloud_tokens.css +110 -0
- package/src/css/actions-bar.css +48 -48
- package/src/css/badge.css +51 -51
- package/src/css/card.css +128 -128
- package/src/css/dashboard.css +70 -0
- package/src/css/detail-page.css +168 -0
- package/src/css/detail.css +68 -68
- package/src/css/index-page.css +44 -32
- package/src/css/index-sidebar.css +193 -187
- package/src/css/index.css +16 -12
- package/src/css/layout.css +90 -90
- package/src/css/page-header.css +33 -33
- package/src/css/pagination.css +72 -72
- package/src/css/table.css +142 -142
- package/src/css/timeline-stages.css +218 -0
- package/src/css/utilities.css +46 -46
- package/src/index.js +72 -53
- package/src/store/createSubResourcePlugin.js +135 -135
- package/src/store/index.js +3 -3
- package/src/store/plugins/auditTrails.js +17 -17
- package/src/store/plugins/files.js +250 -186
- package/src/store/plugins/index.js +7 -5
- package/src/store/plugins/lifecycle.js +180 -180
- package/src/store/plugins/relations.js +68 -68
- package/src/store/plugins/search.js +372 -0
- package/src/store/plugins/selection.js +104 -0
- package/src/store/useObjectStore.js +829 -686
- package/src/types/auditTrail.d.ts +32 -32
- package/src/types/file.d.ts +23 -23
- package/src/types/index.d.ts +35 -35
- package/src/types/notification.d.ts +36 -36
- package/src/types/object.d.ts +40 -40
- package/src/types/organisation.d.ts +41 -41
- package/src/types/register.d.ts +25 -25
- package/src/types/schema.d.ts +39 -39
- package/src/types/shared.d.ts +79 -79
- package/src/types/source.d.ts +14 -14
- package/src/types/task.d.ts +31 -31
- package/src/utils/errors.js +96 -96
- package/src/utils/headers.js +68 -50
- package/src/utils/id.js +13 -0
- package/src/utils/index.js +3 -3
- package/src/utils/schema.js +422 -419
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as CnDataTable } from './CnDataTable.vue'
|
|
1
|
+
export { default as CnDataTable } from './CnDataTable.vue'
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
CnDetailCard — A card container for detail page sections.
|
|
3
|
+
|
|
4
|
+
Visually matches the dashboard widget card style (rounded border, header, content).
|
|
5
|
+
Used inside CnDetailPage to organize entity detail information into cards.
|
|
6
|
+
-->
|
|
7
|
+
<template>
|
|
8
|
+
<div class="cn-detail-card" :class="{ 'cn-detail-card--collapsed': isCollapsed }">
|
|
9
|
+
<!-- Header -->
|
|
10
|
+
<div
|
|
11
|
+
v-if="title || $slots.icon"
|
|
12
|
+
class="cn-detail-card__header"
|
|
13
|
+
:class="{ 'cn-detail-card__header--clickable': collapsible }"
|
|
14
|
+
@click="collapsible && toggleCollapse()">
|
|
15
|
+
<div class="cn-detail-card__header-left">
|
|
16
|
+
<slot name="icon">
|
|
17
|
+
<component
|
|
18
|
+
:is="icon"
|
|
19
|
+
v-if="icon"
|
|
20
|
+
:size="20"
|
|
21
|
+
class="cn-detail-card__icon" />
|
|
22
|
+
</slot>
|
|
23
|
+
<h3 class="cn-detail-card__title">
|
|
24
|
+
{{ title }}
|
|
25
|
+
</h3>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="cn-detail-card__header-right">
|
|
28
|
+
<slot name="header-actions" />
|
|
29
|
+
<button
|
|
30
|
+
v-if="collapsible"
|
|
31
|
+
class="cn-detail-card__collapse-btn"
|
|
32
|
+
:aria-label="isCollapsed ? 'Expand' : 'Collapse'">
|
|
33
|
+
<ChevronDown
|
|
34
|
+
:size="20"
|
|
35
|
+
:class="{ 'cn-detail-card__chevron--rotated': isCollapsed }" />
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Content -->
|
|
41
|
+
<div v-show="!isCollapsed" class="cn-detail-card__content">
|
|
42
|
+
<slot />
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<!-- Footer -->
|
|
46
|
+
<div v-if="$slots.footer" v-show="!isCollapsed" class="cn-detail-card__footer">
|
|
47
|
+
<slot name="footer" />
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<script>
|
|
53
|
+
import ChevronDown from 'vue-material-design-icons/ChevronDown.vue'
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* CnDetailCard — Card container for detail page sections.
|
|
57
|
+
*
|
|
58
|
+
* @example Basic usage
|
|
59
|
+
* <CnDetailCard title="Core Info">
|
|
60
|
+
* <div class="info-grid">...</div>
|
|
61
|
+
* </CnDetailCard>
|
|
62
|
+
*
|
|
63
|
+
* @example With icon and actions
|
|
64
|
+
* <CnDetailCard title="Pipeline" :icon="ChartIcon">
|
|
65
|
+
* <template #header-actions>
|
|
66
|
+
* <NcButton>Edit</NcButton>
|
|
67
|
+
* </template>
|
|
68
|
+
* <PipelineProgress :stages="stages" />
|
|
69
|
+
* </CnDetailCard>
|
|
70
|
+
*
|
|
71
|
+
* @example Collapsible
|
|
72
|
+
* <CnDetailCard title="Products" :collapsible="true">
|
|
73
|
+
* <ProductList :items="products" />
|
|
74
|
+
* </CnDetailCard>
|
|
75
|
+
*/
|
|
76
|
+
export default {
|
|
77
|
+
name: 'CnDetailCard',
|
|
78
|
+
|
|
79
|
+
components: {
|
|
80
|
+
ChevronDown,
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
props: {
|
|
84
|
+
/** Card header title */
|
|
85
|
+
title: {
|
|
86
|
+
type: String,
|
|
87
|
+
default: '',
|
|
88
|
+
},
|
|
89
|
+
/** Optional MDI icon component for the header */
|
|
90
|
+
icon: {
|
|
91
|
+
type: [Object, Function],
|
|
92
|
+
default: null,
|
|
93
|
+
},
|
|
94
|
+
/** Whether the card can be collapsed */
|
|
95
|
+
collapsible: {
|
|
96
|
+
type: Boolean,
|
|
97
|
+
default: false,
|
|
98
|
+
},
|
|
99
|
+
/** Initial collapsed state (only relevant when collapsible is true) */
|
|
100
|
+
collapsed: {
|
|
101
|
+
type: Boolean,
|
|
102
|
+
default: false,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
data() {
|
|
107
|
+
return {
|
|
108
|
+
isCollapsed: this.collapsed,
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
watch: {
|
|
113
|
+
collapsed(val) {
|
|
114
|
+
this.isCollapsed = val
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
methods: {
|
|
119
|
+
toggleCollapse() {
|
|
120
|
+
this.isCollapsed = !this.isCollapsed
|
|
121
|
+
this.$emit('update:collapsed', this.isCollapsed)
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<style scoped>
|
|
128
|
+
.cn-detail-card {
|
|
129
|
+
background: var(--color-main-background);
|
|
130
|
+
border: 1px solid var(--color-border);
|
|
131
|
+
border-radius: var(--border-radius-large, 12px);
|
|
132
|
+
overflow: hidden;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.cn-detail-card__header {
|
|
136
|
+
display: flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
justify-content: space-between;
|
|
139
|
+
padding: 12px 16px;
|
|
140
|
+
border-bottom: 1px solid var(--color-border);
|
|
141
|
+
flex-shrink: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.cn-detail-card__header--clickable {
|
|
145
|
+
cursor: pointer;
|
|
146
|
+
user-select: none;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.cn-detail-card__header--clickable:hover {
|
|
150
|
+
background: var(--color-background-hover);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.cn-detail-card--collapsed .cn-detail-card__header {
|
|
154
|
+
border-bottom: none;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.cn-detail-card__header-left {
|
|
158
|
+
display: flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
gap: 8px;
|
|
161
|
+
min-width: 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.cn-detail-card__header-right {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
gap: 4px;
|
|
168
|
+
flex-shrink: 0;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.cn-detail-card__icon {
|
|
172
|
+
color: var(--color-primary-element);
|
|
173
|
+
flex-shrink: 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.cn-detail-card__title {
|
|
177
|
+
font-weight: 600;
|
|
178
|
+
font-size: 14px;
|
|
179
|
+
margin: 0;
|
|
180
|
+
white-space: nowrap;
|
|
181
|
+
overflow: hidden;
|
|
182
|
+
text-overflow: ellipsis;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.cn-detail-card__collapse-btn {
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
justify-content: center;
|
|
189
|
+
background: none;
|
|
190
|
+
border: none;
|
|
191
|
+
padding: 4px;
|
|
192
|
+
cursor: pointer;
|
|
193
|
+
color: var(--color-text-maxcontrast);
|
|
194
|
+
border-radius: var(--border-radius);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.cn-detail-card__collapse-btn:hover {
|
|
198
|
+
background: var(--color-background-dark);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.cn-detail-card__chevron--rotated {
|
|
202
|
+
transform: rotate(-90deg);
|
|
203
|
+
transition: transform 0.2s ease;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.cn-detail-card__content {
|
|
207
|
+
padding: 16px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.cn-detail-card__footer {
|
|
211
|
+
padding: 8px 16px;
|
|
212
|
+
border-top: 1px solid var(--color-border);
|
|
213
|
+
}
|
|
214
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as CnDetailCard } from './CnDetailCard.vue'
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
CnDetailPage — Generic detail/overview page.
|
|
3
|
+
|
|
4
|
+
A simpler alternative to CnIndexPage for detail, stats, and overview pages.
|
|
5
|
+
No multi-object table, no CRUD dialogs — just a clean layout with:
|
|
6
|
+
- Header (title, description, icon, action buttons)
|
|
7
|
+
- Loading / error / empty states
|
|
8
|
+
- Statistics table section
|
|
9
|
+
- Content sections via slots
|
|
10
|
+
-->
|
|
11
|
+
<template>
|
|
12
|
+
<div class="cn-detail-page" :style="{ maxWidth: maxWidth }">
|
|
13
|
+
<!-- Header -->
|
|
14
|
+
<div class="cn-detail-page__header">
|
|
15
|
+
<div class="cn-detail-page__header-left">
|
|
16
|
+
<slot name="icon">
|
|
17
|
+
<CnIcon v-if="icon" :name="icon" :size="iconSize" class="cn-detail-page__icon" />
|
|
18
|
+
</slot>
|
|
19
|
+
<div class="cn-detail-page__header-text">
|
|
20
|
+
<h2 v-if="title" class="cn-detail-page__title">
|
|
21
|
+
{{ title }}
|
|
22
|
+
</h2>
|
|
23
|
+
<p v-if="description" class="cn-detail-page__description">
|
|
24
|
+
{{ description }}
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="cn-detail-page__header-actions">
|
|
29
|
+
<slot name="header-actions" />
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<!-- Loading state -->
|
|
34
|
+
<div v-if="loading" class="cn-detail-page__loading">
|
|
35
|
+
<NcLoadingIcon :size="32" />
|
|
36
|
+
<span>{{ loadingLabel }}</span>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- Error state -->
|
|
40
|
+
<div v-else-if="error" class="cn-detail-page__error">
|
|
41
|
+
<slot name="error">
|
|
42
|
+
<NcEmptyContent :name="errorMessage">
|
|
43
|
+
<template #icon>
|
|
44
|
+
<AlertCircleOutline :size="48" />
|
|
45
|
+
</template>
|
|
46
|
+
<template #action>
|
|
47
|
+
<NcButton v-if="onRetry" type="primary" @click="onRetry">
|
|
48
|
+
<template #icon>
|
|
49
|
+
<Refresh :size="20" />
|
|
50
|
+
</template>
|
|
51
|
+
{{ retryLabel }}
|
|
52
|
+
</NcButton>
|
|
53
|
+
<slot name="error-actions" />
|
|
54
|
+
</template>
|
|
55
|
+
</NcEmptyContent>
|
|
56
|
+
</slot>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- Empty state -->
|
|
60
|
+
<div v-else-if="empty" class="cn-detail-page__empty">
|
|
61
|
+
<slot name="empty">
|
|
62
|
+
<NcEmptyContent :name="emptyLabel">
|
|
63
|
+
<template #icon>
|
|
64
|
+
<InformationOutline :size="48" />
|
|
65
|
+
</template>
|
|
66
|
+
<template #action>
|
|
67
|
+
<slot name="empty-actions" />
|
|
68
|
+
</template>
|
|
69
|
+
</NcEmptyContent>
|
|
70
|
+
</slot>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<!-- Main content -->
|
|
74
|
+
<div v-else class="cn-detail-page__body">
|
|
75
|
+
<!-- Statistics table -->
|
|
76
|
+
<div v-if="hasStats" class="cn-detail-page__stats">
|
|
77
|
+
<slot name="stats-header">
|
|
78
|
+
<h3 v-if="statsTitle" class="cn-detail-page__section-title">
|
|
79
|
+
{{ statsTitle }}
|
|
80
|
+
</h3>
|
|
81
|
+
</slot>
|
|
82
|
+
<table class="cn-detail-page__stats-table">
|
|
83
|
+
<thead v-if="statsColumns.length > 0">
|
|
84
|
+
<tr>
|
|
85
|
+
<th v-for="col in statsColumns" :key="col.key" :class="col.align ? 'cn-detail-page__stats-cell--' + col.align : ''">
|
|
86
|
+
{{ col.label }}
|
|
87
|
+
</th>
|
|
88
|
+
</tr>
|
|
89
|
+
</thead>
|
|
90
|
+
<tbody>
|
|
91
|
+
<slot name="stats-rows">
|
|
92
|
+
<tr v-for="(row, index) in statsRows" :key="index" :class="{ 'cn-detail-page__stats-row--sub': row.indent }">
|
|
93
|
+
<td v-for="col in statsColumns" :key="col.key" :class="[row.indent ? 'cn-detail-page__stats-cell--indented' : '', col.align ? 'cn-detail-page__stats-cell--' + col.align : '']">
|
|
94
|
+
{{ row[col.key] !== undefined ? row[col.key] : '-' }}
|
|
95
|
+
</td>
|
|
96
|
+
</tr>
|
|
97
|
+
</slot>
|
|
98
|
+
</tbody>
|
|
99
|
+
</table>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<!-- Default content -->
|
|
103
|
+
<div class="cn-detail-page__content">
|
|
104
|
+
<slot />
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- Sections slot — additional content below stats -->
|
|
108
|
+
<div v-if="$slots.sections" class="cn-detail-page__sections">
|
|
109
|
+
<slot name="sections" />
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<!-- Footer -->
|
|
114
|
+
<div v-if="$slots.footer" class="cn-detail-page__footer">
|
|
115
|
+
<slot name="footer" />
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</template>
|
|
119
|
+
|
|
120
|
+
<script>
|
|
121
|
+
import { NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
|
122
|
+
import { CnIcon } from '../CnIcon/index.js'
|
|
123
|
+
import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'
|
|
124
|
+
import InformationOutline from 'vue-material-design-icons/InformationOutline.vue'
|
|
125
|
+
import Refresh from 'vue-material-design-icons/Refresh.vue'
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* CnDetailPage — Generic detail/overview page.
|
|
129
|
+
*
|
|
130
|
+
* A simpler alternative to CnIndexPage for pages that display detail info,
|
|
131
|
+
* statistics, charts, or card grids — without multi-object tables or CRUD
|
|
132
|
+
* dialogs. Provides a consistent layout with header, loading/error/empty
|
|
133
|
+
* states, a statistics table, and flexible content slots.
|
|
134
|
+
*
|
|
135
|
+
* @example Basic usage with stats table and content
|
|
136
|
+
* <CnDetailPage
|
|
137
|
+
* title="Register Overview"
|
|
138
|
+
* description="Statistics and schema details"
|
|
139
|
+
* icon="DatabaseOutline"
|
|
140
|
+
* :stats-title="'Register Statistics'"
|
|
141
|
+
* :stats-columns="[
|
|
142
|
+
* { key: 'type', label: 'Type' },
|
|
143
|
+
* { key: 'total', label: 'Total' },
|
|
144
|
+
* { key: 'size', label: 'Size' },
|
|
145
|
+
* ]"
|
|
146
|
+
* :stats-rows="[
|
|
147
|
+
* { type: 'Objects', total: 150, size: '2.4 MB' },
|
|
148
|
+
* { type: 'Files', total: 42, size: '1.1 MB' },
|
|
149
|
+
* ]"
|
|
150
|
+
* :loading="isLoading">
|
|
151
|
+
* <ChartGrid :data="chartData" />
|
|
152
|
+
* <SchemaCards :schemas="schemas" />
|
|
153
|
+
* </CnDetailPage>
|
|
154
|
+
*
|
|
155
|
+
* @example With header actions and error handling
|
|
156
|
+
* <CnDetailPage
|
|
157
|
+
* title="Schema Details"
|
|
158
|
+
* :error="hasError"
|
|
159
|
+
* error-message="Failed to load schema"
|
|
160
|
+
* :on-retry="loadSchema">
|
|
161
|
+
* <template #header-actions>
|
|
162
|
+
* <NcButton @click="editSchema">Edit</NcButton>
|
|
163
|
+
* </template>
|
|
164
|
+
* <DetailContent :schema="schema" />
|
|
165
|
+
* </CnDetailPage>
|
|
166
|
+
*/
|
|
167
|
+
export default {
|
|
168
|
+
name: 'CnDetailPage',
|
|
169
|
+
|
|
170
|
+
components: {
|
|
171
|
+
NcButton,
|
|
172
|
+
NcEmptyContent,
|
|
173
|
+
NcLoadingIcon,
|
|
174
|
+
CnIcon,
|
|
175
|
+
AlertCircleOutline,
|
|
176
|
+
InformationOutline,
|
|
177
|
+
Refresh,
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
props: {
|
|
181
|
+
/** Page title */
|
|
182
|
+
title: {
|
|
183
|
+
type: String,
|
|
184
|
+
default: '',
|
|
185
|
+
},
|
|
186
|
+
/** Page description (shown below title) */
|
|
187
|
+
description: {
|
|
188
|
+
type: String,
|
|
189
|
+
default: '',
|
|
190
|
+
},
|
|
191
|
+
/** Optional MDI icon name (rendered via CnIcon) */
|
|
192
|
+
icon: {
|
|
193
|
+
type: String,
|
|
194
|
+
default: '',
|
|
195
|
+
},
|
|
196
|
+
/** Icon size in pixels */
|
|
197
|
+
iconSize: {
|
|
198
|
+
type: Number,
|
|
199
|
+
default: 28,
|
|
200
|
+
},
|
|
201
|
+
/** Whether the page is in a loading state */
|
|
202
|
+
loading: {
|
|
203
|
+
type: Boolean,
|
|
204
|
+
default: false,
|
|
205
|
+
},
|
|
206
|
+
/** Message shown during loading */
|
|
207
|
+
loadingLabel: {
|
|
208
|
+
type: String,
|
|
209
|
+
default: 'Loading...',
|
|
210
|
+
},
|
|
211
|
+
/** Whether the page is in an error state */
|
|
212
|
+
error: {
|
|
213
|
+
type: Boolean,
|
|
214
|
+
default: false,
|
|
215
|
+
},
|
|
216
|
+
/** Error message shown in error state */
|
|
217
|
+
errorMessage: {
|
|
218
|
+
type: String,
|
|
219
|
+
default: 'An error occurred',
|
|
220
|
+
},
|
|
221
|
+
/** Callback for retry button in error state. If null, no retry button is shown. */
|
|
222
|
+
onRetry: {
|
|
223
|
+
type: Function,
|
|
224
|
+
default: null,
|
|
225
|
+
},
|
|
226
|
+
/** Label for the retry button */
|
|
227
|
+
retryLabel: {
|
|
228
|
+
type: String,
|
|
229
|
+
default: 'Retry',
|
|
230
|
+
},
|
|
231
|
+
/** Whether the page has no data to show */
|
|
232
|
+
empty: {
|
|
233
|
+
type: Boolean,
|
|
234
|
+
default: false,
|
|
235
|
+
},
|
|
236
|
+
/** Message shown when page is empty */
|
|
237
|
+
emptyLabel: {
|
|
238
|
+
type: String,
|
|
239
|
+
default: 'No data available',
|
|
240
|
+
},
|
|
241
|
+
/** Title shown above the statistics table */
|
|
242
|
+
statsTitle: {
|
|
243
|
+
type: String,
|
|
244
|
+
default: '',
|
|
245
|
+
},
|
|
246
|
+
/**
|
|
247
|
+
* Column definitions for the statistics table.
|
|
248
|
+
* Each column: `{ key: string, label: string, align?: 'left'|'center'|'right' }`
|
|
249
|
+
*
|
|
250
|
+
* @type {Array<{ key: string, label: string, align?: string }>}
|
|
251
|
+
*/
|
|
252
|
+
statsColumns: {
|
|
253
|
+
type: Array,
|
|
254
|
+
default: () => [],
|
|
255
|
+
},
|
|
256
|
+
/**
|
|
257
|
+
* Row data for the statistics table. Each row is an object keyed by
|
|
258
|
+
* column keys. Set `indent: true` on a row for sub-row styling.
|
|
259
|
+
*
|
|
260
|
+
* @type {Array<Object>}
|
|
261
|
+
*/
|
|
262
|
+
statsRows: {
|
|
263
|
+
type: Array,
|
|
264
|
+
default: () => [],
|
|
265
|
+
},
|
|
266
|
+
/** Maximum width of the page content */
|
|
267
|
+
maxWidth: {
|
|
268
|
+
type: String,
|
|
269
|
+
default: '1200px',
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
computed: {
|
|
274
|
+
hasStats() {
|
|
275
|
+
return this.statsColumns.length > 0 && (this.statsRows.length > 0 || !!this.$slots['stats-rows'])
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
</script>
|
|
280
|
+
|
|
281
|
+
<!-- Styles in css/detail-page.css -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as CnDetailPage } from './CnDetailPage.vue'
|