@conduction/nextcloud-vue 2.56.0-beta.34 → 2.56.0-beta.36
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/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue.js +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue2.js +22 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue2.js.map +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue3.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +47 -4
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +239 -3
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +5 -5
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +5 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue.js +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +203 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +38 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +45 -5
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue.js +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js +29 -6
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +126 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +24 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +37 -2
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue.js +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js +135 -2
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js +95 -43
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue.js +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +72 -5
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +33 -8
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue.js +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue.js +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +17 -2
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +33 -3
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue.js +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +16 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +2 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue.js +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js +68 -6
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js +133 -105
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +16 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +38 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +525 -318
- package/dist/esm/utils/buildManifest.js +37 -3
- package/dist/esm/utils/buildManifest.js.map +1 -1
- package/dist/esm/utils/detailWidgetVisibility.js +149 -0
- package/dist/esm/utils/detailWidgetVisibility.js.map +1 -0
- package/dist/esm/utils/formatMetric.js +39 -3
- package/dist/esm/utils/formatMetric.js.map +1 -1
- package/dist/esm/utils/gridStack.js +40 -1
- package/dist/esm/utils/gridStack.js.map +1 -1
- package/dist/esm/utils/humaniseKey.js +41 -0
- package/dist/esm/utils/humaniseKey.js.map +1 -0
- package/dist/esm/utils/notificationRuleLabel.js +6 -23
- package/dist/esm/utils/notificationRuleLabel.js.map +1 -1
- package/dist/esm/utils/resolveManifestTokens.js +1 -0
- package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +9 -0
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +2250 -708
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +525 -318
- package/l10n/en.json +16 -1
- package/l10n/nl.json +38 -1
- package/package.json +1 -1
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +1 -1
- package/src/components/CnBodySections/CnBodySections.vue +22 -1
- package/src/components/CnCountdownWidget/CnCountdownWidget.vue +47 -4
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +244 -8
- package/src/components/CnDashboardPage/CnDashboardPage.vue +5 -0
- package/src/components/CnDeltaWidget/CnDeltaWidget.vue +1 -1
- package/src/components/CnDetailPage/CnDetailPage.md +2 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +203 -4
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +63 -1
- package/src/components/CnGaugeWidget/CnGaugeWidget.vue +1 -1
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +29 -6
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.md +1 -1
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +154 -5
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +37 -2
- package/src/components/CnReportsPage/CnReportsPage.vue +179 -1
- package/src/components/CnStagesWidget/CnStagesWidget.vue +103 -6
- package/src/components/CnStatWidget/CnStatWidget.vue +1 -1
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +34 -3
- package/src/components/CnTabsWidget/CnTabsWidget.vue +33 -3
- package/src/components/CnWalkthrough/CnWalkthrough.vue +16 -0
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +2 -1
- package/src/css/form-field.css +14 -0
- package/src/css/form-widget.css +11 -0
- package/src/css/kpi-card.css +13 -0
- package/src/css/look-board-reports.css +58 -0
- package/src/css/look-board.css +31 -0
- package/src/dialogs/CnTransitionInputDialog.vue +87 -7
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +23 -2
- package/src/utils/buildManifest.js +36 -2
- package/src/utils/detailWidgetVisibility.js +145 -0
- package/src/utils/formatMetric.js +38 -2
- package/src/utils/gridStack.js +39 -0
- package/src/utils/humaniseKey.js +38 -0
- package/src/utils/notificationRuleLabel.js +5 -23
- package/src/utils/resolveManifestTokens.js +1 -0
- package/src/utils/sentinelTokens.js +9 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -59,8 +59,15 @@
|
|
|
59
59
|
class="cn-stages-widget__bar-label"
|
|
60
60
|
:title="stage.hint || stage.label"
|
|
61
61
|
:aria-disabled="stage.disabled && stage.clickable ? 'true' : null"
|
|
62
|
+
:aria-busy="movingTo === stage.id ? 'true' : null"
|
|
62
63
|
:data-testid="`cn-stages-widget-stage-${stage.id}`"
|
|
63
64
|
@click="onBarClick(stage)">
|
|
65
|
+
<NcLoadingIcon
|
|
66
|
+
v-if="movingTo === stage.id"
|
|
67
|
+
class="cn-stages-widget__moving"
|
|
68
|
+
:size="16"
|
|
69
|
+
:name="movingMessage"
|
|
70
|
+
data-testid="cn-stages-widget-moving" />
|
|
64
71
|
{{ stage.label }}<span v-if="stage.state === 'current'" class="cn-stages-widget__sr-only"> ({{ tr('current step') }})</span>
|
|
65
72
|
</component>
|
|
66
73
|
<span v-if="stage.date" class="cn-stages-widget__bar-date">{{ stage.date }}</span>
|
|
@@ -83,7 +90,19 @@
|
|
|
83
90
|
@stageClick="onStageClick"
|
|
84
91
|
@stageBlocked="onStageBlocked">
|
|
85
92
|
<template #label="{ stage }">
|
|
86
|
-
<span
|
|
93
|
+
<span
|
|
94
|
+
class="cn-timeline-stages__label"
|
|
95
|
+
:aria-busy="movingTo === stage.id ? 'true' : null"
|
|
96
|
+
:data-testid="`cn-stages-widget-stage-${stage.id}`">
|
|
97
|
+
<!-- The move in flight. A transition can take seconds, and
|
|
98
|
+
a strip that shows nothing until it returns reads as
|
|
99
|
+
one that never moved. -->
|
|
100
|
+
<NcLoadingIcon
|
|
101
|
+
v-if="movingTo === stage.id"
|
|
102
|
+
class="cn-stages-widget__moving"
|
|
103
|
+
:size="16"
|
|
104
|
+
:name="movingMessage"
|
|
105
|
+
data-testid="cn-stages-widget-moving" />
|
|
87
106
|
{{ stage.label }}
|
|
88
107
|
</span>
|
|
89
108
|
<span v-if="stage.subtitle" class="cn-timeline-stages__subtitle">
|
|
@@ -156,8 +175,11 @@
|
|
|
156
175
|
v-if="pendingAction"
|
|
157
176
|
:transition="pendingAction"
|
|
158
177
|
:schema="schema"
|
|
178
|
+
:error="inputError"
|
|
179
|
+
:fieldErrors="inputFieldErrors"
|
|
180
|
+
:busy="busy"
|
|
159
181
|
@confirm="onInputConfirm"
|
|
160
|
-
@close="
|
|
182
|
+
@close="closeInputDialog" />
|
|
161
183
|
</div>
|
|
162
184
|
</template>
|
|
163
185
|
|
|
@@ -177,6 +199,7 @@ import {
|
|
|
177
199
|
performTransition,
|
|
178
200
|
readAvailableActions,
|
|
179
201
|
transitionError,
|
|
202
|
+
transitionFieldErrors,
|
|
180
203
|
} from '../../composables/useLifecycleTransitions.js'
|
|
181
204
|
import { normalizeLook } from '../../composables/useLook.js'
|
|
182
205
|
import { useObjectStore } from '../../store/useObjectStore.js'
|
|
@@ -534,6 +557,17 @@ export default {
|
|
|
534
557
|
pendingAction: null,
|
|
535
558
|
/** @type {boolean} Whether a move is running. */
|
|
536
559
|
busy: false,
|
|
560
|
+
/**
|
|
561
|
+
* @type {string} The stage a running move leads to, or ''. Drives
|
|
562
|
+
* the loading indicator on that one stage.
|
|
563
|
+
*/
|
|
564
|
+
movingTo: '',
|
|
565
|
+
/** @type {string} A refusal shown inside the open input dialog. */
|
|
566
|
+
inputError: '',
|
|
567
|
+
/** @type {string[]} The input keys that refusal named. */
|
|
568
|
+
inputFieldErrors: [],
|
|
569
|
+
/** @type {string[]} The input keys the last refused move named. */
|
|
570
|
+
lastFieldErrors: [],
|
|
537
571
|
/** @type {string} Why the last move failed, or ''. */
|
|
538
572
|
moveError: '',
|
|
539
573
|
/**
|
|
@@ -766,6 +800,20 @@ export default {
|
|
|
766
800
|
return this.interactive && !this.busy
|
|
767
801
|
},
|
|
768
802
|
|
|
803
|
+
/**
|
|
804
|
+
* What the stage a move is running to says while it runs: the
|
|
805
|
+
* loading indicator's name and the live region's sentence.
|
|
806
|
+
*
|
|
807
|
+
* @return {string} The sentence, or '' when nothing is moving.
|
|
808
|
+
*/
|
|
809
|
+
movingMessage() {
|
|
810
|
+
if (!this.movingTo) {
|
|
811
|
+
return ''
|
|
812
|
+
}
|
|
813
|
+
const stage = this.stages.find((s) => s.id === this.movingTo)
|
|
814
|
+
return this.tr('Moving to {stage}', { stage: stage ? stage.label : this.movingTo })
|
|
815
|
+
},
|
|
816
|
+
|
|
769
817
|
/**
|
|
770
818
|
* The allowed moves by the stage they lead to, or null while they are
|
|
771
819
|
* unknown.
|
|
@@ -1225,13 +1273,44 @@ export default {
|
|
|
1225
1273
|
* @param {object} data The collected input values.
|
|
1226
1274
|
* @return {void}
|
|
1227
1275
|
*/
|
|
1228
|
-
onInputConfirm(data) {
|
|
1276
|
+
async onInputConfirm(data) {
|
|
1229
1277
|
const pending = this.pendingAction
|
|
1230
|
-
this.pendingAction = null
|
|
1231
1278
|
if (!pending || !pending.__request) {
|
|
1279
|
+
this.closeInputDialog()
|
|
1280
|
+
return
|
|
1281
|
+
}
|
|
1282
|
+
this.inputError = ''
|
|
1283
|
+
this.inputFieldErrors = []
|
|
1284
|
+
// THE DIALOG STAYS OPEN WHILE THE MOVE RUNS, with its confirm
|
|
1285
|
+
// button busy. Closing it first left the person looking at an
|
|
1286
|
+
// unchanged strip for as long as the transition took, which on a
|
|
1287
|
+
// live case was long enough to reload the page thinking it had
|
|
1288
|
+
// not been sent.
|
|
1289
|
+
const moved = await this.performMove(pending.__request, data)
|
|
1290
|
+
if (this.pendingAction !== pending) {
|
|
1291
|
+
// Closed while the move ran; nothing left to update.
|
|
1232
1292
|
return
|
|
1233
1293
|
}
|
|
1234
|
-
|
|
1294
|
+
if (moved) {
|
|
1295
|
+
this.closeInputDialog()
|
|
1296
|
+
return
|
|
1297
|
+
}
|
|
1298
|
+
// Refused. The sentence goes into the dialog the person is looking
|
|
1299
|
+
// at, with what they typed still in it.
|
|
1300
|
+
this.inputError = this.moveError
|
|
1301
|
+
this.inputFieldErrors = this.lastFieldErrors
|
|
1302
|
+
this.moveError = ''
|
|
1303
|
+
},
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* Close the input dialog and forget the refusal it was showing.
|
|
1307
|
+
*
|
|
1308
|
+
* @return {void}
|
|
1309
|
+
*/
|
|
1310
|
+
closeInputDialog() {
|
|
1311
|
+
this.pendingAction = null
|
|
1312
|
+
this.inputError = ''
|
|
1313
|
+
this.inputFieldErrors = []
|
|
1235
1314
|
},
|
|
1236
1315
|
|
|
1237
1316
|
/**
|
|
@@ -1239,11 +1318,17 @@ export default {
|
|
|
1239
1318
|
*
|
|
1240
1319
|
* @param {{stage: {id: string, label: string}, action: string}} request The move.
|
|
1241
1320
|
* @param {object} [data] The collected transition inputs, when any.
|
|
1242
|
-
* @return {Promise<
|
|
1321
|
+
* @return {Promise<boolean>} Whether the move was made.
|
|
1243
1322
|
*/
|
|
1244
1323
|
async performMove(request, data) {
|
|
1245
1324
|
this.busy = true
|
|
1325
|
+
this.movingTo = request.stage.id
|
|
1246
1326
|
this.moveError = ''
|
|
1327
|
+
this.lastFieldErrors = []
|
|
1328
|
+
// Said as the move starts, not only when it lands: a screen reader
|
|
1329
|
+
// user otherwise hears nothing for as long as the server takes.
|
|
1330
|
+
this.statusMessage = this.movingMessage
|
|
1331
|
+
let moved = false
|
|
1247
1332
|
try {
|
|
1248
1333
|
if (this.lifecycleMode) {
|
|
1249
1334
|
await performTransition(this.recordId, request.action, data)
|
|
@@ -1270,11 +1355,16 @@ export default {
|
|
|
1270
1355
|
if (this.lifecycleMode) {
|
|
1271
1356
|
await this.loadActions()
|
|
1272
1357
|
}
|
|
1358
|
+
moved = true
|
|
1273
1359
|
} catch (error) {
|
|
1274
1360
|
this.moveError = transitionError(error, this.tr('The move could not be made'))
|
|
1361
|
+
this.lastFieldErrors = transitionFieldErrors(error)
|
|
1362
|
+
this.statusMessage = this.moveError
|
|
1275
1363
|
} finally {
|
|
1276
1364
|
this.busy = false
|
|
1365
|
+
this.movingTo = ''
|
|
1277
1366
|
}
|
|
1367
|
+
return moved
|
|
1278
1368
|
},
|
|
1279
1369
|
|
|
1280
1370
|
/**
|
|
@@ -1501,6 +1591,13 @@ export default {
|
|
|
1501
1591
|
padding: 8px 0;
|
|
1502
1592
|
}
|
|
1503
1593
|
|
|
1594
|
+
/* The stage a running move leads to: a small spinner before its label. */
|
|
1595
|
+
.cn-stages-widget__moving {
|
|
1596
|
+
display: inline-flex;
|
|
1597
|
+
vertical-align: middle;
|
|
1598
|
+
margin-inline-end: 4px;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1504
1601
|
.cn-stages-widget__notice {
|
|
1505
1602
|
margin: 0;
|
|
1506
1603
|
color: var(--color-text-maxcontrast);
|
|
@@ -1460,7 +1460,7 @@ export default {
|
|
|
1460
1460
|
* @return {string}
|
|
1461
1461
|
*/
|
|
1462
1462
|
formattedValue() {
|
|
1463
|
-
return formatMetricValue(this.displayValue, this.content.format, this.configCtx, this.objectCtx?.object || null)
|
|
1463
|
+
return formatMetricValue(this.displayValue, this.content.format, this.configCtx, this.objectCtx?.object || null, this.effectiveTranslate)
|
|
1464
1464
|
},
|
|
1465
1465
|
|
|
1466
1466
|
/**
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<template>
|
|
18
18
|
<div
|
|
19
19
|
class="cn-stats-block-widget"
|
|
20
|
-
:class="[iconClass, { 'cn-stats-block-widget--multi': hasEntries && !isRows && !isGrid, 'cn-stats-block-widget--rows': isRows, 'cn-stats-block-widget--grid': isGrid }]">
|
|
20
|
+
:class="[iconClass, { 'cn-stats-block-widget--multi': hasEntries && !isRows && !isGrid, 'cn-stats-block-widget--rows': isRows, 'cn-stats-block-widget--grid': isGrid, 'cn-stats-block-widget--tiles-box': isGrid && tiles === 'box' }]">
|
|
21
21
|
<!-- Grid layout: the `stats` (each a stat tile's content, so every
|
|
22
22
|
source kind a stat takes: ratio, elapsed, computed...) in `columns`
|
|
23
23
|
columns, inside this one card (the board's "Je week tot nu toe"
|
|
@@ -288,6 +288,21 @@ export default {
|
|
|
288
288
|
default: 2,
|
|
289
289
|
},
|
|
290
290
|
|
|
291
|
+
/**
|
|
292
|
+
* How the grid layout draws its tiles: `'box'` gives each tile a
|
|
293
|
+
* surface of its own (a grey ground, 10px radius, 14px 16px padding),
|
|
294
|
+
* as board DcRapportages draws its efficiency figures. Empty (the
|
|
295
|
+
* default) keeps the tiles as parts of the one card, without a box
|
|
296
|
+
* (board PqMijnWerk "Je week tot nu toe").
|
|
297
|
+
*
|
|
298
|
+
* @type {'box'|null}
|
|
299
|
+
* @spec openspec/changes/screens-stats-block-tile-box/specs/stats-block-grid/spec.md#requirement-a-stats-block-grid-can-box-its-tiles
|
|
300
|
+
*/
|
|
301
|
+
tiles: {
|
|
302
|
+
type: String,
|
|
303
|
+
default: null,
|
|
304
|
+
},
|
|
305
|
+
|
|
291
306
|
/**
|
|
292
307
|
* How the single-source value is shown: a style name (`'currency'`,
|
|
293
308
|
* `'number'`, `'percent'`, `'decimal'`, `'duration-hours'`) or
|
|
@@ -566,7 +581,7 @@ export default {
|
|
|
566
581
|
if (!fmt || this.loading) {
|
|
567
582
|
return null
|
|
568
583
|
}
|
|
569
|
-
return formatMetricValue(this.resolvedCount, fmt, this.configCtx, this.pageObject)
|
|
584
|
+
return formatMetricValue(this.resolvedCount, fmt, this.configCtx, this.pageObject, this.tl)
|
|
570
585
|
},
|
|
571
586
|
|
|
572
587
|
/**
|
|
@@ -598,7 +613,7 @@ export default {
|
|
|
598
613
|
return {
|
|
599
614
|
// The value as the entry's `format` says, money in the
|
|
600
615
|
// currency the entry names; null keeps the plain count.
|
|
601
|
-
formatted: fmt && resolved ? formatMetricValue(raw, fmt, this.configCtx, this.pageObject) : null,
|
|
616
|
+
formatted: fmt && resolved ? formatMetricValue(raw, fmt, this.configCtx, this.pageObject, this.tl) : null,
|
|
602
617
|
entry: entry || {},
|
|
603
618
|
key: `${(entry && entry.register) || ''}/${(entry && entry.schema) || ''}/${i}`,
|
|
604
619
|
count: resolved ? raw : 0,
|
|
@@ -961,7 +976,10 @@ export default {
|
|
|
961
976
|
color: var(--color-text-maxcontrast);
|
|
962
977
|
}
|
|
963
978
|
|
|
979
|
+
/* `width: 100%` plus this padding overflowed the card by 48px: the last
|
|
980
|
+
column ran past the card's right edge (visible once the tiles have a box). */
|
|
964
981
|
.cn-stats-block-widget--grid {
|
|
982
|
+
box-sizing: border-box;
|
|
965
983
|
padding: 4px 24px 20px;
|
|
966
984
|
}
|
|
967
985
|
|
|
@@ -977,6 +995,19 @@ export default {
|
|
|
977
995
|
min-width: 0;
|
|
978
996
|
}
|
|
979
997
|
|
|
998
|
+
/* `tiles: "box"`: each tile on a grey ground of its own (board DcRapportages,
|
|
999
|
+
"Vergaderingen en efficiëntie"), 12px apart. */
|
|
1000
|
+
.cn-stats-block-widget--tiles-box .cn-stats-block-widget__grid {
|
|
1001
|
+
gap: 12px;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
.cn-stats-block-widget--tiles-box .cn-stats-block-widget__grid-stat {
|
|
1005
|
+
box-sizing: border-box;
|
|
1006
|
+
padding: 14px 16px;
|
|
1007
|
+
border-radius: 10px;
|
|
1008
|
+
background: var(--color-background-hover);
|
|
1009
|
+
}
|
|
1010
|
+
|
|
980
1011
|
.cn-stats-block-widget__rows {
|
|
981
1012
|
display: flex;
|
|
982
1013
|
flex-direction: column;
|
|
@@ -311,6 +311,12 @@ export default {
|
|
|
311
311
|
cnLook: { default: 'nextcloud' },
|
|
312
312
|
/** The tab list's name under the board look, provided by CnDetailPage (`config.tabsLabel`). */
|
|
313
313
|
cnDetailTabsLabel: { default: null },
|
|
314
|
+
/**
|
|
315
|
+
* The records the detail page's `visibilityContext` names, keyed
|
|
316
|
+
* by name (a case's case type), provided by CnDetailPage. A tab's
|
|
317
|
+
* `visibleWhen` reads them as `@context.<name>`. Null on any other page.
|
|
318
|
+
*/
|
|
319
|
+
cnDetailVisibilityRows: { default: null },
|
|
314
320
|
/**
|
|
315
321
|
* The host's translate (CnAppRoot provides `cnTranslate`), so a tab
|
|
316
322
|
* label written in the manifest reads in the user's language.
|
|
@@ -475,6 +481,29 @@ export default {
|
|
|
475
481
|
},
|
|
476
482
|
|
|
477
483
|
computed: {
|
|
484
|
+
/**
|
|
485
|
+
* What a tab's `visibleWhen` reads: the record, with the records the
|
|
486
|
+
* detail page's `visibilityContext` names under `@context.<name>`. Null while
|
|
487
|
+
* the record, or one of those records, is not there yet.
|
|
488
|
+
*
|
|
489
|
+
* @return {object|null}
|
|
490
|
+
* @spec openspec/changes/detail-widgets-visible-when/specs/detail-widgets-visible-when/spec.md#requirement-a-detail-page-can-load-records-a-condition-names
|
|
491
|
+
*/
|
|
492
|
+
conditionData() {
|
|
493
|
+
if (!this.objectData) {
|
|
494
|
+
return null
|
|
495
|
+
}
|
|
496
|
+
const holder = this.cnDetailVisibilityRows
|
|
497
|
+
if (!holder) {
|
|
498
|
+
return this.objectData
|
|
499
|
+
}
|
|
500
|
+
const rows = holder.value
|
|
501
|
+
if (!rows) {
|
|
502
|
+
return null
|
|
503
|
+
}
|
|
504
|
+
return { ...this.objectData, '@context': rows }
|
|
505
|
+
},
|
|
506
|
+
|
|
478
507
|
/**
|
|
479
508
|
* The open panel's own menu items, or an empty list.
|
|
480
509
|
*
|
|
@@ -859,10 +888,11 @@ export default {
|
|
|
859
888
|
return 'visible'
|
|
860
889
|
}
|
|
861
890
|
if (isLocallyDecidableVisibleWhen(cond)) {
|
|
862
|
-
|
|
891
|
+
const data = this.conditionData
|
|
892
|
+
if (!data) {
|
|
863
893
|
return 'pending'
|
|
864
894
|
}
|
|
865
|
-
return evaluateVisibleWhenLocal(cond,
|
|
895
|
+
return evaluateVisibleWhenLocal(cond, data) ? 'visible' : 'hidden'
|
|
866
896
|
}
|
|
867
897
|
const answer = this.sourceVisibility[index]
|
|
868
898
|
if (answer === undefined) {
|
|
@@ -883,7 +913,7 @@ export default {
|
|
|
883
913
|
async refreshSourceVisibility() {
|
|
884
914
|
const tabs = Array.isArray(this.content?.tabs) ? this.content.tabs : []
|
|
885
915
|
const seq = ++this.visibilitySeq
|
|
886
|
-
const ctx = { objectId: this.objectId, object: this.objectData || undefined }
|
|
916
|
+
const ctx = { objectId: this.objectId, object: this.conditionData || this.objectData || undefined }
|
|
887
917
|
await Promise.all(tabs.map(async (tab, index) => {
|
|
888
918
|
const cond = tab && typeof tab === 'object' ? tab.visibleWhen : null
|
|
889
919
|
if (!cond || isLocallyDecidableVisibleWhen(cond)) {
|
|
@@ -683,11 +683,27 @@ export default {
|
|
|
683
683
|
* deferred tour) go through here — so a centered step cannot be
|
|
684
684
|
* swallowed by either.
|
|
685
685
|
*
|
|
686
|
+
* A tour that declares `startOn` opens only on one of those route
|
|
687
|
+
* names, whatever its first step looks like.
|
|
688
|
+
*
|
|
686
689
|
* @param {object} tour The tour definition.
|
|
687
690
|
* @param {string} [routeName] The current route name.
|
|
688
691
|
* @return {boolean} True when the tour may open on this route.
|
|
692
|
+
* @spec openspec/changes/a-tour-starts-on-the-pages-it-names/specs/walkthrough/spec.md
|
|
689
693
|
*/
|
|
690
694
|
routeMatchesTour(tour, routeName) {
|
|
695
|
+
// A tour that names where it starts (`startOn`) opens only there.
|
|
696
|
+
// It wins over the centered-step rule below: a welcome card floats
|
|
697
|
+
// in the middle of the screen, but a user who opens a case from a
|
|
698
|
+
// mail link lands on that case, and the card then blocks the page
|
|
699
|
+
// they came for. Measured 13 October on dossiq: a staff user's first
|
|
700
|
+
// visit was a deep link to a Woo request, and "Welcome to Dossiq"
|
|
701
|
+
// opened over it. The tour waits in `_pendingAutoTour` and opens when
|
|
702
|
+
// the user reaches a named route.
|
|
703
|
+
const startOn = Array.isArray(tour && tour.startOn) ? tour.startOn.map(String) : []
|
|
704
|
+
if (startOn.length > 0) {
|
|
705
|
+
return !!routeName && startOn.includes(String(routeName))
|
|
706
|
+
}
|
|
691
707
|
const page = this.firstStepPage(tour)
|
|
692
708
|
if (!page) {
|
|
693
709
|
return true
|
|
@@ -89,7 +89,8 @@ const DEFAULT_LIMIT = 100
|
|
|
89
89
|
*
|
|
90
90
|
* It renders the five working days by default (`days: 7` adds the weekend),
|
|
91
91
|
* highlights today, marks items that are late, and says so when a day has
|
|
92
|
-
* nothing. On a narrow screen the strip scrolls sideways
|
|
92
|
+
* nothing. On a narrow screen the strip scrolls sideways; under the board
|
|
93
|
+
* look (`.cn-look-board`) the days wrap into rows instead.
|
|
93
94
|
*
|
|
94
95
|
* Items come from an OpenRegister source (one request for the whole week) or
|
|
95
96
|
* from static `items`. Resolved by its registry type key `week-strip`.
|
package/src/css/form-field.css
CHANGED
|
@@ -183,3 +183,17 @@
|
|
|
183
183
|
.cn-look-board .cn-form-page__footer .cn-form-page__primary {
|
|
184
184
|
margin-inline-start: auto;
|
|
185
185
|
}
|
|
186
|
+
|
|
187
|
+
/* A Nextcloud text area or text field with its label inside the field hides
|
|
188
|
+
its placeholder until the field takes focus, so the label and the
|
|
189
|
+
placeholder are never drawn over each other. A theme that forces every
|
|
190
|
+
placeholder visible (thematiq's nldesign system sets
|
|
191
|
+
`::placeholder { opacity: 1 !important }`) drew both: the dossiq DqTaak
|
|
192
|
+
notes field read "Een notitie toevoegen" over "Wat heb je gevonden, wie heb
|
|
193
|
+
je gebeld?". Under the board look the placeholder stays hidden at rest, as
|
|
194
|
+
Nextcloud intends; a field with its label outside keeps its placeholder.
|
|
195
|
+
@spec openspec/changes/board-field-label-over-placeholder/specs/form-field-board-look/spec.md#requirement-a-field-label-and-its-placeholder-are-never-drawn-over-each-other */
|
|
196
|
+
.cn-look-board .textarea__input:not(:focus, .textarea__input--label-outside)::placeholder,
|
|
197
|
+
.cn-look-board .input-field:not(.input-field--label-outside) .input-field__input:not(:focus)::placeholder {
|
|
198
|
+
opacity: 0 !important;
|
|
199
|
+
}
|
package/src/css/form-widget.css
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
.cn-form-widget__textarea {
|
|
40
|
+
box-sizing: border-box;
|
|
40
41
|
width: 100%;
|
|
41
42
|
min-height: 80px;
|
|
42
43
|
padding: 8px;
|
|
@@ -58,3 +59,13 @@
|
|
|
58
59
|
color: var(--color-error);
|
|
59
60
|
margin: 0;
|
|
60
61
|
}
|
|
62
|
+
|
|
63
|
+
/* A select takes its field's width, however narrow the card. NcSelect is at
|
|
64
|
+
least 260px wide on its own, so in pipelinq's 234px "Nieuw contactmoment"
|
|
65
|
+
card at 1100 wide the channel and client selects ran 40px past the card's
|
|
66
|
+
edge. The client picker (CnResourceSelect) is an NcSelect too.
|
|
67
|
+
@spec openspec/changes/interaction-form-fits-and-reads-dutch/specs/interaction-form-widget/spec.md#requirement-the-interaction-form-fits-its-card-and-reads-in-the-readers-language */
|
|
68
|
+
.cn-form-widget .v-select.select {
|
|
69
|
+
min-width: 0;
|
|
70
|
+
width: 100%;
|
|
71
|
+
}
|
package/src/css/kpi-card.css
CHANGED
|
@@ -444,6 +444,19 @@ a.cn-kpi-card--stacked:focus-visible {
|
|
|
444
444
|
color: var(--color-main-text);
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
+
/* A board tile whose label wraps grows into its cell instead of clipping its
|
|
448
|
+
own last line. dossiq's "Afgehandeld deze maand" at 1100 wide held 129px of
|
|
449
|
+
content in a 114px tile and lost its caption's bottom. The tile still fills
|
|
450
|
+
a cell it fits (`min-height: 100%`), so every tile that fits keeps its
|
|
451
|
+
size; one that does not takes the height of its content, and the card-fit
|
|
452
|
+
cell's `safe center` keeps its top in view.
|
|
453
|
+
@spec openspec/changes/board-stat-tile-grows/specs/stat-tile-board-look/spec.md#requirement-a-board-stat-tile-shows-all-its-lines */
|
|
454
|
+
.cn-kpi-card--board.cn-kpi-card--stacked {
|
|
455
|
+
flex: 0 0 auto;
|
|
456
|
+
height: auto;
|
|
457
|
+
min-height: 100%;
|
|
458
|
+
}
|
|
459
|
+
|
|
447
460
|
.cn-kpi-card--board.cn-kpi-card--stacked .cn-kpi-card__caption-line,
|
|
448
461
|
.cn-kpi-card--board.cn-kpi-card--stacked .cn-stats-block__count-label {
|
|
449
462
|
font-size: 13px;
|
|
@@ -115,3 +115,61 @@
|
|
|
115
115
|
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-dashboard-page {
|
|
116
116
|
padding: 0;
|
|
117
117
|
}
|
|
118
|
+
|
|
119
|
+
/* The widget area draws no panel of its own (board DcRapportages: the
|
|
120
|
+
highlighted report's card sits straight on the page ground). A theme's
|
|
121
|
+
catch-all for anything named like a widget (nldesign's element overrides:
|
|
122
|
+
`[class*="widget"]:not([class*="widget__"]):not(#header *)`, an id
|
|
123
|
+
specificity no class rule outranks) painted `__widgets` white, hence the
|
|
124
|
+
`!important`. */
|
|
125
|
+
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-reports-page__widgets {
|
|
126
|
+
background: transparent !important;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* The grid insets every card by its 12px margin (CnDashboardPage's default
|
|
130
|
+
`gridMargin`); the page padding already places the area, so the card's
|
|
131
|
+
edges line up with the report cards above it. */
|
|
132
|
+
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-reports-page__widgets .cn-dashboard-grid {
|
|
133
|
+
width: calc(100% + 24px);
|
|
134
|
+
margin: -12px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* A report card is a link, but its title and description read as text, as on
|
|
138
|
+
the board: no underline, whatever a theme does to links. */
|
|
139
|
+
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-reports-page__card,
|
|
140
|
+
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-reports-page__card * {
|
|
141
|
+
text-decoration: none !important;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* The header buttons beside the title (screens-reports-header-buttons, board
|
|
145
|
+
DcRapportages "Downloaden"): the board's 40px button with a grey border on
|
|
146
|
+
the main background, the primary one in the primary fill, as on the index
|
|
147
|
+
header. `!important` holds them under a theme that restyles every button
|
|
148
|
+
(nldesign). The buildiq square keeps its own rule. */
|
|
149
|
+
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-reports-page__header-buttons > .button-vue--secondary {
|
|
150
|
+
height: var(--cn-board-control-height) !important;
|
|
151
|
+
min-height: var(--cn-board-control-height) !important;
|
|
152
|
+
padding: 0 14px !important;
|
|
153
|
+
border: 1px solid var(--color-border-dark) !important;
|
|
154
|
+
border-radius: var(--cn-board-control-radius) !important;
|
|
155
|
+
background: var(--color-main-background) !important;
|
|
156
|
+
color: var(--color-main-text) !important;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-reports-page__header-buttons > .button-vue--secondary:hover {
|
|
160
|
+
background: var(--color-background-hover) !important;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-reports-page__header-buttons > .button-vue--primary {
|
|
164
|
+
height: var(--cn-board-control-height) !important;
|
|
165
|
+
min-height: var(--cn-board-control-height) !important;
|
|
166
|
+
padding: 0 16px !important;
|
|
167
|
+
border: 0 !important;
|
|
168
|
+
border-radius: var(--cn-board-control-radius) !important;
|
|
169
|
+
background: var(--color-primary-element) !important;
|
|
170
|
+
color: var(--color-primary-element-text) !important;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.cn-look-board .cn-reports-page:not(.cn-look-nextcloud *) .cn-reports-page__header-buttons > .button-vue--primary:hover {
|
|
174
|
+
background: var(--color-primary-element-hover) !important;
|
|
175
|
+
}
|
package/src/css/look-board.css
CHANGED
|
@@ -616,6 +616,18 @@
|
|
|
616
616
|
font-weight: 600;
|
|
617
617
|
}
|
|
618
618
|
|
|
619
|
+
/* ---- Dashboard card titles --------------------------------------------- */
|
|
620
|
+
/* A card's title on a dashboard (and on the widgets under a reports page) is
|
|
621
|
+
the board's card h2: 17px weight 700 (boards PqMijnWerk, DcRapportages and
|
|
622
|
+
the app dashboards; `--cn-board-heading-size`, as on a detail page's cards).
|
|
623
|
+
The wrapper's own 14px weight 600 stays for the Nextcloud look, and the
|
|
624
|
+
native-dashboard chrome keeps its 20px. */
|
|
625
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-widget-wrapper:not(.cn-widget-wrapper--nc-dashboard) .cn-widget-wrapper__title {
|
|
626
|
+
font-size: var(--cn-board-heading-size, 17px);
|
|
627
|
+
font-weight: 700;
|
|
628
|
+
line-height: 1.3;
|
|
629
|
+
}
|
|
630
|
+
|
|
619
631
|
/* ---- Record list on a dashboard card (PtDashboard) ---------------------- */
|
|
620
632
|
/* The dashboard's card body is flush, so the rows carry their own inset: the
|
|
621
633
|
board's card pads 24px at the sides and each row 14px, a soft hairline
|
|
@@ -702,3 +714,22 @@
|
|
|
702
714
|
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-record-list__chevron {
|
|
703
715
|
display: none;
|
|
704
716
|
}
|
|
717
|
+
|
|
718
|
+
/* ---- Week strip (DqMijnWerk "Termijnen deze week") ---------------------- */
|
|
719
|
+
/* The days wrap inside the card instead of scrolling sideways: a row holds
|
|
720
|
+
as many days of at least 120px as the card fits, and the rest go to the
|
|
721
|
+
next row (dossiq at 1100 wide held 680px of days in a 494px card and
|
|
722
|
+
scrolled; now three days and two). 120px keeps a case title's words whole;
|
|
723
|
+
five 88px days fitted one row but broke "Bezwaar" mid-word. A card wide
|
|
724
|
+
enough for the week draws it as before, one row of equal columns. The
|
|
725
|
+
day's head wraps its "today" badge under the label when the day is
|
|
726
|
+
narrow.
|
|
727
|
+
@spec openspec/changes/board-week-strip-fits/specs/week-strip-board-look/spec.md#requirement-a-board-week-strip-fits-its-card */
|
|
728
|
+
.cn-look-board .cn-week-strip:not(.cn-look-nextcloud *) .cn-week-strip__days {
|
|
729
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 120px), 1fr));
|
|
730
|
+
min-width: 0;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.cn-look-board .cn-week-strip:not(.cn-look-nextcloud *) .cn-week-strip__head {
|
|
734
|
+
flex-wrap: wrap;
|
|
735
|
+
}
|