@@ -176,6 +184,13 @@
{{ t('stackiq', 'Applications in use') }}
+
+ {{ t('stackiq', 'Recorded class differs from scores') }}
+
{{ t('stackiq', 'Application') }}
+ |
+ {{ t('stackiq', 'Suggested by scores') }}
+ |
+
+ {{ t('stackiq', 'Value / fit / risk') }}
+ |
{{ t('stackiq', 'Rationale') }}
|
@@ -220,6 +241,19 @@
:key="row.uuid"
data-testid="pr-row">
{{ row.moduleName }} |
+
+
+ {{
+ quadrantLabel(
+ row.suggestedTimeClassification,
+ )
+ }}
+
+ —
+ |
+ {{ scoresLabel(row) }} |
{{ row.timeRationale || '—' }} |
{{ row.timeReviewDate || '—' }} |
{{ row.lifecyclePhase }} |
@@ -262,6 +296,7 @@ import { translate as t } from '@nextcloud/l10n'
import { generateUrl } from '@nextcloud/router'
import {
NcButton,
+ NcCheckboxRadioSwitch,
NcEmptyContent,
NcLoadingIcon,
NcNoteCard,
@@ -270,6 +305,7 @@ import {
import ChartBoxOutline from 'vue-material-design-icons/ChartBoxOutline.vue'
import Download from 'vue-material-design-icons/Download.vue'
import Refresh from 'vue-material-design-icons/Refresh.vue'
+import ValueFitPlot from '../../components/portfolio/ValueFitPlot.vue'
import { useLiveCollections } from '../../composables/useLiveCollections.js'
import { objectStore } from '../../store/store.js'
import { resolveUuid } from '../../utils/lifecyclePhase.js'
@@ -281,6 +317,7 @@ import {
QUADRANT_ORDER,
quadrantColor,
} from '../../utils/portfolioReport.js'
+import { mismatchRows } from '../../utils/valueAssessment.js'
/**
* @class PortfolioReport
@@ -300,11 +337,13 @@ export default {
name: 'PortfolioReport',
components: {
NcButton,
+ NcCheckboxRadioSwitch,
NcLoadingIcon,
NcSelect,
NcEmptyContent,
NcNoteCard,
CnChartWidget,
+ ValueFitPlot,
Refresh,
Download,
ChartBoxOutline,
@@ -330,6 +369,7 @@ export default {
error: null,
selectedOrg: null,
report: null,
+ onlyMismatch: false,
}
},
@@ -434,12 +474,14 @@ export default {
*
* @return {Array<{key: string, rows: Array}>} Grouped rows.
* @spec openspec/changes/portfolio-rationalization-time/specs/portfolio-rationalization-time/spec.md#requirement-portfolio-rationalization-report-aggregates-per-organisation
+ * @spec openspec/specs/application-value-assessment/spec.md#requirement-req-ava-003-the-portfolio-report-plots-value-against-fit-and-flags-classes-the-scores-contradict
*/
groupedRows() {
if (!this.report) {
return []
}
- return groupRowsByQuadrant(this.report.rows || [])
+ const rows = this.report.rows || []
+ return groupRowsByQuadrant(this.onlyMismatch ? mismatchRows(rows) : rows)
},
},
@@ -595,6 +637,25 @@ export default {
return map[key] || key
},
+ /**
+ * The three scores of a row as "value / fit / risk", a dash for a missing one.
+ *
+ * @param {object} row A report row.
+ * @return {string} The label.
+ * @spec openspec/specs/application-value-assessment/spec.md#requirement-req-ava-003-the-portfolio-report-plots-value-against-fit-and-flags-classes-the-scores-contradict
+ */
+ scoresLabel(row) {
+ const scores = [row.businessValue, row.technicalFit, row.riskScore]
+ if (scores.every((score) => score === null || score === undefined)) {
+ return '—'
+ }
+ return scores
+ .map((score) =>
+ score === null || score === undefined ? '-' : score,
+ )
+ .join(' / ')
+ },
+
// Exposed to the template as methods — thin re-exports of the pure
// `portfolioReport.js` utils (vitest-covered), since Vue 2 Options
// API templates cannot call a bare imported function directly.
@@ -712,6 +773,15 @@ export default {
color: var(--color-text-maxcontrast);
}
+.pr-mismatchFilter {
+ margin-bottom: 12px;
+}
+
+.pr-mismatch {
+ font-weight: bold;
+ color: var(--color-warning-text);
+}
+
.pr-loading {
margin: 40px auto;
display: block;
diff --git a/src/views/settings/sections/ArchiMateImportExport.vue b/src/views/settings/sections/ArchiMateImportExport.vue
index 6ab9d584f..aba236d94 100644
--- a/src/views/settings/sections/ArchiMateImportExport.vue
+++ b/src/views/settings/sections/ArchiMateImportExport.vue
@@ -527,6 +527,30 @@
+
+
+
{{ importProgressView.label }}
+
+
+ {{ importProgressView.detail }}
+
+
+
+
+ {{
+ t(
+ 'stackiq',
+ 'Import cancelled. {count} objects were saved before it stopped.',
+ { count: importCancelled.objects_saved || 0 },
+ )
+ }}
+
+