@conduction/nextcloud-vue 2.120.0 → 2.121.0
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/CnIndexPage/CnIndexPage.vue2.js +53 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +546 -523
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js +9 -0
- package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js +9 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js.map +1 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js +214 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js.map +1 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js +125 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js +9 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js +163 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js +131 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js +127 -0
- package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js +9 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js +82 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js +61 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js +60 -0
- package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js +10 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js +226 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js +126 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +42 -32
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/composables/indexSources.js +116 -16
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +42 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +42 -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 +147 -0
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +14135 -12578
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +147 -0
- package/l10n/en.json +42 -1
- package/l10n/nl.json +42 -1
- package/package.json +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +74 -3
- package/src/components/CnIndexPage/useNamedSource.js +9 -0
- package/src/components/CnReadinessChecklist/CnReadinessChecklist.vue +335 -0
- package/src/components/CnReadinessChecklist/index.js +9 -0
- package/src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue +300 -0
- package/src/components/CnSchemaRulesWidget/index.js +10 -0
- package/src/components/CnSchemaRulesWidget/schemaRules.js +123 -0
- package/src/components/CnSearchIndexStatus/CnSearchIndexStatus.vue +160 -0
- package/src/components/CnSearchIndexStatus/index.js +10 -0
- package/src/components/CnSearchIndexStatus/searchIndexStatus.js +56 -0
- package/src/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue +328 -0
- package/src/components/CnShippedConfigurationSection/index.js +9 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +10 -0
- package/src/components/index.js +4 -0
- package/src/composables/indexSources.js +116 -16
- package/src/css/index-page.css +7 -0
- package/src/index.js +4 -0
- package/src/utils/libraryWidgetKeys.js +1 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, createBlock, withCtx, createTextVNode, toDisplayString, Fragment, createElementVNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = {
|
|
4
|
+
class: "cn-search-index-status",
|
|
5
|
+
"data-testid": "cn-search-index-status"
|
|
6
|
+
};
|
|
7
|
+
const _hoisted_2 = { class: "cn-search-index-status__figures" };
|
|
8
|
+
const _hoisted_3 = { "data-testid": "cn-search-index-tables" };
|
|
9
|
+
const _hoisted_4 = { "data-testid": "cn-search-index-indexes" };
|
|
10
|
+
const _hoisted_5 = { "data-testid": "cn-search-index-last-run" };
|
|
11
|
+
const _hoisted_6 = { "data-testid": "cn-search-index-concurrent" };
|
|
12
|
+
const _hoisted_7 = { "data-testid": "cn-search-index-commands" };
|
|
13
|
+
|
|
14
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
15
|
+
const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
|
|
16
|
+
const _component_NcNoteCard = resolveComponent("NcNoteCard");
|
|
17
|
+
|
|
18
|
+
return (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
19
|
+
($data.loading)
|
|
20
|
+
? (openBlock(), createBlock(_component_NcLoadingIcon, {
|
|
21
|
+
key: 0,
|
|
22
|
+
size: 24,
|
|
23
|
+
name: $options.t('nextcloud-vue', 'Loading')
|
|
24
|
+
}, null, 8 /* PROPS */, ["name"]))
|
|
25
|
+
: ($data.error)
|
|
26
|
+
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
27
|
+
key: 1,
|
|
28
|
+
type: "error",
|
|
29
|
+
"data-testid": "cn-search-index-error"
|
|
30
|
+
}, {
|
|
31
|
+
default: withCtx(() => [
|
|
32
|
+
createTextVNode(toDisplayString($data.error), 1 /* TEXT */)
|
|
33
|
+
]),
|
|
34
|
+
_: 1 /* STABLE */
|
|
35
|
+
}))
|
|
36
|
+
: (openBlock(), createElementBlock(Fragment, { key: 2 }, [
|
|
37
|
+
createElementVNode("dl", _hoisted_2, [
|
|
38
|
+
createElementVNode("div", null, [
|
|
39
|
+
createElementVNode("dt", null, toDisplayString($options.t('nextcloud-vue', 'Tables in scope')), 1 /* TEXT */),
|
|
40
|
+
createElementVNode("dd", _hoisted_3, toDisplayString($data.status.tableCount), 1 /* TEXT */)
|
|
41
|
+
]),
|
|
42
|
+
createElementVNode("div", null, [
|
|
43
|
+
createElementVNode("dt", null, toDisplayString($options.t('nextcloud-vue', 'Indexes on them')), 1 /* TEXT */),
|
|
44
|
+
createElementVNode("dd", _hoisted_4, toDisplayString($data.status.indexCount), 1 /* TEXT */)
|
|
45
|
+
]),
|
|
46
|
+
createElementVNode("div", null, [
|
|
47
|
+
createElementVNode("dt", null, toDisplayString($options.t('nextcloud-vue', 'Last run')), 1 /* TEXT */),
|
|
48
|
+
createElementVNode("dd", _hoisted_5, toDisplayString($options.lastRun), 1 /* TEXT */)
|
|
49
|
+
]),
|
|
50
|
+
createElementVNode("div", null, [
|
|
51
|
+
createElementVNode("dt", null, toDisplayString($options.t('nextcloud-vue', 'Rebuild without locking')), 1 /* TEXT */),
|
|
52
|
+
createElementVNode("dd", _hoisted_6, toDisplayString($options.concurrent), 1 /* TEXT */)
|
|
53
|
+
])
|
|
54
|
+
]),
|
|
55
|
+
createElementVNode("p", _hoisted_7, toDisplayString($options.t('nextcloud-vue', 'Run {command} status on the server for the same figures, and {command} rebuild --apply after a schema change.', { command: $props.command })), 1 /* TEXT */)
|
|
56
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
57
|
+
]))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { render };
|
|
61
|
+
//# sourceMappingURL=CnSearchIndexStatus.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnSearchIndexStatus.vue3.js","sources":["../../../../src/components/CnSearchIndexStatus/CnSearchIndexStatus.vue?vue&type=template&id=52b442a4&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-search-index-status\" data-testid=\"cn-search-index-status\">\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\t\t<NcNoteCard v-else-if=\"error\" type=\"error\" data-testid=\"cn-search-index-error\">\n\t\t\t{{ error }}\n\t\t</NcNoteCard>\n\t\t<template v-else>\n\t\t\t<dl class=\"cn-search-index-status__figures\">\n\t\t\t\t<div>\n\t\t\t\t\t<dt>{{ t('nextcloud-vue', 'Tables in scope') }}</dt>\n\t\t\t\t\t<dd data-testid=\"cn-search-index-tables\">\n\t\t\t\t\t\t{{ status.tableCount }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\t<dt>{{ t('nextcloud-vue', 'Indexes on them') }}</dt>\n\t\t\t\t\t<dd data-testid=\"cn-search-index-indexes\">\n\t\t\t\t\t\t{{ status.indexCount }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\t<dt>{{ t('nextcloud-vue', 'Last run') }}</dt>\n\t\t\t\t\t<dd data-testid=\"cn-search-index-last-run\">\n\t\t\t\t\t\t{{ lastRun }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\t<dt>{{ t('nextcloud-vue', 'Rebuild without locking') }}</dt>\n\t\t\t\t\t<dd data-testid=\"cn-search-index-concurrent\">\n\t\t\t\t\t\t{{ concurrent }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t</dl>\n\t\t\t<p data-testid=\"cn-search-index-commands\">\n\t\t\t\t{{ t('nextcloud-vue', 'Run {command} status on the server for the same figures, and {command} rebuild --apply after a schema change.', { command }) }}\n\t\t\t</p>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { getCanonicalLocale, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { fetchSearchIndexStatus, SEARCH_INDEX_COMMAND, SEARCH_INDEX_STATUS_URL } from './searchIndexStatus.js'\n\n/**\n * CnSearchIndexStatus: the state of OpenRegister's search indexes (tables in\n * scope, indexes on them, the last maintenance run, whether a rebuild locks a\n * table), read live and never copied. The acts are named, not offered: a\n * rebuild walks every table in scope and nothing stops one from a settings\n * page, so the occ verbs are printed for somebody with a shell.\n *\n * @spec openspec/changes/search-index-status/specs/search-index-status/spec.md\n */\nexport default {\n\tname: 'CnSearchIndexStatus',\n\n\tcomponents: { NcLoadingIcon, NcNoteCard },\n\n\tprops: {\n\t\t/** The app path OpenRegister reports its indexes at. */\n\t\tsource: { type: String, default: SEARCH_INDEX_STATUS_URL },\n\t\t/** The occ command that acts on the indexes. */\n\t\tcommand: { type: String, default: SEARCH_INDEX_COMMAND },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\terror: '',\n\t\t\tstatus: { concurrentRebuildSupported: false, tables: {}, tableCount: 0, indexCount: 0, lastRun: null },\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} When maintenance last ran, or that it never has. */\n\t\tlastRun() {\n\t\t\tif (!this.status.lastRun) {\n\t\t\t\treturn t('nextcloud-vue', 'Never')\n\t\t\t}\n\t\t\tconst when = new Date(this.status.lastRun)\n\t\t\tif (Number.isNaN(when.getTime())) {\n\t\t\t\treturn String(this.status.lastRun)\n\t\t\t}\n\t\t\treturn when.toLocaleString(getCanonicalLocale(), { dateStyle: 'medium', timeStyle: 'short' })\n\t\t},\n\n\t\t/** @return {string} Whether a rebuild locks the table, in words. */\n\t\tconcurrent() {\n\t\t\treturn this.status.concurrentRebuildSupported === true\n\t\t\t\t? t('nextcloud-vue', 'Yes')\n\t\t\t\t: t('nextcloud-vue', 'No, a rebuild locks the table it is on')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Read what OpenRegister says about its indexes.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tthis.status = await fetchSearchIndexStatus(this.source)\n\t\t\t} catch (e) {\n\t\t\t\tthis.error = String((e && e.message) || e)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Label above value, and the label wraps: Nextcloud's settings stylesheet gives a `dt` a fixed width. */\n.cn-search-index-status__figures {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n\tgap: 16px 24px;\n\tmargin-block: 0 12px;\n}\n\n.cn-search-index-status__figures > div {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-search-index-status__figures dt {\n\tdisplay: block;\n\tfloat: none;\n\twidth: auto;\n\tmargin: 0;\n\tpadding: 0;\n\ttext-align: start;\n\twhite-space: normal;\n\toverflow-wrap: anywhere;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-search-index-status__figures dd {\n\tfont-weight: bold;\n\tmargin: 0;\n\toverflow-wrap: anywhere;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_createElementVNode","_toDisplayString"],"mappings":";;;AASM,EAAA,KAAK,EAAC,wBAAE;AAAA,EAAA,aAAA,EAAA,wBAAA;;AAMP,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,iCAAe,GAAA;AAGpB,MAAA,UAAA,GAAA,EAAA,aAAA,EAAA,wBAAA,GAAA;AAMA,MAAA,UAAA,GAAA,EAAA,aAAA,EAAA,yBAAA,GAAA;AAMA,MAAA,UAAA,GAAA,EAAA,aAAA,EAAA,0BAAA,GAAA;AAMD,MAAA,UAAA,GAAA,EAAA,aAAA,EAAA,4BAAA,GAAA;AAKD,MAAA,UAAA,GAAA,EAAA,aAAA,EAAA,0BAAA,GAAA;;;;;;;KA/BY,KAAA,CAAA,OAAA;sBAAhBA,WAAgB,CAAA,wBAAA,EAAA;;AAAA,UAAA,IAAA,EAAA,EAAA;AAAA,UAAA,IAAA,EAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,SAAA,CAAA;;SACO,KAAK,CAAA,KAAA;wBAA3BA,WAEY,CAAA,qBAAA,EAAA;;AAFiB,YAAA,IAAI,EAAC,OAAO;AAAC,YAAA,aAAC,EAAA,uBAAA;;mCACtC;8CAAF,KAAE,CAAA,KAAA,CAAA,EAAA,CAAA,YAAA;;;;wBAEHC;AACC,YAAAC,kBAAA,CAyBE,MAzBF,UAyBE,EAAA;gCAnBE,CAAA,KAAA,EAAA,IAAA,EAAA;AAJL,gBAAAA,kBAAA,CAAI,4BAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,iBAAA,CAAA,CAAA,EAAA,CAAA,YAAA;AACJ,gBAAAA,kBAAA,CAEK,IAFL,EAAA,UAEK,EADDC,eAAA,CAAA,KAAA,CAAA,MAAM,CAAC,UAAU,CAAA,EAAA,CAAA,YAAA;;gCAQhB,CAAA,KAAA,EAAA,IAAA,EAAA;AAJL,gBAAAD,kBAAA,CAAI,4BAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,iBAAA,CAAA,CAAA,EAAA,CAAA,YAAA;AACJ,gBAAAA,kBAAA,CAEK,IAFL,EAAA,UAEK,EADDC,eAAA,CAAA,KAAA,CAAA,MAAM,CAAC,UAAU,CAAA,EAAA,CAAA,YAAA;;gCAQhB,CAAA,KAAA,EAAA,IAAA,EAAA;AAJL,gBAAAD,kBAAA,CAAI,4BAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,UAAA,CAAA,CAAA,EAAA,CAAA,YAAA;gBACJA,kBAEK,CAAA,IAAA,EAFL,UAEK,EAAAC,eAAA,CADD,QAAO,CAAA,OAAA,CAAA,EAAA,CAAA,YAAA;;gCAQV,CAAA,KAAA,EAAA,IAAA,EAAA;AAJD,gBAAAD,kBAAA,CAAI,4BAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,yBAAA,CAAA,CAAA,EAAA,CAAA,YAAA;gBACJA,kBAEK,CAAA,IAAA,EAFL,UAEK,EAAAC,eAAA,CADD,QAAU,CAAA,UAAA,CAAA,EAAA,CAAA,YAAA;;;AAIhB,YAAAD,kBAAA,IAAA,EAAA;;;;;;;"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import axios__default from '@nextcloud/axios';
|
|
2
|
+
import { generateUrl } from '@nextcloud/router';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
6
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/** The occ command that rebuilds, snapshots, restores and reports the indexes. */
|
|
11
|
+
const SEARCH_INDEX_COMMAND = 'occ openregister:tables:search-index';
|
|
12
|
+
|
|
13
|
+
/** Where OpenRegister reports its search indexes. */
|
|
14
|
+
const SEARCH_INDEX_STATUS_URL = '/apps/openregister/api/settings/search-index';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* When the last stored run finished, or started, or null when none ran.
|
|
18
|
+
*
|
|
19
|
+
* OpenRegister answers `lastRun` as the report of that run (`startedAt`,
|
|
20
|
+
* `finishedAt`) and as `[]` when nothing ran.
|
|
21
|
+
*
|
|
22
|
+
* @param {object|Array|string|null|undefined} report What OpenRegister answered as `lastRun`.
|
|
23
|
+
* @return {string|null} An ISO 8601 time, or null when no run is on record.
|
|
24
|
+
*/
|
|
25
|
+
function lastRunTime(report) {
|
|
26
|
+
if (typeof report === 'string') {
|
|
27
|
+
return report === '' ? null : report
|
|
28
|
+
}
|
|
29
|
+
if (report === null || typeof report !== 'object' || Array.isArray(report)) {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
const time = report.finishedAt || report.startedAt || '';
|
|
33
|
+
return typeof time === 'string' && time !== '' ? time : null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* What OpenRegister says about the indexes behind object search. A failed
|
|
38
|
+
* read throws: an outage and an instance with no indexes look the same from
|
|
39
|
+
* the browser, and only one of them is somebody's problem.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} [url] The app path to read.
|
|
42
|
+
* @return {Promise<{concurrentRebuildSupported: boolean, tables: object, tableCount: number, indexCount: number, lastRun: (string|null)}>}
|
|
43
|
+
* @throws {Error} When it cannot be read.
|
|
44
|
+
*/
|
|
45
|
+
async function fetchSearchIndexStatus(url = SEARCH_INDEX_STATUS_URL) {
|
|
46
|
+
const { data } = await axios__default.get(generateUrl(url));
|
|
47
|
+
if (data === null || typeof data !== 'object' || data.error !== undefined) {
|
|
48
|
+
throw new Error(String((data && data.error) ?? 'The search index status could not be read.'))
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
concurrentRebuildSupported: data.concurrentRebuildSupported === true,
|
|
52
|
+
tables: data.tables && typeof data.tables === 'object' ? data.tables : {},
|
|
53
|
+
tableCount: Number(data.tableCount ?? 0),
|
|
54
|
+
indexCount: Number(data.indexCount ?? 0),
|
|
55
|
+
lastRun: lastRunTime(data.lastRun),
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { SEARCH_INDEX_COMMAND, SEARCH_INDEX_STATUS_URL, fetchSearchIndexStatus, lastRunTime };
|
|
60
|
+
//# sourceMappingURL=searchIndexStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchIndexStatus.js","sources":["../../../../src/components/CnSearchIndexStatus/searchIndexStatus.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\nimport axios from '@nextcloud/axios'\nimport { generateUrl } from '@nextcloud/router'\n\n/** The occ command that rebuilds, snapshots, restores and reports the indexes. */\nexport const SEARCH_INDEX_COMMAND = 'occ openregister:tables:search-index'\n\n/** Where OpenRegister reports its search indexes. */\nexport const SEARCH_INDEX_STATUS_URL = '/apps/openregister/api/settings/search-index'\n\n/**\n * When the last stored run finished, or started, or null when none ran.\n *\n * OpenRegister answers `lastRun` as the report of that run (`startedAt`,\n * `finishedAt`) and as `[]` when nothing ran.\n *\n * @param {object|Array|string|null|undefined} report What OpenRegister answered as `lastRun`.\n * @return {string|null} An ISO 8601 time, or null when no run is on record.\n */\nexport function lastRunTime(report) {\n\tif (typeof report === 'string') {\n\t\treturn report === '' ? null : report\n\t}\n\tif (report === null || typeof report !== 'object' || Array.isArray(report)) {\n\t\treturn null\n\t}\n\tconst time = report.finishedAt || report.startedAt || ''\n\treturn typeof time === 'string' && time !== '' ? time : null\n}\n\n/**\n * What OpenRegister says about the indexes behind object search. A failed\n * read throws: an outage and an instance with no indexes look the same from\n * the browser, and only one of them is somebody's problem.\n *\n * @param {string} [url] The app path to read.\n * @return {Promise<{concurrentRebuildSupported: boolean, tables: object, tableCount: number, indexCount: number, lastRun: (string|null)}>}\n * @throws {Error} When it cannot be read.\n */\nexport async function fetchSearchIndexStatus(url = SEARCH_INDEX_STATUS_URL) {\n\tconst { data } = await axios.get(generateUrl(url))\n\tif (data === null || typeof data !== 'object' || data.error !== undefined) {\n\t\tthrow new Error(String((data && data.error) ?? 'The search index status could not be read.'))\n\t}\n\treturn {\n\t\tconcurrentRebuildSupported: data.concurrentRebuildSupported === true,\n\t\ttables: data.tables && typeof data.tables === 'object' ? data.tables : {},\n\t\ttableCount: Number(data.tableCount ?? 0),\n\t\tindexCount: Number(data.indexCount ?? 0),\n\t\tlastRun: lastRunTime(data.lastRun),\n\t}\n}\n"],"names":["axios"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AAGA;AACA;AACY,MAAC,oBAAoB,GAAG,uCAAsC;AAC1E;AACA;AACY,MAAC,uBAAuB,GAAG,+CAA8C;AACrF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,MAAM,EAAE;AACpC,CAAC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;AACjC,EAAE,OAAO,MAAM,KAAK,EAAE,GAAG,IAAI,GAAG,MAAM;AACtC,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AAC7E,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,SAAS,IAAI,GAAE;AACzD,CAAC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE,GAAG,IAAI,GAAG,IAAI;AAC7D,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,sBAAsB,CAAC,GAAG,GAAG,uBAAuB,EAAE;AAC5E,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAMA,cAAK,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,EAAC;AACnD,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE;AAC5E,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,4CAA4C,CAAC,CAAC;AAC/F,EAAE;AACF,CAAC,OAAO;AACR,EAAE,0BAA0B,EAAE,IAAI,CAAC,0BAA0B,KAAK,IAAI;AACtE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE;AAC3E,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;AAC1C,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;AAC1C,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC;AACpC,EAAE;AACF;;;;"}
|
package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import script from './CnShippedConfigurationSection.vue2.js';
|
|
2
|
+
export { SHIPPED_STATES } from './CnShippedConfigurationSection.vue2.js';
|
|
3
|
+
import { render } from './CnShippedConfigurationSection.vue3.js';
|
|
4
|
+
|
|
5
|
+
script.render = render;
|
|
6
|
+
script.__scopeId = "data-v-fd515054";
|
|
7
|
+
const __sfc_main = script;
|
|
8
|
+
|
|
9
|
+
export { __sfc_main as default };
|
|
10
|
+
//# sourceMappingURL=CnShippedConfigurationSection.vue.js.map
|
package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnShippedConfigurationSection.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import axios__default from '@nextcloud/axios';
|
|
2
|
+
import { x as showSuccess } from '../../node_modules/@nextcloud/dialogs/dist/chunks/index.js';
|
|
3
|
+
import { translate } from '@nextcloud/l10n';
|
|
4
|
+
import { generateUrl } from '@nextcloud/router';
|
|
5
|
+
import { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The states a seeded object can be in.
|
|
9
|
+
*
|
|
10
|
+
* @type {Readonly<Record<string, string>>}
|
|
11
|
+
*/
|
|
12
|
+
const SHIPPED_STATES = Object.freeze({
|
|
13
|
+
shipped: 'shipped',
|
|
14
|
+
changed: 'changed',
|
|
15
|
+
local: 'local',
|
|
16
|
+
removed: 'removed',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* CnShippedConfigurationSection: what an app shipped, what this instance
|
|
21
|
+
* changed, and what it authored. One row per seeded object with the set and
|
|
22
|
+
* version it came from, its state, and the newer version offered per object.
|
|
23
|
+
* Taking a newer version over a local change says so on the button, because
|
|
24
|
+
* the change goes.
|
|
25
|
+
*
|
|
26
|
+
* The app's endpoint answers `GET {endpoint}/{schema}` with `{ items: [{
|
|
27
|
+
* targetObject, title, state, set, setVersion, latestVersion, updateAvailable
|
|
28
|
+
* }] }` and takes `POST {endpoint}/{schema}/{targetObject}/adopt` with `{ set,
|
|
29
|
+
* acceptLocalChangeLoss }`, refusing with `{ reason }`. An empty ledger beside
|
|
30
|
+
* a register that holds objects is said as such (`countUrl`), not as "nothing
|
|
31
|
+
* seeded".
|
|
32
|
+
*
|
|
33
|
+
* @spec openspec/changes/shipped-configuration-section/specs/shipped-configuration-section/spec.md
|
|
34
|
+
*/
|
|
35
|
+
var script = {
|
|
36
|
+
name: 'CnShippedConfigurationSection',
|
|
37
|
+
|
|
38
|
+
components: { NcButton, NcNoteCard, NcSelect },
|
|
39
|
+
|
|
40
|
+
inject: {
|
|
41
|
+
/** The host app's translate, for the texts the page declares. */
|
|
42
|
+
cnTranslate: { default: () => (key) => key },
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
props: {
|
|
46
|
+
/** The app path of the shipped ledger (`/apps/<app>/api/starter/shipped`). */
|
|
47
|
+
endpoint: { type: String, required: true },
|
|
48
|
+
/** The schemas to show, `[{ slug, label }]`; the first is shown first. */
|
|
49
|
+
schemas: { type: Array, default: () => [] },
|
|
50
|
+
/**
|
|
51
|
+
* An app path that counts a schema's objects, `{schema}` filled in
|
|
52
|
+
* (`/apps/openregister/api/objects/<register>/{schema}`), read with
|
|
53
|
+
* `_limit=1` for its `total`.
|
|
54
|
+
*/
|
|
55
|
+
countUrl: { type: String, default: '' },
|
|
56
|
+
/** A line above the table. */
|
|
57
|
+
description: { type: String, default: '' },
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
data() {
|
|
61
|
+
return {
|
|
62
|
+
schema: this.schemas.length > 0 ? String(this.schemas[0].slug) : '',
|
|
63
|
+
rows: [],
|
|
64
|
+
present: null,
|
|
65
|
+
loadError: '',
|
|
66
|
+
adoptError: '',
|
|
67
|
+
adopting: '',
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
computed: {
|
|
72
|
+
/** @return {Array<{id: string, label: string}>} The picker's options. */
|
|
73
|
+
schemaOptions() {
|
|
74
|
+
return this.schemas.map((option) => ({ id: String(option.slug), label: this.text(option.label || option.slug) }))
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
schemaOption: {
|
|
78
|
+
/** @return {object|null} The chosen schema. */
|
|
79
|
+
get() {
|
|
80
|
+
return this.schemaOptions.find((option) => option.id === this.schema) || null
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/** @param {object|null} option The chosen schema. */
|
|
84
|
+
set(option) {
|
|
85
|
+
if (option) {
|
|
86
|
+
this.schema = option.id;
|
|
87
|
+
this.reload();
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/** @return {string} What the empty table says, which depends on what the register holds. */
|
|
93
|
+
emptyLine() {
|
|
94
|
+
if (typeof this.present === 'number' && this.present > 0) {
|
|
95
|
+
return translate('nextcloud-vue', 'The register holds {count} of these. None came from a shipped set, so there is no shipped version to compare with.', { count: this.present })
|
|
96
|
+
}
|
|
97
|
+
return translate('nextcloud-vue', 'Nothing has been seeded yet.')
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
mounted() {
|
|
102
|
+
this.reload();
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
methods: {
|
|
106
|
+
t: translate,
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {string} value A declared text.
|
|
110
|
+
* @return {string} It, translated by the host.
|
|
111
|
+
*/
|
|
112
|
+
text(value) {
|
|
113
|
+
return value ? String(this.cnTranslate(String(value))) : ''
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Read what shipped for the shown schema. A failed read empties the
|
|
118
|
+
* table: rows left beside an error would read as the current state.
|
|
119
|
+
*
|
|
120
|
+
* @return {Promise<void>}
|
|
121
|
+
*/
|
|
122
|
+
async reload() {
|
|
123
|
+
if (!this.schema) {
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const { data } = await axios__default.get(generateUrl(`${this.endpoint}/${encodeURIComponent(this.schema)}`));
|
|
128
|
+
this.rows = Array.isArray(data && data.items) ? data.items : [];
|
|
129
|
+
this.loadError = '';
|
|
130
|
+
this.present = null;
|
|
131
|
+
if (this.rows.length === 0) {
|
|
132
|
+
this.present = await this.count();
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
this.rows = [];
|
|
136
|
+
this.present = null;
|
|
137
|
+
this.loadError = translate('nextcloud-vue', 'Could not read the shipped configuration.');
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* How many objects of the shown schema the register holds, whatever
|
|
143
|
+
* brought them.
|
|
144
|
+
*
|
|
145
|
+
* @return {Promise<number|null>} The count, or null when it cannot be read.
|
|
146
|
+
*/
|
|
147
|
+
async count() {
|
|
148
|
+
if (!this.countUrl) {
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const { data } = await axios__default.get(generateUrl(this.countUrl.replace('{schema}', encodeURIComponent(this.schema))), { params: { _limit: 1 } });
|
|
153
|
+
const total = Number(data && data.total);
|
|
154
|
+
return Number.isFinite(total) ? total : null
|
|
155
|
+
} catch {
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @param {object} row A ledger row.
|
|
162
|
+
* @return {string} What an administrator reads beside the object.
|
|
163
|
+
*/
|
|
164
|
+
stateLabel(row) {
|
|
165
|
+
const state = (row && row.state) || SHIPPED_STATES.local;
|
|
166
|
+
return {
|
|
167
|
+
[SHIPPED_STATES.shipped]: translate('nextcloud-vue', 'Shipped, unchanged'),
|
|
168
|
+
[SHIPPED_STATES.changed]: translate('nextcloud-vue', 'Shipped, changed here'),
|
|
169
|
+
[SHIPPED_STATES.removed]: translate('nextcloud-vue', 'Shipped, removed here'),
|
|
170
|
+
}[state] || translate('nextcloud-vue', 'Yours')
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {object} row A ledger row.
|
|
175
|
+
* @return {boolean} Whether a newer shipped version can be taken.
|
|
176
|
+
*/
|
|
177
|
+
updatable(row) {
|
|
178
|
+
return Boolean(row && row.updateAvailable && row.state !== SHIPPED_STATES.removed)
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {object} row A ledger row.
|
|
183
|
+
* @return {boolean} Whether taking the newer version overwrites a local change.
|
|
184
|
+
*/
|
|
185
|
+
losesLocalChange(row) {
|
|
186
|
+
return Boolean(row && row.state === SHIPPED_STATES.changed)
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @param {object} row A ledger row.
|
|
191
|
+
* @return {string} The button's words, which say when a local change goes.
|
|
192
|
+
*/
|
|
193
|
+
adoptLabel(row) {
|
|
194
|
+
return this.losesLocalChange(row)
|
|
195
|
+
? translate('nextcloud-vue', 'Take {version} and lose your change', { version: row.latestVersion })
|
|
196
|
+
: translate('nextcloud-vue', 'Take {version}', { version: row.latestVersion })
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Take the newer shipped version of one object, then read the ledger again.
|
|
201
|
+
*
|
|
202
|
+
* @param {object} row A ledger row.
|
|
203
|
+
* @return {Promise<void>}
|
|
204
|
+
*/
|
|
205
|
+
async adopt(row) {
|
|
206
|
+
this.adopting = row.targetObject;
|
|
207
|
+
this.adoptError = '';
|
|
208
|
+
try {
|
|
209
|
+
await axios__default.post(
|
|
210
|
+
generateUrl(`${this.endpoint}/${encodeURIComponent(this.schema)}/${encodeURIComponent(row.targetObject)}/adopt`),
|
|
211
|
+
{ set: row.set, acceptLocalChangeLoss: this.losesLocalChange(row) },
|
|
212
|
+
);
|
|
213
|
+
showSuccess(translate('nextcloud-vue', 'The newer version was taken.'));
|
|
214
|
+
await this.reload();
|
|
215
|
+
} catch (error) {
|
|
216
|
+
const data = error && error.response && error.response.data;
|
|
217
|
+
this.adoptError = String((data && (data.reason || data.message)) || translate('nextcloud-vue', 'The newer version was not taken.'));
|
|
218
|
+
} finally {
|
|
219
|
+
this.adopting = '';
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export { SHIPPED_STATES, script as default };
|
|
226
|
+
//# sourceMappingURL=CnShippedConfigurationSection.vue2.js.map
|
package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnShippedConfigurationSection.vue2.js","sources":["../../../../src/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-shipped-configuration\" data-testid=\"cn-shipped-configuration\">\n\t\t<p v-if=\"description\" class=\"cn-shipped-configuration__intro\">\n\t\t\t{{ text(description) }}\n\t\t</p>\n\n\t\t<NcSelect v-if=\"schemas.length > 1\"\n\t\t\tv-model=\"schemaOption\"\n\t\t\tclass=\"cn-shipped-configuration__picker\"\n\t\t\t:options=\"schemaOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Show')\"\n\t\t\t:clearable=\"false\"\n\t\t\tdata-testid=\"cn-shipped-schema\" />\n\n\t\t<table class=\"cn-shipped-configuration__table\" data-testid=\"cn-shipped-configuration-table\">\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Name') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'State') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Shipped set') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Version') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Newer version') }}\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t<tr v-for=\"row in rows\" :key=\"row.targetObject\">\n\t\t\t\t\t<td>{{ row.title || row.targetObject }}</td>\n\t\t\t\t\t<td :data-testid=\"'cn-shipped-state-' + row.targetObject\">\n\t\t\t\t\t\t{{ stateLabel(row) }}\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>{{ row.set }}</td>\n\t\t\t\t\t<td>{{ row.setVersion }}</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<NcButton v-if=\"updatable(row)\"\n\t\t\t\t\t\t\t:data-testid=\"'cn-shipped-adopt-' + row.targetObject\"\n\t\t\t\t\t\t\t:disabled=\"adopting === row.targetObject\"\n\t\t\t\t\t\t\t@click=\"adopt(row)\">\n\t\t\t\t\t\t\t{{ adoptLabel(row) }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t<span v-else class=\"cn-shipped-configuration__muted\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Up to date') }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t\t<tr v-if=\"!rows.length\">\n\t\t\t\t\t<td colspan=\"5\"\n\t\t\t\t\t\tclass=\"cn-shipped-configuration__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-shipped-configuration-empty\">\n\t\t\t\t\t\t{{ emptyLine }}\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<NcNoteCard v-if=\"loadError\" type=\"error\" data-testid=\"cn-shipped-configuration-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\t\t<NcNoteCard v-if=\"adoptError\" type=\"error\" data-testid=\"cn-shipped-configuration-adopt-error\">\n\t\t\t{{ adoptError }}\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { showSuccess } from '@nextcloud/dialogs'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\n\n/**\n * The states a seeded object can be in.\n *\n * @type {Readonly<Record<string, string>>}\n */\nexport const SHIPPED_STATES = Object.freeze({\n\tshipped: 'shipped',\n\tchanged: 'changed',\n\tlocal: 'local',\n\tremoved: 'removed',\n})\n\n/**\n * CnShippedConfigurationSection: what an app shipped, what this instance\n * changed, and what it authored. One row per seeded object with the set and\n * version it came from, its state, and the newer version offered per object.\n * Taking a newer version over a local change says so on the button, because\n * the change goes.\n *\n * The app's endpoint answers `GET {endpoint}/{schema}` with `{ items: [{\n * targetObject, title, state, set, setVersion, latestVersion, updateAvailable\n * }] }` and takes `POST {endpoint}/{schema}/{targetObject}/adopt` with `{ set,\n * acceptLocalChangeLoss }`, refusing with `{ reason }`. An empty ledger beside\n * a register that holds objects is said as such (`countUrl`), not as \"nothing\n * seeded\".\n *\n * @spec openspec/changes/shipped-configuration-section/specs/shipped-configuration-section/spec.md\n */\nexport default {\n\tname: 'CnShippedConfigurationSection',\n\n\tcomponents: { NcButton, NcNoteCard, NcSelect },\n\n\tinject: {\n\t\t/** The host app's translate, for the texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The app path of the shipped ledger (`/apps/<app>/api/starter/shipped`). */\n\t\tendpoint: { type: String, required: true },\n\t\t/** The schemas to show, `[{ slug, label }]`; the first is shown first. */\n\t\tschemas: { type: Array, default: () => [] },\n\t\t/**\n\t\t * An app path that counts a schema's objects, `{schema}` filled in\n\t\t * (`/apps/openregister/api/objects/<register>/{schema}`), read with\n\t\t * `_limit=1` for its `total`.\n\t\t */\n\t\tcountUrl: { type: String, default: '' },\n\t\t/** A line above the table. */\n\t\tdescription: { type: String, default: '' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tschema: this.schemas.length > 0 ? String(this.schemas[0].slug) : '',\n\t\t\trows: [],\n\t\t\tpresent: null,\n\t\t\tloadError: '',\n\t\t\tadoptError: '',\n\t\t\tadopting: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {Array<{id: string, label: string}>} The picker's options. */\n\t\tschemaOptions() {\n\t\t\treturn this.schemas.map((option) => ({ id: String(option.slug), label: this.text(option.label || option.slug) }))\n\t\t},\n\n\t\tschemaOption: {\n\t\t\t/** @return {object|null} The chosen schema. */\n\t\t\tget() {\n\t\t\t\treturn this.schemaOptions.find((option) => option.id === this.schema) || null\n\t\t\t},\n\n\t\t\t/** @param {object|null} option The chosen schema. */\n\t\t\tset(option) {\n\t\t\t\tif (option) {\n\t\t\t\t\tthis.schema = option.id\n\t\t\t\t\tthis.reload()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\n\t\t/** @return {string} What the empty table says, which depends on what the register holds. */\n\t\temptyLine() {\n\t\t\tif (typeof this.present === 'number' && this.present > 0) {\n\t\t\t\treturn t('nextcloud-vue', 'The register holds {count} of these. None came from a shipped set, so there is no shipped version to compare with.', { count: this.present })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Nothing has been seeded yet.')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.reload()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * @param {string} value A declared text.\n\t\t * @return {string} It, translated by the host.\n\t\t */\n\t\ttext(value) {\n\t\t\treturn value ? String(this.cnTranslate(String(value))) : ''\n\t\t},\n\n\t\t/**\n\t\t * Read what shipped for the shown schema. A failed read empties the\n\t\t * table: rows left beside an error would read as the current state.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync reload() {\n\t\t\tif (!this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(generateUrl(`${this.endpoint}/${encodeURIComponent(this.schema)}`))\n\t\t\t\tthis.rows = Array.isArray(data && data.items) ? data.items : []\n\t\t\t\tthis.loadError = ''\n\t\t\t\tthis.present = null\n\t\t\t\tif (this.rows.length === 0) {\n\t\t\t\t\tthis.present = await this.count()\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.present = null\n\t\t\t\tthis.loadError = t('nextcloud-vue', 'Could not read the shipped configuration.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * How many objects of the shown schema the register holds, whatever\n\t\t * brought them.\n\t\t *\n\t\t * @return {Promise<number|null>} The count, or null when it cannot be read.\n\t\t */\n\t\tasync count() {\n\t\t\tif (!this.countUrl) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(generateUrl(this.countUrl.replace('{schema}', encodeURIComponent(this.schema))), { params: { _limit: 1 } })\n\t\t\t\tconst total = Number(data && data.total)\n\t\t\t\treturn Number.isFinite(total) ? total : null\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @param {object} row A ledger row.\n\t\t * @return {string} What an administrator reads beside the object.\n\t\t */\n\t\tstateLabel(row) {\n\t\t\tconst state = (row && row.state) || SHIPPED_STATES.local\n\t\t\treturn {\n\t\t\t\t[SHIPPED_STATES.shipped]: t('nextcloud-vue', 'Shipped, unchanged'),\n\t\t\t\t[SHIPPED_STATES.changed]: t('nextcloud-vue', 'Shipped, changed here'),\n\t\t\t\t[SHIPPED_STATES.removed]: t('nextcloud-vue', 'Shipped, removed here'),\n\t\t\t}[state] || t('nextcloud-vue', 'Yours')\n\t\t},\n\n\t\t/**\n\t\t * @param {object} row A ledger row.\n\t\t * @return {boolean} Whether a newer shipped version can be taken.\n\t\t */\n\t\tupdatable(row) {\n\t\t\treturn Boolean(row && row.updateAvailable && row.state !== SHIPPED_STATES.removed)\n\t\t},\n\n\t\t/**\n\t\t * @param {object} row A ledger row.\n\t\t * @return {boolean} Whether taking the newer version overwrites a local change.\n\t\t */\n\t\tlosesLocalChange(row) {\n\t\t\treturn Boolean(row && row.state === SHIPPED_STATES.changed)\n\t\t},\n\n\t\t/**\n\t\t * @param {object} row A ledger row.\n\t\t * @return {string} The button's words, which say when a local change goes.\n\t\t */\n\t\tadoptLabel(row) {\n\t\t\treturn this.losesLocalChange(row)\n\t\t\t\t? t('nextcloud-vue', 'Take {version} and lose your change', { version: row.latestVersion })\n\t\t\t\t: t('nextcloud-vue', 'Take {version}', { version: row.latestVersion })\n\t\t},\n\n\t\t/**\n\t\t * Take the newer shipped version of one object, then read the ledger again.\n\t\t *\n\t\t * @param {object} row A ledger row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync adopt(row) {\n\t\t\tthis.adopting = row.targetObject\n\t\t\tthis.adoptError = ''\n\t\t\ttry {\n\t\t\t\tawait axios.post(\n\t\t\t\t\tgenerateUrl(`${this.endpoint}/${encodeURIComponent(this.schema)}/${encodeURIComponent(row.targetObject)}/adopt`),\n\t\t\t\t\t{ set: row.set, acceptLocalChangeLoss: this.losesLocalChange(row) },\n\t\t\t\t)\n\t\t\t\tshowSuccess(t('nextcloud-vue', 'The newer version was taken.'))\n\t\t\t\tawait this.reload()\n\t\t\t} catch (error) {\n\t\t\t\tconst data = error && error.response && error.response.data\n\t\t\t\tthis.adoptError = String((data && (data.reason || data.message)) || t('nextcloud-vue', 'The newer version was not taken.'))\n\t\t\t} finally {\n\t\t\t\tthis.adopting = ''\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-shipped-configuration__intro,\n.cn-shipped-configuration__muted {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-shipped-configuration__picker {\n\tmax-width: 320px;\n}\n\n.cn-shipped-configuration__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tmargin-top: 12px;\n\t/* Nextcloud sets `table { white-space: nowrap }`; a cell here can be a sentence. */\n\twhite-space: normal;\n}\n\n.cn-shipped-configuration__table th,\n.cn-shipped-configuration__table td {\n\tpadding: 6px 8px;\n\tborder-bottom: 1px solid var(--color-border);\n\ttext-align: start;\n}\n</style>\n"],"names":["t","axios"],"mappings":"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"}
|
package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, toDisplayString, createCommentVNode, createBlock, createElementVNode, Fragment, renderList, withCtx, createTextVNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = {
|
|
4
|
+
class: "cn-shipped-configuration",
|
|
5
|
+
"data-testid": "cn-shipped-configuration"
|
|
6
|
+
};
|
|
7
|
+
const _hoisted_2 = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "cn-shipped-configuration__intro"
|
|
10
|
+
};
|
|
11
|
+
const _hoisted_3 = {
|
|
12
|
+
class: "cn-shipped-configuration__table",
|
|
13
|
+
"data-testid": "cn-shipped-configuration-table"
|
|
14
|
+
};
|
|
15
|
+
const _hoisted_4 = { scope: "col" };
|
|
16
|
+
const _hoisted_5 = { scope: "col" };
|
|
17
|
+
const _hoisted_6 = { scope: "col" };
|
|
18
|
+
const _hoisted_7 = { scope: "col" };
|
|
19
|
+
const _hoisted_8 = { scope: "col" };
|
|
20
|
+
const _hoisted_9 = ["data-testid"];
|
|
21
|
+
const _hoisted_10 = {
|
|
22
|
+
key: 1,
|
|
23
|
+
class: "cn-shipped-configuration__muted"
|
|
24
|
+
};
|
|
25
|
+
const _hoisted_11 = { key: 0 };
|
|
26
|
+
const _hoisted_12 = {
|
|
27
|
+
colspan: "5",
|
|
28
|
+
class: "cn-shipped-configuration__muted",
|
|
29
|
+
"data-testid": "cn-shipped-configuration-empty"
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
33
|
+
const _component_NcSelect = resolveComponent("NcSelect");
|
|
34
|
+
const _component_NcButton = resolveComponent("NcButton");
|
|
35
|
+
const _component_NcNoteCard = resolveComponent("NcNoteCard");
|
|
36
|
+
|
|
37
|
+
return (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
38
|
+
($props.description)
|
|
39
|
+
? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($options.text($props.description)), 1 /* TEXT */))
|
|
40
|
+
: createCommentVNode("v-if", true),
|
|
41
|
+
($props.schemas.length > 1)
|
|
42
|
+
? (openBlock(), createBlock(_component_NcSelect, {
|
|
43
|
+
key: 1,
|
|
44
|
+
modelValue: $options.schemaOption,
|
|
45
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (($options.schemaOption) = $event)),
|
|
46
|
+
class: "cn-shipped-configuration__picker",
|
|
47
|
+
options: $options.schemaOptions,
|
|
48
|
+
inputLabel: $options.t('nextcloud-vue', 'Show'),
|
|
49
|
+
clearable: false,
|
|
50
|
+
"data-testid": "cn-shipped-schema"
|
|
51
|
+
}, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel"]))
|
|
52
|
+
: createCommentVNode("v-if", true),
|
|
53
|
+
createElementVNode("table", _hoisted_3, [
|
|
54
|
+
createElementVNode("thead", null, [
|
|
55
|
+
createElementVNode("tr", null, [
|
|
56
|
+
createElementVNode("th", _hoisted_4, toDisplayString($options.t('nextcloud-vue', 'Name')), 1 /* TEXT */),
|
|
57
|
+
createElementVNode("th", _hoisted_5, toDisplayString($options.t('nextcloud-vue', 'State')), 1 /* TEXT */),
|
|
58
|
+
createElementVNode("th", _hoisted_6, toDisplayString($options.t('nextcloud-vue', 'Shipped set')), 1 /* TEXT */),
|
|
59
|
+
createElementVNode("th", _hoisted_7, toDisplayString($options.t('nextcloud-vue', 'Version')), 1 /* TEXT */),
|
|
60
|
+
createElementVNode("th", _hoisted_8, toDisplayString($options.t('nextcloud-vue', 'Newer version')), 1 /* TEXT */)
|
|
61
|
+
])
|
|
62
|
+
]),
|
|
63
|
+
createElementVNode("tbody", null, [
|
|
64
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($data.rows, (row) => {
|
|
65
|
+
return (openBlock(), createElementBlock("tr", {
|
|
66
|
+
key: row.targetObject
|
|
67
|
+
}, [
|
|
68
|
+
createElementVNode("td", null, toDisplayString(row.title || row.targetObject), 1 /* TEXT */),
|
|
69
|
+
createElementVNode("td", {
|
|
70
|
+
"data-testid": 'cn-shipped-state-' + row.targetObject
|
|
71
|
+
}, toDisplayString($options.stateLabel(row)), 9 /* TEXT, PROPS */, _hoisted_9),
|
|
72
|
+
createElementVNode("td", null, toDisplayString(row.set), 1 /* TEXT */),
|
|
73
|
+
createElementVNode("td", null, toDisplayString(row.setVersion), 1 /* TEXT */),
|
|
74
|
+
createElementVNode("td", null, [
|
|
75
|
+
($options.updatable(row))
|
|
76
|
+
? (openBlock(), createBlock(_component_NcButton, {
|
|
77
|
+
key: 0,
|
|
78
|
+
"data-testid": 'cn-shipped-adopt-' + row.targetObject,
|
|
79
|
+
disabled: $data.adopting === row.targetObject,
|
|
80
|
+
onClick: $event => ($options.adopt(row))
|
|
81
|
+
}, {
|
|
82
|
+
default: withCtx(() => [
|
|
83
|
+
createTextVNode(toDisplayString($options.adoptLabel(row)), 1 /* TEXT */)
|
|
84
|
+
]),
|
|
85
|
+
_: 2 /* DYNAMIC */
|
|
86
|
+
}, 1032 /* PROPS, DYNAMIC_SLOTS */, ["data-testid", "disabled", "onClick"]))
|
|
87
|
+
: (openBlock(), createElementBlock("span", _hoisted_10, toDisplayString($options.t('nextcloud-vue', 'Up to date')), 1 /* TEXT */))
|
|
88
|
+
])
|
|
89
|
+
]))
|
|
90
|
+
}), 128 /* KEYED_FRAGMENT */)),
|
|
91
|
+
(!$data.rows.length)
|
|
92
|
+
? (openBlock(), createElementBlock("tr", _hoisted_11, [
|
|
93
|
+
createElementVNode("td", _hoisted_12, toDisplayString($options.emptyLine), 1 /* TEXT */)
|
|
94
|
+
]))
|
|
95
|
+
: createCommentVNode("v-if", true)
|
|
96
|
+
])
|
|
97
|
+
]),
|
|
98
|
+
($data.loadError)
|
|
99
|
+
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
100
|
+
key: 2,
|
|
101
|
+
type: "error",
|
|
102
|
+
"data-testid": "cn-shipped-configuration-error"
|
|
103
|
+
}, {
|
|
104
|
+
default: withCtx(() => [
|
|
105
|
+
createTextVNode(toDisplayString($data.loadError), 1 /* TEXT */)
|
|
106
|
+
]),
|
|
107
|
+
_: 1 /* STABLE */
|
|
108
|
+
}))
|
|
109
|
+
: createCommentVNode("v-if", true),
|
|
110
|
+
($data.adoptError)
|
|
111
|
+
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
112
|
+
key: 3,
|
|
113
|
+
type: "error",
|
|
114
|
+
"data-testid": "cn-shipped-configuration-adopt-error"
|
|
115
|
+
}, {
|
|
116
|
+
default: withCtx(() => [
|
|
117
|
+
createTextVNode(toDisplayString($data.adoptError), 1 /* TEXT */)
|
|
118
|
+
]),
|
|
119
|
+
_: 1 /* STABLE */
|
|
120
|
+
}))
|
|
121
|
+
: createCommentVNode("v-if", true)
|
|
122
|
+
]))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { render };
|
|
126
|
+
//# sourceMappingURL=CnShippedConfigurationSection.vue3.js.map
|
package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnShippedConfigurationSection.vue3.js","sources":["../../../../src/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue?vue&type=template&id=fd515054&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-shipped-configuration\" data-testid=\"cn-shipped-configuration\">\n\t\t<p v-if=\"description\" class=\"cn-shipped-configuration__intro\">\n\t\t\t{{ text(description) }}\n\t\t</p>\n\n\t\t<NcSelect v-if=\"schemas.length > 1\"\n\t\t\tv-model=\"schemaOption\"\n\t\t\tclass=\"cn-shipped-configuration__picker\"\n\t\t\t:options=\"schemaOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Show')\"\n\t\t\t:clearable=\"false\"\n\t\t\tdata-testid=\"cn-shipped-schema\" />\n\n\t\t<table class=\"cn-shipped-configuration__table\" data-testid=\"cn-shipped-configuration-table\">\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Name') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'State') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Shipped set') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Version') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Newer version') }}\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t<tr v-for=\"row in rows\" :key=\"row.targetObject\">\n\t\t\t\t\t<td>{{ row.title || row.targetObject }}</td>\n\t\t\t\t\t<td :data-testid=\"'cn-shipped-state-' + row.targetObject\">\n\t\t\t\t\t\t{{ stateLabel(row) }}\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>{{ row.set }}</td>\n\t\t\t\t\t<td>{{ row.setVersion }}</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<NcButton v-if=\"updatable(row)\"\n\t\t\t\t\t\t\t:data-testid=\"'cn-shipped-adopt-' + row.targetObject\"\n\t\t\t\t\t\t\t:disabled=\"adopting === row.targetObject\"\n\t\t\t\t\t\t\t@click=\"adopt(row)\">\n\t\t\t\t\t\t\t{{ adoptLabel(row) }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t<span v-else class=\"cn-shipped-configuration__muted\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Up to date') }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t\t<tr v-if=\"!rows.length\">\n\t\t\t\t\t<td colspan=\"5\"\n\t\t\t\t\t\tclass=\"cn-shipped-configuration__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-shipped-configuration-empty\">\n\t\t\t\t\t\t{{ emptyLine }}\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<NcNoteCard v-if=\"loadError\" type=\"error\" data-testid=\"cn-shipped-configuration-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\t\t<NcNoteCard v-if=\"adoptError\" type=\"error\" data-testid=\"cn-shipped-configuration-adopt-error\">\n\t\t\t{{ adoptError }}\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { showSuccess } from '@nextcloud/dialogs'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\n\n/**\n * The states a seeded object can be in.\n *\n * @type {Readonly<Record<string, string>>}\n */\nexport const SHIPPED_STATES = Object.freeze({\n\tshipped: 'shipped',\n\tchanged: 'changed',\n\tlocal: 'local',\n\tremoved: 'removed',\n})\n\n/**\n * CnShippedConfigurationSection: what an app shipped, what this instance\n * changed, and what it authored. One row per seeded object with the set and\n * version it came from, its state, and the newer version offered per object.\n * Taking a newer version over a local change says so on the button, because\n * the change goes.\n *\n * The app's endpoint answers `GET {endpoint}/{schema}` with `{ items: [{\n * targetObject, title, state, set, setVersion, latestVersion, updateAvailable\n * }] }` and takes `POST {endpoint}/{schema}/{targetObject}/adopt` with `{ set,\n * acceptLocalChangeLoss }`, refusing with `{ reason }`. An empty ledger beside\n * a register that holds objects is said as such (`countUrl`), not as \"nothing\n * seeded\".\n *\n * @spec openspec/changes/shipped-configuration-section/specs/shipped-configuration-section/spec.md\n */\nexport default {\n\tname: 'CnShippedConfigurationSection',\n\n\tcomponents: { NcButton, NcNoteCard, NcSelect },\n\n\tinject: {\n\t\t/** The host app's translate, for the texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The app path of the shipped ledger (`/apps/<app>/api/starter/shipped`). */\n\t\tendpoint: { type: String, required: true },\n\t\t/** The schemas to show, `[{ slug, label }]`; the first is shown first. */\n\t\tschemas: { type: Array, default: () => [] },\n\t\t/**\n\t\t * An app path that counts a schema's objects, `{schema}` filled in\n\t\t * (`/apps/openregister/api/objects/<register>/{schema}`), read with\n\t\t * `_limit=1` for its `total`.\n\t\t */\n\t\tcountUrl: { type: String, default: '' },\n\t\t/** A line above the table. */\n\t\tdescription: { type: String, default: '' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tschema: this.schemas.length > 0 ? String(this.schemas[0].slug) : '',\n\t\t\trows: [],\n\t\t\tpresent: null,\n\t\t\tloadError: '',\n\t\t\tadoptError: '',\n\t\t\tadopting: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {Array<{id: string, label: string}>} The picker's options. */\n\t\tschemaOptions() {\n\t\t\treturn this.schemas.map((option) => ({ id: String(option.slug), label: this.text(option.label || option.slug) }))\n\t\t},\n\n\t\tschemaOption: {\n\t\t\t/** @return {object|null} The chosen schema. */\n\t\t\tget() {\n\t\t\t\treturn this.schemaOptions.find((option) => option.id === this.schema) || null\n\t\t\t},\n\n\t\t\t/** @param {object|null} option The chosen schema. */\n\t\t\tset(option) {\n\t\t\t\tif (option) {\n\t\t\t\t\tthis.schema = option.id\n\t\t\t\t\tthis.reload()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\n\t\t/** @return {string} What the empty table says, which depends on what the register holds. */\n\t\temptyLine() {\n\t\t\tif (typeof this.present === 'number' && this.present > 0) {\n\t\t\t\treturn t('nextcloud-vue', 'The register holds {count} of these. None came from a shipped set, so there is no shipped version to compare with.', { count: this.present })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Nothing has been seeded yet.')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.reload()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * @param {string} value A declared text.\n\t\t * @return {string} It, translated by the host.\n\t\t */\n\t\ttext(value) {\n\t\t\treturn value ? String(this.cnTranslate(String(value))) : ''\n\t\t},\n\n\t\t/**\n\t\t * Read what shipped for the shown schema. A failed read empties the\n\t\t * table: rows left beside an error would read as the current state.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync reload() {\n\t\t\tif (!this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(generateUrl(`${this.endpoint}/${encodeURIComponent(this.schema)}`))\n\t\t\t\tthis.rows = Array.isArray(data && data.items) ? data.items : []\n\t\t\t\tthis.loadError = ''\n\t\t\t\tthis.present = null\n\t\t\t\tif (this.rows.length === 0) {\n\t\t\t\t\tthis.present = await this.count()\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.present = null\n\t\t\t\tthis.loadError = t('nextcloud-vue', 'Could not read the shipped configuration.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * How many objects of the shown schema the register holds, whatever\n\t\t * brought them.\n\t\t *\n\t\t * @return {Promise<number|null>} The count, or null when it cannot be read.\n\t\t */\n\t\tasync count() {\n\t\t\tif (!this.countUrl) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(generateUrl(this.countUrl.replace('{schema}', encodeURIComponent(this.schema))), { params: { _limit: 1 } })\n\t\t\t\tconst total = Number(data && data.total)\n\t\t\t\treturn Number.isFinite(total) ? total : null\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @param {object} row A ledger row.\n\t\t * @return {string} What an administrator reads beside the object.\n\t\t */\n\t\tstateLabel(row) {\n\t\t\tconst state = (row && row.state) || SHIPPED_STATES.local\n\t\t\treturn {\n\t\t\t\t[SHIPPED_STATES.shipped]: t('nextcloud-vue', 'Shipped, unchanged'),\n\t\t\t\t[SHIPPED_STATES.changed]: t('nextcloud-vue', 'Shipped, changed here'),\n\t\t\t\t[SHIPPED_STATES.removed]: t('nextcloud-vue', 'Shipped, removed here'),\n\t\t\t}[state] || t('nextcloud-vue', 'Yours')\n\t\t},\n\n\t\t/**\n\t\t * @param {object} row A ledger row.\n\t\t * @return {boolean} Whether a newer shipped version can be taken.\n\t\t */\n\t\tupdatable(row) {\n\t\t\treturn Boolean(row && row.updateAvailable && row.state !== SHIPPED_STATES.removed)\n\t\t},\n\n\t\t/**\n\t\t * @param {object} row A ledger row.\n\t\t * @return {boolean} Whether taking the newer version overwrites a local change.\n\t\t */\n\t\tlosesLocalChange(row) {\n\t\t\treturn Boolean(row && row.state === SHIPPED_STATES.changed)\n\t\t},\n\n\t\t/**\n\t\t * @param {object} row A ledger row.\n\t\t * @return {string} The button's words, which say when a local change goes.\n\t\t */\n\t\tadoptLabel(row) {\n\t\t\treturn this.losesLocalChange(row)\n\t\t\t\t? t('nextcloud-vue', 'Take {version} and lose your change', { version: row.latestVersion })\n\t\t\t\t: t('nextcloud-vue', 'Take {version}', { version: row.latestVersion })\n\t\t},\n\n\t\t/**\n\t\t * Take the newer shipped version of one object, then read the ledger again.\n\t\t *\n\t\t * @param {object} row A ledger row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync adopt(row) {\n\t\t\tthis.adopting = row.targetObject\n\t\t\tthis.adoptError = ''\n\t\t\ttry {\n\t\t\t\tawait axios.post(\n\t\t\t\t\tgenerateUrl(`${this.endpoint}/${encodeURIComponent(this.schema)}/${encodeURIComponent(row.targetObject)}/adopt`),\n\t\t\t\t\t{ set: row.set, acceptLocalChangeLoss: this.losesLocalChange(row) },\n\t\t\t\t)\n\t\t\t\tshowSuccess(t('nextcloud-vue', 'The newer version was taken.'))\n\t\t\t\tawait this.reload()\n\t\t\t} catch (error) {\n\t\t\t\tconst data = error && error.response && error.response.data\n\t\t\t\tthis.adoptError = String((data && (data.reason || data.message)) || t('nextcloud-vue', 'The newer version was not taken.'))\n\t\t\t} finally {\n\t\t\t\tthis.adopting = ''\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-shipped-configuration__intro,\n.cn-shipped-configuration__muted {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-shipped-configuration__picker {\n\tmax-width: 320px;\n}\n\n.cn-shipped-configuration__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tmargin-top: 12px;\n\t/* Nextcloud sets `table { white-space: nowrap }`; a cell here can be a sentence. */\n\twhite-space: normal;\n}\n\n.cn-shipped-configuration__table th,\n.cn-shipped-configuration__table td {\n\tpadding: 6px 8px;\n\tborder-bottom: 1px solid var(--color-border);\n\ttext-align: start;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_createBlock","_createElementVNode","_Fragment","_renderList","_createTextVNode"],"mappings":"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"}
|