@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
|
@@ -60,6 +60,9 @@ import { searchFieldParams } from '../../utils/searchFieldParams.js';
|
|
|
60
60
|
* chosen values, `{ fieldKey: values }`. Mapped through the source's
|
|
61
61
|
* `searchFields` declaration into loader arguments and merged into every
|
|
62
62
|
* load, so a manifest page's sidebar narrows the SERVER's query.
|
|
63
|
+
* @param {import('vue').Ref<string>} [options.searchTerm] The page's search
|
|
64
|
+
* term. A source that names a `searchParam` gets it as that argument, so the
|
|
65
|
+
* server searches; a new term reloads from the first page.
|
|
63
66
|
*
|
|
64
67
|
* @return {object} { isNamedSource, namedSource, namedRows, namedLoading, namedQuickFilters }
|
|
65
68
|
*/
|
|
@@ -107,6 +110,7 @@ function useNamedSource(props, options = {}) {
|
|
|
107
110
|
const tabs = manifestTabs || sourceTabs;
|
|
108
111
|
const activeIndex = options.activeQuickFilterIndex || null;
|
|
109
112
|
const activeFilters = options.activeFilters || null;
|
|
113
|
+
const searchTerm = (typeof source.searchParam === 'string' && source.searchParam !== '') ? (options.searchTerm || null) : null;
|
|
110
114
|
// A source that pages (`pages: true`) gets the page as `limit` and
|
|
111
115
|
// `offset`, from the pager the page draws for it.
|
|
112
116
|
const paging = source.pages === true ? (options.paging || null) : null;
|
|
@@ -170,6 +174,7 @@ function useNamedSource(props, options = {}) {
|
|
|
170
174
|
activeFilters ? activeFilters.value : null,
|
|
171
175
|
props.entitySource,
|
|
172
176
|
),
|
|
177
|
+
...(searchTerm && searchTerm.value ? { [source.searchParam]: searchTerm.value } : {}),
|
|
173
178
|
...(paging ? { limit: paging.value.limit, offset: (paging.value.page - 1) * paging.value.limit } : {}),
|
|
174
179
|
},
|
|
175
180
|
tokenCtx(),
|
|
@@ -219,6 +224,10 @@ function useNamedSource(props, options = {}) {
|
|
|
219
224
|
watch(activeFilters, reloadFromFirstPage, { deep: true });
|
|
220
225
|
}
|
|
221
226
|
|
|
227
|
+
if (searchTerm) {
|
|
228
|
+
watch(searchTerm, reloadFromFirstPage);
|
|
229
|
+
}
|
|
230
|
+
|
|
222
231
|
// A new page, or a new page size, is a new request. A new tab or filter
|
|
223
232
|
// first returns to page 1, which reloads through here.
|
|
224
233
|
if (paging) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNamedSource.js","sources":["../../../../src/components/CnIndexPage/useNamedSource.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n */\n\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { computed, inject, onMounted, onUnmounted, watch } from 'vue'\nimport { resolveIndexSource } from '../../composables/indexSources.js'\nimport { withPersonalLenses } from '../../utils/personalLenses.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { searchFieldParams } from '../../utils/searchFieldParams.js'\n\n/**\n * Third data mode for CnIndexPage: a NAMED source.\n *\n * The page already knew two ways to get rows — fetch OpenRegister objects from\n * `register` + `schema`, or accept them as `:objects`. Neither reaches a list\n * that is not an OpenRegister object and not already in a parent's hands, so\n * those lists became `type: \"custom\"` pages whose only real job was loading\n * rows from somewhere else.\n *\n * `source` closes that: the manifest names a source and the index loads it.\n *\n * Precedence is deliberate and narrow. NON-EMPTY `:objects` still wins, so a\n * parent that hands rows down is never overridden by a source it did not ask\n * for; and a source wins over `register`/`schema`, because naming both is a\n * contradiction and silently self-fetching would render the WRONG list rather\n * than an obviously empty one.\n *\n * Note the test is \"has rows\", not \"was the prop passed\" — `useSelfFetchList`\n * uses the latter. The difference only shows for `:objects=\"[]\"` alongside a\n * source, where the source wins; that is the useful reading of an empty array\n * next to an explicit source name.\n *\n * Quick filters (cn-tasks-entity-source): a source may supply its own tabs\n * (`quickFilters`, same `{ label, filter, default? }` shape the manifest\n * uses). A manifest that declares `config.quickFilters` still wins. The\n * active tab's filter is merged OVER `sourceConfig` into every load, tab\n * winning on a colliding key, the same precedence self-fetch gives its tabs.\n * The default tab is seeded BEFORE the watcher registers, so mounting issues\n * exactly one request. Single mode only: `quickFilterMultiple` unions filter\n * maps for an OpenRegister query, which a source loader has no grammar for.\n *\n * 🔑 TOKENS RESOLVE HERE TOO. A tab filter is a filter, and a manifest author\n * writing `{ dueBefore: '@today+7d' }` has no way of knowing that the page\n * behind the label happens to be a named source rather than a self-fetch.\n * Without this the literal string `@today+7d` went over the wire, and a\n * source's server either rejected it or, worse, quietly answered the wrong\n * window: dossiq's \"Due this week\" lens is exactly that shape. Self-fetch\n * resolved tokens from the first day; this closes the gap rather than\n * teaching manifests which pages may use them.\n *\n * @param {object} props The CnIndexPage props.\n * @param {object} [options] Wiring handed down by CnIndexPage's setup.\n * @param {import('vue').Ref<number|null>} [options.activeQuickFilterIndex]\n * The shared tab-index ref `useSelfFetchList` owns (it exists even when\n * self-fetch is off); this composable seeds and watches it.\n * @param {import('vue').Ref<object>} [options.activeFilters] The sidebar's\n * chosen values, `{ fieldKey: values }`. Mapped through the source's\n * `searchFields` declaration into loader arguments and merged into every\n * load, so a manifest page's sidebar narrows the SERVER's query.\n *\n * @return {object} { isNamedSource, namedSource, namedRows, namedLoading, namedQuickFilters }\n */\nexport function useNamedSource(props, options = {}) {\n\tconst objectsProvided = !!(props.objects && props.objects.length > 0)\n\tconst isNamedSource = !!props.entitySource && !objectsProvided\n\n\tif (!isNamedSource) {\n\t\treturn {\n\t\t\tisNamedSource: false,\n\t\t\tnamedSource: null,\n\t\t\tnamedRows: computed(() => []),\n\t\t\tnamedLoading: computed(() => false),\n\t\t\tnamedQuickFilters: null,\n\t\t}\n\t}\n\n\tconst cnTranslate = inject('cnTranslate', null)\n\tconst source = resolveIndexSource(props.entitySource, props.sourceConfig, {\n\t\ttranslate: (text) => (typeof cnTranslate === 'function' ? cnTranslate(text) : text),\n\t})\n\n\t// An unknown source name resolves to null and was already warned about.\n\t// The page then behaves as an empty index rather than throwing.\n\tif (!source) {\n\t\treturn {\n\t\t\tisNamedSource: true,\n\t\t\tnamedSource: null,\n\t\t\tnamedRows: computed(() => []),\n\t\t\tnamedLoading: computed(() => false),\n\t\t\tnamedQuickFilters: null,\n\t\t}\n\t}\n\n\t// Effective tabs: the manifest's own strip wins, the source's is the\n\t// default. Both flow into the SAME load-merge below, so a manifest tab\n\t// can steer a source loader too.\n\tconst ownTabs = withPersonalLenses(props.quickFilters, props.personalLenses)\n\tconst manifestTabs = (Array.isArray(ownTabs) && ownTabs.length > 0)\n\t\t? ownTabs\n\t\t: null\n\tconst sourceTabs = (Array.isArray(source.quickFilters) && source.quickFilters.length > 0)\n\t\t? source.quickFilters\n\t\t: null\n\tconst tabs = manifestTabs || sourceTabs\n\tconst activeIndex = options.activeQuickFilterIndex || null\n\tconst activeFilters = options.activeFilters || null\n\t// A source that pages (`pages: true`) gets the page as `limit` and\n\t// `offset`, from the pager the page draws for it.\n\tconst paging = source.pages === true ? (options.paging || null) : null\n\n\t// Seed the default tab before the watcher below registers: the mount load\n\t// already carries this tab's filter, so the seed must not fire a second\n\t// request. `useSelfFetchList` resolved the index from the PROP only, so\n\t// source-supplied tabs arrive with the ref still null.\n\tif (tabs && activeIndex && activeIndex.value === null) {\n\t\tconst di = tabs.findIndex((tab) => tab && tab.default === true)\n\t\tactiveIndex.value = di >= 0 ? di : 0\n\t}\n\n\t// The same three bags `useSelfFetchList` injects, so a token means the\n\t// same thing on both kinds of page. They default to null/absent, so a\n\t// host that provides none is unaffected and `@today` still works: that\n\t// one needs no context at all.\n\tconst objectCtxRaw = inject('cnObjectContext', null)\n\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\tconst appConfigRaw = inject('cnAppConfig', null)\n\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\n\t/**\n\t * Build the token-resolution ctx from the injected bags, unwrapping refs.\n\t *\n\t * @return {object} The ctx `{ objectId?, object?, workspace, config }`.\n\t */\n\tconst tokenCtx = () => {\n\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\treturn base\n\t}\n\n\t/**\n\t * Load the source with the active tab's filter merged over the page's\n\t * `sourceConfig` (tab wins on a colliding key), tokens resolved.\n\t *\n\t * The ctx is rebuilt on every load rather than captured once, the same\n\t * way `useSelfFetchList` does it: `@today` has to mean today at the\n\t * moment of the fetch, not at the moment the page mounted, and a tab\n\t * left open across midnight would otherwise keep asking for yesterday.\n\t *\n\t * @return {Promise<void>} Resolves when the load settles.\n\t */\n\tconst loadActive = async () => {\n\t\tconst idx = activeIndex ? activeIndex.value : null\n\t\tconst tab = (tabs && idx !== null && idx !== undefined) ? tabs[idx] : null\n\t\t// 🔑 THE FIELD NARROWS WITHIN THE LENS, SO IT IS MERGED LAST. A lens\n\t\t// sets the question (\"unclaimed work\"); a field narrows the answer\n\t\t// (\"on this case\"). On the rare colliding key the field wins, because\n\t\t// it is the thing the person just chose and can see selected. Both\n\t\t// reach the SERVER: nothing here reduces a fetched page.\n\t\tconst config = resolveFilterTokens(\n\t\t\t{\n\t\t\t\t...(props.sourceConfig || {}),\n\t\t\t\t...((tab && tab.filter) || {}),\n\t\t\t\t...searchFieldParams(\n\t\t\t\t\tsource.searchFields,\n\t\t\t\t\tactiveFilters ? activeFilters.value : null,\n\t\t\t\t\tprops.entitySource,\n\t\t\t\t),\n\t\t\t\t...(paging ? { limit: paging.value.limit, offset: (paging.value.page - 1) * paging.value.limit } : {}),\n\t\t\t},\n\t\t\ttokenCtx(),\n\t\t)\n\t\ttry {\n\t\t\tawait source.load(config)\n\t\t} catch (error) {\n\t\t\t// Surfaced, not swallowed: a failed load and an empty source look\n\t\t\t// identical in the table, and only one of them is a problem.\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(`[CnIndexPage] entitySource \"${props.entitySource}\" failed to load`, error)\n\t\t}\n\t}\n\n\tonMounted(loadActive)\n\n\t// A dialog that acted on a row (CnActionDialog, CnAssignDialog) signals\n\t// `cn:page:refresh`; the rows it changed are read again.\n\tconst onPageRefresh = () => loadActive()\n\tonMounted(() => subscribe('cn:page:refresh', onPageRefresh))\n\tonUnmounted(() => unsubscribe('cn:page:refresh', onPageRefresh))\n\n\t// Registered AFTER the seed above, so only a person switching tabs\n\t// reloads. Without tabs there is nothing to watch.\n\t/**\n\t * Reload for a new query: on a paging source back to page 1, which\n\t * reloads through the paging watcher below.\n\t *\n\t * @return {void}\n\t */\n\tfunction reloadFromFirstPage() {\n\t\tif (paging && paging.value.page !== 1) {\n\t\t\tpaging.value = { ...paging.value, page: 1 }\n\t\t\treturn\n\t\t}\n\t\tloadActive()\n\t}\n\n\tif (tabs && activeIndex) {\n\t\twatch(activeIndex, reloadFromFirstPage)\n\t}\n\n\t// A sidebar choice is a new QUERY, not a new view of the page already\n\t// fetched, so it reloads. `deep` because the map is rewritten key by key\n\t// and a shallow watch misses a second value added to the same field.\n\tif (activeFilters) {\n\t\twatch(activeFilters, reloadFromFirstPage, { deep: true })\n\t}\n\n\t// A new page, or a new page size, is a new request. A new tab or filter\n\t// first returns to page 1, which reloads through here.\n\tif (paging) {\n\t\twatch(paging, loadActive, { deep: true })\n\t}\n\n\t// No manual invalidation tick here on purpose. The adapters read from a\n\t// reactive store, so a computed that calls into them tracks it the ordinary\n\t// way. A hand-cranked counter would REPLACE that reactivity rather than add\n\t// to it, and would then miss every update that did not go through load() —\n\t// a flow renamed in the editor, for instance.\n\treturn {\n\t\tisNamedSource: true,\n\t\tnamedSource: source,\n\t\tnamedRows: computed(() => source.rows() || []),\n\t\tnamedLoading: computed(() => source.loading()),\n\t\t// The tabs the page should RENDER: the manifest's when it declared\n\t\t// any (the strip then also drives self-authored filters), else the\n\t\t// source's own. Null when neither exists.\n\t\tnamedQuickFilters: tabs,\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useNamedSource.js","sources":["../../../../src/components/CnIndexPage/useNamedSource.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n */\n\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { computed, inject, onMounted, onUnmounted, watch } from 'vue'\nimport { resolveIndexSource } from '../../composables/indexSources.js'\nimport { withPersonalLenses } from '../../utils/personalLenses.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { searchFieldParams } from '../../utils/searchFieldParams.js'\n\n/**\n * Third data mode for CnIndexPage: a NAMED source.\n *\n * The page already knew two ways to get rows — fetch OpenRegister objects from\n * `register` + `schema`, or accept them as `:objects`. Neither reaches a list\n * that is not an OpenRegister object and not already in a parent's hands, so\n * those lists became `type: \"custom\"` pages whose only real job was loading\n * rows from somewhere else.\n *\n * `source` closes that: the manifest names a source and the index loads it.\n *\n * Precedence is deliberate and narrow. NON-EMPTY `:objects` still wins, so a\n * parent that hands rows down is never overridden by a source it did not ask\n * for; and a source wins over `register`/`schema`, because naming both is a\n * contradiction and silently self-fetching would render the WRONG list rather\n * than an obviously empty one.\n *\n * Note the test is \"has rows\", not \"was the prop passed\" — `useSelfFetchList`\n * uses the latter. The difference only shows for `:objects=\"[]\"` alongside a\n * source, where the source wins; that is the useful reading of an empty array\n * next to an explicit source name.\n *\n * Quick filters (cn-tasks-entity-source): a source may supply its own tabs\n * (`quickFilters`, same `{ label, filter, default? }` shape the manifest\n * uses). A manifest that declares `config.quickFilters` still wins. The\n * active tab's filter is merged OVER `sourceConfig` into every load, tab\n * winning on a colliding key, the same precedence self-fetch gives its tabs.\n * The default tab is seeded BEFORE the watcher registers, so mounting issues\n * exactly one request. Single mode only: `quickFilterMultiple` unions filter\n * maps for an OpenRegister query, which a source loader has no grammar for.\n *\n * 🔑 TOKENS RESOLVE HERE TOO. A tab filter is a filter, and a manifest author\n * writing `{ dueBefore: '@today+7d' }` has no way of knowing that the page\n * behind the label happens to be a named source rather than a self-fetch.\n * Without this the literal string `@today+7d` went over the wire, and a\n * source's server either rejected it or, worse, quietly answered the wrong\n * window: dossiq's \"Due this week\" lens is exactly that shape. Self-fetch\n * resolved tokens from the first day; this closes the gap rather than\n * teaching manifests which pages may use them.\n *\n * @param {object} props The CnIndexPage props.\n * @param {object} [options] Wiring handed down by CnIndexPage's setup.\n * @param {import('vue').Ref<number|null>} [options.activeQuickFilterIndex]\n * The shared tab-index ref `useSelfFetchList` owns (it exists even when\n * self-fetch is off); this composable seeds and watches it.\n * @param {import('vue').Ref<object>} [options.activeFilters] The sidebar's\n * chosen values, `{ fieldKey: values }`. Mapped through the source's\n * `searchFields` declaration into loader arguments and merged into every\n * load, so a manifest page's sidebar narrows the SERVER's query.\n * @param {import('vue').Ref<string>} [options.searchTerm] The page's search\n * term. A source that names a `searchParam` gets it as that argument, so the\n * server searches; a new term reloads from the first page.\n *\n * @return {object} { isNamedSource, namedSource, namedRows, namedLoading, namedQuickFilters }\n */\nexport function useNamedSource(props, options = {}) {\n\tconst objectsProvided = !!(props.objects && props.objects.length > 0)\n\tconst isNamedSource = !!props.entitySource && !objectsProvided\n\n\tif (!isNamedSource) {\n\t\treturn {\n\t\t\tisNamedSource: false,\n\t\t\tnamedSource: null,\n\t\t\tnamedRows: computed(() => []),\n\t\t\tnamedLoading: computed(() => false),\n\t\t\tnamedQuickFilters: null,\n\t\t}\n\t}\n\n\tconst cnTranslate = inject('cnTranslate', null)\n\tconst source = resolveIndexSource(props.entitySource, props.sourceConfig, {\n\t\ttranslate: (text) => (typeof cnTranslate === 'function' ? cnTranslate(text) : text),\n\t})\n\n\t// An unknown source name resolves to null and was already warned about.\n\t// The page then behaves as an empty index rather than throwing.\n\tif (!source) {\n\t\treturn {\n\t\t\tisNamedSource: true,\n\t\t\tnamedSource: null,\n\t\t\tnamedRows: computed(() => []),\n\t\t\tnamedLoading: computed(() => false),\n\t\t\tnamedQuickFilters: null,\n\t\t}\n\t}\n\n\t// Effective tabs: the manifest's own strip wins, the source's is the\n\t// default. Both flow into the SAME load-merge below, so a manifest tab\n\t// can steer a source loader too.\n\tconst ownTabs = withPersonalLenses(props.quickFilters, props.personalLenses)\n\tconst manifestTabs = (Array.isArray(ownTabs) && ownTabs.length > 0)\n\t\t? ownTabs\n\t\t: null\n\tconst sourceTabs = (Array.isArray(source.quickFilters) && source.quickFilters.length > 0)\n\t\t? source.quickFilters\n\t\t: null\n\tconst tabs = manifestTabs || sourceTabs\n\tconst activeIndex = options.activeQuickFilterIndex || null\n\tconst activeFilters = options.activeFilters || null\n\tconst searchTerm = (typeof source.searchParam === 'string' && source.searchParam !== '') ? (options.searchTerm || null) : null\n\t// A source that pages (`pages: true`) gets the page as `limit` and\n\t// `offset`, from the pager the page draws for it.\n\tconst paging = source.pages === true ? (options.paging || null) : null\n\n\t// Seed the default tab before the watcher below registers: the mount load\n\t// already carries this tab's filter, so the seed must not fire a second\n\t// request. `useSelfFetchList` resolved the index from the PROP only, so\n\t// source-supplied tabs arrive with the ref still null.\n\tif (tabs && activeIndex && activeIndex.value === null) {\n\t\tconst di = tabs.findIndex((tab) => tab && tab.default === true)\n\t\tactiveIndex.value = di >= 0 ? di : 0\n\t}\n\n\t// The same three bags `useSelfFetchList` injects, so a token means the\n\t// same thing on both kinds of page. They default to null/absent, so a\n\t// host that provides none is unaffected and `@today` still works: that\n\t// one needs no context at all.\n\tconst objectCtxRaw = inject('cnObjectContext', null)\n\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\tconst appConfigRaw = inject('cnAppConfig', null)\n\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\n\t/**\n\t * Build the token-resolution ctx from the injected bags, unwrapping refs.\n\t *\n\t * @return {object} The ctx `{ objectId?, object?, workspace, config }`.\n\t */\n\tconst tokenCtx = () => {\n\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\treturn base\n\t}\n\n\t/**\n\t * Load the source with the active tab's filter merged over the page's\n\t * `sourceConfig` (tab wins on a colliding key), tokens resolved.\n\t *\n\t * The ctx is rebuilt on every load rather than captured once, the same\n\t * way `useSelfFetchList` does it: `@today` has to mean today at the\n\t * moment of the fetch, not at the moment the page mounted, and a tab\n\t * left open across midnight would otherwise keep asking for yesterday.\n\t *\n\t * @return {Promise<void>} Resolves when the load settles.\n\t */\n\tconst loadActive = async () => {\n\t\tconst idx = activeIndex ? activeIndex.value : null\n\t\tconst tab = (tabs && idx !== null && idx !== undefined) ? tabs[idx] : null\n\t\t// 🔑 THE FIELD NARROWS WITHIN THE LENS, SO IT IS MERGED LAST. A lens\n\t\t// sets the question (\"unclaimed work\"); a field narrows the answer\n\t\t// (\"on this case\"). On the rare colliding key the field wins, because\n\t\t// it is the thing the person just chose and can see selected. Both\n\t\t// reach the SERVER: nothing here reduces a fetched page.\n\t\tconst config = resolveFilterTokens(\n\t\t\t{\n\t\t\t\t...(props.sourceConfig || {}),\n\t\t\t\t...((tab && tab.filter) || {}),\n\t\t\t\t...searchFieldParams(\n\t\t\t\t\tsource.searchFields,\n\t\t\t\t\tactiveFilters ? activeFilters.value : null,\n\t\t\t\t\tprops.entitySource,\n\t\t\t\t),\n\t\t\t\t...(searchTerm && searchTerm.value ? { [source.searchParam]: searchTerm.value } : {}),\n\t\t\t\t...(paging ? { limit: paging.value.limit, offset: (paging.value.page - 1) * paging.value.limit } : {}),\n\t\t\t},\n\t\t\ttokenCtx(),\n\t\t)\n\t\ttry {\n\t\t\tawait source.load(config)\n\t\t} catch (error) {\n\t\t\t// Surfaced, not swallowed: a failed load and an empty source look\n\t\t\t// identical in the table, and only one of them is a problem.\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(`[CnIndexPage] entitySource \"${props.entitySource}\" failed to load`, error)\n\t\t}\n\t}\n\n\tonMounted(loadActive)\n\n\t// A dialog that acted on a row (CnActionDialog, CnAssignDialog) signals\n\t// `cn:page:refresh`; the rows it changed are read again.\n\tconst onPageRefresh = () => loadActive()\n\tonMounted(() => subscribe('cn:page:refresh', onPageRefresh))\n\tonUnmounted(() => unsubscribe('cn:page:refresh', onPageRefresh))\n\n\t// Registered AFTER the seed above, so only a person switching tabs\n\t// reloads. Without tabs there is nothing to watch.\n\t/**\n\t * Reload for a new query: on a paging source back to page 1, which\n\t * reloads through the paging watcher below.\n\t *\n\t * @return {void}\n\t */\n\tfunction reloadFromFirstPage() {\n\t\tif (paging && paging.value.page !== 1) {\n\t\t\tpaging.value = { ...paging.value, page: 1 }\n\t\t\treturn\n\t\t}\n\t\tloadActive()\n\t}\n\n\tif (tabs && activeIndex) {\n\t\twatch(activeIndex, reloadFromFirstPage)\n\t}\n\n\t// A sidebar choice is a new QUERY, not a new view of the page already\n\t// fetched, so it reloads. `deep` because the map is rewritten key by key\n\t// and a shallow watch misses a second value added to the same field.\n\tif (activeFilters) {\n\t\twatch(activeFilters, reloadFromFirstPage, { deep: true })\n\t}\n\n\tif (searchTerm) {\n\t\twatch(searchTerm, reloadFromFirstPage)\n\t}\n\n\t// A new page, or a new page size, is a new request. A new tab or filter\n\t// first returns to page 1, which reloads through here.\n\tif (paging) {\n\t\twatch(paging, loadActive, { deep: true })\n\t}\n\n\t// No manual invalidation tick here on purpose. The adapters read from a\n\t// reactive store, so a computed that calls into them tracks it the ordinary\n\t// way. A hand-cranked counter would REPLACE that reactivity rather than add\n\t// to it, and would then miss every update that did not go through load() —\n\t// a flow renamed in the editor, for instance.\n\treturn {\n\t\tisNamedSource: true,\n\t\tnamedSource: source,\n\t\tnamedRows: computed(() => source.rows() || []),\n\t\tnamedLoading: computed(() => source.loading()),\n\t\t// The tabs the page should RENDER: the manifest's when it declared\n\t\t// any (the strip then also drives self-authored filters), else the\n\t\t// source's own. Null when neither exists.\n\t\tnamedQuickFilters: tabs,\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import script from './CnReadinessChecklist.vue2.js';
|
|
2
|
+
import { render } from './CnReadinessChecklist.vue3.js';
|
|
3
|
+
|
|
4
|
+
script.render = render;
|
|
5
|
+
script.__scopeId = "data-v-4e10a556";
|
|
6
|
+
const __sfc_main = script;
|
|
7
|
+
|
|
8
|
+
export { __sfc_main as default };
|
|
9
|
+
//# sourceMappingURL=CnReadinessChecklist.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnReadinessChecklist.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import axios__default from '@nextcloud/axios';
|
|
2
|
+
import { translate } from '@nextcloud/l10n';
|
|
3
|
+
import { generateUrl } from '@nextcloud/router';
|
|
4
|
+
import { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue';
|
|
5
|
+
import { readPath } from '../../utils/readPath.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The fields an item is read from by default.
|
|
9
|
+
*
|
|
10
|
+
* @type {Readonly<Record<string, string>>}
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_FIELDS = Object.freeze({
|
|
13
|
+
id: 'id',
|
|
14
|
+
label: 'label',
|
|
15
|
+
explanation: 'explanation',
|
|
16
|
+
met: 'met',
|
|
17
|
+
fixHref: 'fixHref',
|
|
18
|
+
failure: 'failure',
|
|
19
|
+
group: 'group',
|
|
20
|
+
blocking: 'blocking',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* CnReadinessChecklist: a list of things an instance needs, each with its
|
|
25
|
+
* state in words (met, open, unknown), an explanation and a link to the
|
|
26
|
+
* screen that fixes it. Reported, never gated: nothing here blocks the app.
|
|
27
|
+
*
|
|
28
|
+
* Items come from `items`, or are read from an app endpoint (`source`, an app
|
|
29
|
+
* path resolved with `generateUrl`, the list at `itemsPath`) and re-read when
|
|
30
|
+
* the reader comes back to the tab. `fields` maps an item's keys onto the
|
|
31
|
+
* row's (`{ label: 'title', met: 'done', fixHref: 'screen' }`). Items carrying
|
|
32
|
+
* a `group` render under that group's heading (`groups`, in order).
|
|
33
|
+
*
|
|
34
|
+
* @spec openspec/changes/readiness-checklist/specs/readiness-checklist/spec.md
|
|
35
|
+
*/
|
|
36
|
+
var script = {
|
|
37
|
+
name: 'CnReadinessChecklist',
|
|
38
|
+
|
|
39
|
+
components: { NcLoadingIcon, NcNoteCard },
|
|
40
|
+
|
|
41
|
+
inject: {
|
|
42
|
+
/** The host app's translate, for texts the page declares. */
|
|
43
|
+
cnTranslate: { default: () => (key) => key },
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
props: {
|
|
47
|
+
/** The items: `{ id, label, explanation?, met (true|false|null), fixHref?, failure?, group?, blocking? }`. */
|
|
48
|
+
items: { type: Array, default: () => [] },
|
|
49
|
+
/** An app path to read the items from (`/apps/<app>/api/setup/status`). */
|
|
50
|
+
source: { type: String, default: '' },
|
|
51
|
+
/** Where the items sit in the answer. */
|
|
52
|
+
itemsPath: { type: String, default: 'items' },
|
|
53
|
+
/** Item key to row key, over the defaults. */
|
|
54
|
+
fields: { type: Object, default: () => ({}) },
|
|
55
|
+
/** Headings for grouped items, in order: `[{ id, title }]`. */
|
|
56
|
+
groups: { type: Array, default: () => [] },
|
|
57
|
+
/** A line above the list. */
|
|
58
|
+
description: { type: String, default: '' },
|
|
59
|
+
/** The words for each state: `{ met, unmet, unknown }`. */
|
|
60
|
+
statusLabels: { type: Object, default: () => ({}) },
|
|
61
|
+
/** Said when there is nothing to check. */
|
|
62
|
+
emptyText: { type: String, default: '' },
|
|
63
|
+
/** Said when the source could not be read. */
|
|
64
|
+
errorText: { type: String, default: '' },
|
|
65
|
+
/** Said before the unmet items marked `blocking` ("Cannot run until this is installed:"). */
|
|
66
|
+
blockingText: { type: String, default: '' },
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
emits: ['loaded'],
|
|
70
|
+
|
|
71
|
+
data() {
|
|
72
|
+
return {
|
|
73
|
+
loading: false,
|
|
74
|
+
loadError: '',
|
|
75
|
+
loadedItems: null,
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
computed: {
|
|
80
|
+
/** @return {Array<object>} The items, normalised. */
|
|
81
|
+
shownItems() {
|
|
82
|
+
const raw = this.loadedItems !== null ? this.loadedItems : this.items;
|
|
83
|
+
const map = { ...DEFAULT_FIELDS, ...(this.fields || {}) };
|
|
84
|
+
return (Array.isArray(raw) ? raw : []).map((row, index) => {
|
|
85
|
+
const read = (key) => readPath(row, map[key]);
|
|
86
|
+
const met = read('met');
|
|
87
|
+
return {
|
|
88
|
+
id: String(read('id') ?? index),
|
|
89
|
+
label: this.text(read('label') ?? read('id') ?? ''),
|
|
90
|
+
explanation: this.text(read('explanation') ?? ''),
|
|
91
|
+
met: met === true || met === false ? met : null,
|
|
92
|
+
fixHref: typeof read('fixHref') === 'string' ? read('fixHref') : '',
|
|
93
|
+
failure: read('failure') ? String(read('failure')) : '',
|
|
94
|
+
group: read('group') ? String(read('group')) : '',
|
|
95
|
+
blocking: read('blocking') === true,
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
/** @return {Array<object>} The unmet blocking items. */
|
|
101
|
+
blockingItems() {
|
|
102
|
+
return this.shownItems.filter((item) => item.blocking && item.met !== true)
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/** @return {Array<{id: string, title: string, items: Array<object>}>} The groups that hold items, in order. */
|
|
106
|
+
shownGroups() {
|
|
107
|
+
const declared = (Array.isArray(this.groups) ? this.groups : []).map((group) => ({
|
|
108
|
+
id: String(group.id),
|
|
109
|
+
title: this.text(group.title || ''),
|
|
110
|
+
items: this.shownItems.filter((item) => item.group === String(group.id)),
|
|
111
|
+
}));
|
|
112
|
+
const known = new Set(declared.map((group) => group.id));
|
|
113
|
+
const rest = this.shownItems.filter((item) => !known.has(item.group));
|
|
114
|
+
const all = rest.length > 0 ? [{ id: 'items', title: '', items: rest }, ...declared] : declared;
|
|
115
|
+
return all.filter((group) => group.items.length > 0)
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
watch: {
|
|
120
|
+
source() {
|
|
121
|
+
this.reload();
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
mounted() {
|
|
126
|
+
this.reload();
|
|
127
|
+
window.addEventListener('focus', this.onFocus);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
beforeUnmount() {
|
|
131
|
+
window.removeEventListener('focus', this.onFocus);
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
methods: {
|
|
135
|
+
t: translate,
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* A declared text through the host's translate.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} value The text.
|
|
141
|
+
* @return {string} The translated text.
|
|
142
|
+
*/
|
|
143
|
+
text(value) {
|
|
144
|
+
return value ? String(this.cnTranslate(String(value))) : ''
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {object} item The item.
|
|
149
|
+
* @return {string} `met`, `unmet` or `unknown`.
|
|
150
|
+
*/
|
|
151
|
+
stateOf(item) {
|
|
152
|
+
if (item.met === true) {
|
|
153
|
+
return 'met'
|
|
154
|
+
}
|
|
155
|
+
return item.met === false ? 'unmet' : 'unknown'
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The word for an item's state.
|
|
160
|
+
*
|
|
161
|
+
* @param {object} item The item.
|
|
162
|
+
* @return {string} The word.
|
|
163
|
+
*/
|
|
164
|
+
stateLabel(item) {
|
|
165
|
+
const state = this.stateOf(item);
|
|
166
|
+
const own = this.statusLabels && this.statusLabels[state];
|
|
167
|
+
if (own) {
|
|
168
|
+
return this.text(own)
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
met: translate('nextcloud-vue', 'Done'),
|
|
172
|
+
unmet: translate('nextcloud-vue', 'Still open'),
|
|
173
|
+
unknown: translate('nextcloud-vue', 'Unknown'),
|
|
174
|
+
}[state]
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Read the items again, from the source. The source asks the instance
|
|
179
|
+
* rather than a stored flag, so a fix made elsewhere shows on return.
|
|
180
|
+
*
|
|
181
|
+
* @return {Promise<void>}
|
|
182
|
+
*/
|
|
183
|
+
async reload() {
|
|
184
|
+
if (!this.source) {
|
|
185
|
+
this.loadedItems = null;
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
this.loading = this.loadedItems === null;
|
|
189
|
+
this.loadError = '';
|
|
190
|
+
try {
|
|
191
|
+
const { data } = await axios__default.get(generateUrl(this.source));
|
|
192
|
+
const rows = Array.isArray(data) ? data : readPath(data, this.itemsPath);
|
|
193
|
+
this.loadedItems = Array.isArray(rows) ? rows : [];
|
|
194
|
+
/** @event loaded The source answered. Payload: the whole answer. */
|
|
195
|
+
this.$emit('loaded', data);
|
|
196
|
+
} catch {
|
|
197
|
+
this.loadedItems = [];
|
|
198
|
+
this.loadError = this.errorText ? this.text(this.errorText) : translate('nextcloud-vue', 'The checklist could not be read.');
|
|
199
|
+
} finally {
|
|
200
|
+
this.loading = false;
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
/** Re-read when the reader comes back from fixing something. */
|
|
205
|
+
onFocus() {
|
|
206
|
+
if (this.source) {
|
|
207
|
+
this.reload();
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export { script as default };
|
|
214
|
+
//# sourceMappingURL=CnReadinessChecklist.vue2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnReadinessChecklist.vue2.js","sources":["../../../../src/components/CnReadinessChecklist/CnReadinessChecklist.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-readiness-checklist\" data-testid=\"cn-readiness-checklist\">\n\t\t<p v-if=\"description\" class=\"cn-readiness-checklist__description\">\n\t\t\t{{ description }}\n\t\t</p>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\n\t\t<NcNoteCard v-else-if=\"loadError\" type=\"error\" data-testid=\"cn-readiness-checklist-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\n\t\t<template v-else>\n\t\t\t<NcNoteCard v-if=\"blockingText && blockingItems.length > 0\"\n\t\t\t\ttype=\"error\"\n\t\t\t\tdata-testid=\"cn-readiness-checklist-blocking\">\n\t\t\t\t{{ blockingText }} {{ blockingItems.map((item) => item.label).join(', ') }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"shownItems.length === 0\" class=\"cn-readiness-checklist__empty\" data-testid=\"cn-readiness-checklist-empty\">\n\t\t\t\t{{ emptyText || t('nextcloud-vue', 'Nothing to check.') }}\n\t\t\t</p>\n\n\t\t\t<section v-for=\"group in shownGroups\"\n\t\t\t\t:key=\"group.id\"\n\t\t\t\tclass=\"cn-readiness-checklist__group\"\n\t\t\t\t:data-testid=\"`cn-readiness-checklist-group-${group.id}`\">\n\t\t\t\t<h4 v-if=\"group.title\" class=\"cn-readiness-checklist__group-title\">\n\t\t\t\t\t{{ group.title }}\n\t\t\t\t</h4>\n\t\t\t\t<ul class=\"cn-readiness-checklist__list\">\n\t\t\t\t\t<li v-for=\"item in group.items\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-readiness-checklist__item\"\n\t\t\t\t\t\t:data-testid=\"`cn-readiness-item-${item.id}`\">\n\t\t\t\t\t\t<!-- The state is a word, not a colour (WCAG 1.4.1); the\n\t\t\t\t\t\t colour repeats it. -->\n\t\t\t\t\t\t<span class=\"cn-readiness-checklist__state\"\n\t\t\t\t\t\t\t:class=\"`cn-readiness-checklist__state--${stateOf(item)}`\"\n\t\t\t\t\t\t\tdata-testid=\"cn-readiness-item-state\">\n\t\t\t\t\t\t\t{{ stateLabel(item) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-readiness-checklist__body\">\n\t\t\t\t\t\t\t<a v-if=\"item.fixHref\"\n\t\t\t\t\t\t\t\t:href=\"item.fixHref\"\n\t\t\t\t\t\t\t\tclass=\"cn-readiness-checklist__label\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-readiness-item-fix\">{{ item.label }}</a>\n\t\t\t\t\t\t\t<span v-else class=\"cn-readiness-checklist__label\">{{ item.label }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.explanation\" class=\"cn-readiness-checklist__explanation\">{{ item.explanation }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.failure\" class=\"cn-readiness-checklist__failure\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'This could not be read: {reason}', { reason: item.failure }) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * The fields an item is read from by default.\n *\n * @type {Readonly<Record<string, string>>}\n */\nconst DEFAULT_FIELDS = Object.freeze({\n\tid: 'id',\n\tlabel: 'label',\n\texplanation: 'explanation',\n\tmet: 'met',\n\tfixHref: 'fixHref',\n\tfailure: 'failure',\n\tgroup: 'group',\n\tblocking: 'blocking',\n})\n\n/**\n * CnReadinessChecklist: a list of things an instance needs, each with its\n * state in words (met, open, unknown), an explanation and a link to the\n * screen that fixes it. Reported, never gated: nothing here blocks the app.\n *\n * Items come from `items`, or are read from an app endpoint (`source`, an app\n * path resolved with `generateUrl`, the list at `itemsPath`) and re-read when\n * the reader comes back to the tab. `fields` maps an item's keys onto the\n * row's (`{ label: 'title', met: 'done', fixHref: 'screen' }`). Items carrying\n * a `group` render under that group's heading (`groups`, in order).\n *\n * @spec openspec/changes/readiness-checklist/specs/readiness-checklist/spec.md\n */\nexport default {\n\tname: 'CnReadinessChecklist',\n\n\tcomponents: { NcLoadingIcon, NcNoteCard },\n\n\tinject: {\n\t\t/** The host app's translate, for texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The items: `{ id, label, explanation?, met (true|false|null), fixHref?, failure?, group?, blocking? }`. */\n\t\titems: { type: Array, default: () => [] },\n\t\t/** An app path to read the items from (`/apps/<app>/api/setup/status`). */\n\t\tsource: { type: String, default: '' },\n\t\t/** Where the items sit in the answer. */\n\t\titemsPath: { type: String, default: 'items' },\n\t\t/** Item key to row key, over the defaults. */\n\t\tfields: { type: Object, default: () => ({}) },\n\t\t/** Headings for grouped items, in order: `[{ id, title }]`. */\n\t\tgroups: { type: Array, default: () => [] },\n\t\t/** A line above the list. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** The words for each state: `{ met, unmet, unknown }`. */\n\t\tstatusLabels: { type: Object, default: () => ({}) },\n\t\t/** Said when there is nothing to check. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** Said when the source could not be read. */\n\t\terrorText: { type: String, default: '' },\n\t\t/** Said before the unmet items marked `blocking` (\"Cannot run until this is installed:\"). */\n\t\tblockingText: { type: String, default: '' },\n\t},\n\n\temits: ['loaded'],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: false,\n\t\t\tloadError: '',\n\t\t\tloadedItems: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {Array<object>} The items, normalised. */\n\t\tshownItems() {\n\t\t\tconst raw = this.loadedItems !== null ? this.loadedItems : this.items\n\t\t\tconst map = { ...DEFAULT_FIELDS, ...(this.fields || {}) }\n\t\t\treturn (Array.isArray(raw) ? raw : []).map((row, index) => {\n\t\t\t\tconst read = (key) => readPath(row, map[key])\n\t\t\t\tconst met = read('met')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(read('id') ?? index),\n\t\t\t\t\tlabel: this.text(read('label') ?? read('id') ?? ''),\n\t\t\t\t\texplanation: this.text(read('explanation') ?? ''),\n\t\t\t\t\tmet: met === true || met === false ? met : null,\n\t\t\t\t\tfixHref: typeof read('fixHref') === 'string' ? read('fixHref') : '',\n\t\t\t\t\tfailure: read('failure') ? String(read('failure')) : '',\n\t\t\t\t\tgroup: read('group') ? String(read('group')) : '',\n\t\t\t\t\tblocking: read('blocking') === true,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The unmet blocking items. */\n\t\tblockingItems() {\n\t\t\treturn this.shownItems.filter((item) => item.blocking && item.met !== true)\n\t\t},\n\n\t\t/** @return {Array<{id: string, title: string, items: Array<object>}>} The groups that hold items, in order. */\n\t\tshownGroups() {\n\t\t\tconst declared = (Array.isArray(this.groups) ? this.groups : []).map((group) => ({\n\t\t\t\tid: String(group.id),\n\t\t\t\ttitle: this.text(group.title || ''),\n\t\t\t\titems: this.shownItems.filter((item) => item.group === String(group.id)),\n\t\t\t}))\n\t\t\tconst known = new Set(declared.map((group) => group.id))\n\t\t\tconst rest = this.shownItems.filter((item) => !known.has(item.group))\n\t\t\tconst all = rest.length > 0 ? [{ id: 'items', title: '', items: rest }, ...declared] : declared\n\t\t\treturn all.filter((group) => group.items.length > 0)\n\t\t},\n\t},\n\n\twatch: {\n\t\tsource() {\n\t\t\tthis.reload()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.reload()\n\t\twindow.addEventListener('focus', this.onFocus)\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('focus', this.onFocus)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A declared text through the host's translate.\n\t\t *\n\t\t * @param {string} value The text.\n\t\t * @return {string} The translated text.\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 * @param {object} item The item.\n\t\t * @return {string} `met`, `unmet` or `unknown`.\n\t\t */\n\t\tstateOf(item) {\n\t\t\tif (item.met === true) {\n\t\t\t\treturn 'met'\n\t\t\t}\n\t\t\treturn item.met === false ? 'unmet' : 'unknown'\n\t\t},\n\n\t\t/**\n\t\t * The word for an item's state.\n\t\t *\n\t\t * @param {object} item The item.\n\t\t * @return {string} The word.\n\t\t */\n\t\tstateLabel(item) {\n\t\t\tconst state = this.stateOf(item)\n\t\t\tconst own = this.statusLabels && this.statusLabels[state]\n\t\t\tif (own) {\n\t\t\t\treturn this.text(own)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmet: t('nextcloud-vue', 'Done'),\n\t\t\t\tunmet: t('nextcloud-vue', 'Still open'),\n\t\t\t\tunknown: t('nextcloud-vue', 'Unknown'),\n\t\t\t}[state]\n\t\t},\n\n\t\t/**\n\t\t * Read the items again, from the source. The source asks the instance\n\t\t * rather than a stored flag, so a fix made elsewhere shows on return.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync reload() {\n\t\t\tif (!this.source) {\n\t\t\t\tthis.loadedItems = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = this.loadedItems === null\n\t\t\tthis.loadError = ''\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(generateUrl(this.source))\n\t\t\t\tconst rows = Array.isArray(data) ? data : readPath(data, this.itemsPath)\n\t\t\t\tthis.loadedItems = Array.isArray(rows) ? rows : []\n\t\t\t\t/** @event loaded The source answered. Payload: the whole answer. */\n\t\t\t\tthis.$emit('loaded', data)\n\t\t\t} catch {\n\t\t\t\tthis.loadedItems = []\n\t\t\t\tthis.loadError = this.errorText ? this.text(this.errorText) : t('nextcloud-vue', 'The checklist could not be read.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/** Re-read when the reader comes back from fixing something. */\n\t\tonFocus() {\n\t\t\tif (this.source) {\n\t\t\t\tthis.reload()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-readiness-checklist__description,\n.cn-readiness-checklist__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0 0 12px;\n}\n\n.cn-readiness-checklist__group-title {\n\tmargin: 12px 0 4px;\n}\n\n.cn-readiness-checklist__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-readiness-checklist__item {\n\tdisplay: flex;\n\tgap: calc(var(--default-grid-baseline, 4px) * 3);\n\tpadding-block: calc(var(--default-grid-baseline, 4px) * 2);\n\tborder-block-end: 1px solid var(--color-border);\n}\n\n.cn-readiness-checklist__state {\n\tflex: 0 0 auto;\n\tmin-width: 6em;\n\tfont-weight: bold;\n}\n\n.cn-readiness-checklist__state--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-readiness-checklist__state--unmet {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-readiness-checklist__state--unknown {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-readiness-checklist__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-readiness-checklist__explanation {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-readiness-checklist__failure {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["t","axios"],"mappings":"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"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, toDisplayString, createCommentVNode, createBlock, withCtx, createTextVNode, Fragment, renderList, createElementVNode, normalizeClass } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = {
|
|
4
|
+
class: "cn-readiness-checklist",
|
|
5
|
+
"data-testid": "cn-readiness-checklist"
|
|
6
|
+
};
|
|
7
|
+
const _hoisted_2 = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "cn-readiness-checklist__description"
|
|
10
|
+
};
|
|
11
|
+
const _hoisted_3 = {
|
|
12
|
+
key: 1,
|
|
13
|
+
class: "cn-readiness-checklist__empty",
|
|
14
|
+
"data-testid": "cn-readiness-checklist-empty"
|
|
15
|
+
};
|
|
16
|
+
const _hoisted_4 = ["data-testid"];
|
|
17
|
+
const _hoisted_5 = {
|
|
18
|
+
key: 0,
|
|
19
|
+
class: "cn-readiness-checklist__group-title"
|
|
20
|
+
};
|
|
21
|
+
const _hoisted_6 = { class: "cn-readiness-checklist__list" };
|
|
22
|
+
const _hoisted_7 = ["data-testid"];
|
|
23
|
+
const _hoisted_8 = { class: "cn-readiness-checklist__body" };
|
|
24
|
+
const _hoisted_9 = ["href"];
|
|
25
|
+
const _hoisted_10 = {
|
|
26
|
+
key: 1,
|
|
27
|
+
class: "cn-readiness-checklist__label"
|
|
28
|
+
};
|
|
29
|
+
const _hoisted_11 = {
|
|
30
|
+
key: 2,
|
|
31
|
+
class: "cn-readiness-checklist__explanation"
|
|
32
|
+
};
|
|
33
|
+
const _hoisted_12 = {
|
|
34
|
+
key: 3,
|
|
35
|
+
class: "cn-readiness-checklist__failure"
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
39
|
+
const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
|
|
40
|
+
const _component_NcNoteCard = resolveComponent("NcNoteCard");
|
|
41
|
+
|
|
42
|
+
return (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
43
|
+
($props.description)
|
|
44
|
+
? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($props.description), 1 /* TEXT */))
|
|
45
|
+
: createCommentVNode("v-if", true),
|
|
46
|
+
($data.loading)
|
|
47
|
+
? (openBlock(), createBlock(_component_NcLoadingIcon, {
|
|
48
|
+
key: 1,
|
|
49
|
+
size: 32,
|
|
50
|
+
name: $options.t('nextcloud-vue', 'Loading')
|
|
51
|
+
}, null, 8 /* PROPS */, ["name"]))
|
|
52
|
+
: ($data.loadError)
|
|
53
|
+
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
54
|
+
key: 2,
|
|
55
|
+
type: "error",
|
|
56
|
+
"data-testid": "cn-readiness-checklist-error"
|
|
57
|
+
}, {
|
|
58
|
+
default: withCtx(() => [
|
|
59
|
+
createTextVNode(toDisplayString($data.loadError), 1 /* TEXT */)
|
|
60
|
+
]),
|
|
61
|
+
_: 1 /* STABLE */
|
|
62
|
+
}))
|
|
63
|
+
: (openBlock(), createElementBlock(Fragment, { key: 3 }, [
|
|
64
|
+
($props.blockingText && $options.blockingItems.length > 0)
|
|
65
|
+
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
66
|
+
key: 0,
|
|
67
|
+
type: "error",
|
|
68
|
+
"data-testid": "cn-readiness-checklist-blocking"
|
|
69
|
+
}, {
|
|
70
|
+
default: withCtx(() => [
|
|
71
|
+
createTextVNode(toDisplayString($props.blockingText) + " " + toDisplayString($options.blockingItems.map((item) => item.label).join(', ')), 1 /* TEXT */)
|
|
72
|
+
]),
|
|
73
|
+
_: 1 /* STABLE */
|
|
74
|
+
}))
|
|
75
|
+
: createCommentVNode("v-if", true),
|
|
76
|
+
($options.shownItems.length === 0)
|
|
77
|
+
? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString($props.emptyText || $options.t('nextcloud-vue', 'Nothing to check.')), 1 /* TEXT */))
|
|
78
|
+
: createCommentVNode("v-if", true),
|
|
79
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($options.shownGroups, (group) => {
|
|
80
|
+
return (openBlock(), createElementBlock("section", {
|
|
81
|
+
key: group.id,
|
|
82
|
+
class: "cn-readiness-checklist__group",
|
|
83
|
+
"data-testid": `cn-readiness-checklist-group-${group.id}`
|
|
84
|
+
}, [
|
|
85
|
+
(group.title)
|
|
86
|
+
? (openBlock(), createElementBlock("h4", _hoisted_5, toDisplayString(group.title), 1 /* TEXT */))
|
|
87
|
+
: createCommentVNode("v-if", true),
|
|
88
|
+
createElementVNode("ul", _hoisted_6, [
|
|
89
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (item) => {
|
|
90
|
+
return (openBlock(), createElementBlock("li", {
|
|
91
|
+
key: item.id,
|
|
92
|
+
class: "cn-readiness-checklist__item",
|
|
93
|
+
"data-testid": `cn-readiness-item-${item.id}`
|
|
94
|
+
}, [
|
|
95
|
+
createElementVNode("span", {
|
|
96
|
+
class: normalizeClass(["cn-readiness-checklist__state", `cn-readiness-checklist__state--${$options.stateOf(item)}`]),
|
|
97
|
+
"data-testid": "cn-readiness-item-state"
|
|
98
|
+
}, toDisplayString($options.stateLabel(item)), 3 /* TEXT, CLASS */),
|
|
99
|
+
createElementVNode("span", _hoisted_8, [
|
|
100
|
+
(item.fixHref)
|
|
101
|
+
? (openBlock(), createElementBlock("a", {
|
|
102
|
+
key: 0,
|
|
103
|
+
href: item.fixHref,
|
|
104
|
+
class: "cn-readiness-checklist__label",
|
|
105
|
+
"data-testid": "cn-readiness-item-fix"
|
|
106
|
+
}, toDisplayString(item.label), 9 /* TEXT, PROPS */, _hoisted_9))
|
|
107
|
+
: (openBlock(), createElementBlock("span", _hoisted_10, toDisplayString(item.label), 1 /* TEXT */)),
|
|
108
|
+
(item.explanation)
|
|
109
|
+
? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(item.explanation), 1 /* TEXT */))
|
|
110
|
+
: createCommentVNode("v-if", true),
|
|
111
|
+
(item.failure)
|
|
112
|
+
? (openBlock(), createElementBlock("span", _hoisted_12, toDisplayString($options.t('nextcloud-vue', 'This could not be read: {reason}', { reason: item.failure })), 1 /* TEXT */))
|
|
113
|
+
: createCommentVNode("v-if", true)
|
|
114
|
+
])
|
|
115
|
+
], 8 /* PROPS */, _hoisted_7))
|
|
116
|
+
}), 128 /* KEYED_FRAGMENT */))
|
|
117
|
+
])
|
|
118
|
+
], 8 /* PROPS */, _hoisted_4))
|
|
119
|
+
}), 128 /* KEYED_FRAGMENT */))
|
|
120
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
121
|
+
]))
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export { render };
|
|
125
|
+
//# sourceMappingURL=CnReadinessChecklist.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnReadinessChecklist.vue3.js","sources":["../../../../src/components/CnReadinessChecklist/CnReadinessChecklist.vue?vue&type=template&id=4e10a556&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-readiness-checklist\" data-testid=\"cn-readiness-checklist\">\n\t\t<p v-if=\"description\" class=\"cn-readiness-checklist__description\">\n\t\t\t{{ description }}\n\t\t</p>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\n\t\t<NcNoteCard v-else-if=\"loadError\" type=\"error\" data-testid=\"cn-readiness-checklist-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\n\t\t<template v-else>\n\t\t\t<NcNoteCard v-if=\"blockingText && blockingItems.length > 0\"\n\t\t\t\ttype=\"error\"\n\t\t\t\tdata-testid=\"cn-readiness-checklist-blocking\">\n\t\t\t\t{{ blockingText }} {{ blockingItems.map((item) => item.label).join(', ') }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"shownItems.length === 0\" class=\"cn-readiness-checklist__empty\" data-testid=\"cn-readiness-checklist-empty\">\n\t\t\t\t{{ emptyText || t('nextcloud-vue', 'Nothing to check.') }}\n\t\t\t</p>\n\n\t\t\t<section v-for=\"group in shownGroups\"\n\t\t\t\t:key=\"group.id\"\n\t\t\t\tclass=\"cn-readiness-checklist__group\"\n\t\t\t\t:data-testid=\"`cn-readiness-checklist-group-${group.id}`\">\n\t\t\t\t<h4 v-if=\"group.title\" class=\"cn-readiness-checklist__group-title\">\n\t\t\t\t\t{{ group.title }}\n\t\t\t\t</h4>\n\t\t\t\t<ul class=\"cn-readiness-checklist__list\">\n\t\t\t\t\t<li v-for=\"item in group.items\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-readiness-checklist__item\"\n\t\t\t\t\t\t:data-testid=\"`cn-readiness-item-${item.id}`\">\n\t\t\t\t\t\t<!-- The state is a word, not a colour (WCAG 1.4.1); the\n\t\t\t\t\t\t colour repeats it. -->\n\t\t\t\t\t\t<span class=\"cn-readiness-checklist__state\"\n\t\t\t\t\t\t\t:class=\"`cn-readiness-checklist__state--${stateOf(item)}`\"\n\t\t\t\t\t\t\tdata-testid=\"cn-readiness-item-state\">\n\t\t\t\t\t\t\t{{ stateLabel(item) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-readiness-checklist__body\">\n\t\t\t\t\t\t\t<a v-if=\"item.fixHref\"\n\t\t\t\t\t\t\t\t:href=\"item.fixHref\"\n\t\t\t\t\t\t\t\tclass=\"cn-readiness-checklist__label\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-readiness-item-fix\">{{ item.label }}</a>\n\t\t\t\t\t\t\t<span v-else class=\"cn-readiness-checklist__label\">{{ item.label }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.explanation\" class=\"cn-readiness-checklist__explanation\">{{ item.explanation }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.failure\" class=\"cn-readiness-checklist__failure\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'This could not be read: {reason}', { reason: item.failure }) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * The fields an item is read from by default.\n *\n * @type {Readonly<Record<string, string>>}\n */\nconst DEFAULT_FIELDS = Object.freeze({\n\tid: 'id',\n\tlabel: 'label',\n\texplanation: 'explanation',\n\tmet: 'met',\n\tfixHref: 'fixHref',\n\tfailure: 'failure',\n\tgroup: 'group',\n\tblocking: 'blocking',\n})\n\n/**\n * CnReadinessChecklist: a list of things an instance needs, each with its\n * state in words (met, open, unknown), an explanation and a link to the\n * screen that fixes it. Reported, never gated: nothing here blocks the app.\n *\n * Items come from `items`, or are read from an app endpoint (`source`, an app\n * path resolved with `generateUrl`, the list at `itemsPath`) and re-read when\n * the reader comes back to the tab. `fields` maps an item's keys onto the\n * row's (`{ label: 'title', met: 'done', fixHref: 'screen' }`). Items carrying\n * a `group` render under that group's heading (`groups`, in order).\n *\n * @spec openspec/changes/readiness-checklist/specs/readiness-checklist/spec.md\n */\nexport default {\n\tname: 'CnReadinessChecklist',\n\n\tcomponents: { NcLoadingIcon, NcNoteCard },\n\n\tinject: {\n\t\t/** The host app's translate, for texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The items: `{ id, label, explanation?, met (true|false|null), fixHref?, failure?, group?, blocking? }`. */\n\t\titems: { type: Array, default: () => [] },\n\t\t/** An app path to read the items from (`/apps/<app>/api/setup/status`). */\n\t\tsource: { type: String, default: '' },\n\t\t/** Where the items sit in the answer. */\n\t\titemsPath: { type: String, default: 'items' },\n\t\t/** Item key to row key, over the defaults. */\n\t\tfields: { type: Object, default: () => ({}) },\n\t\t/** Headings for grouped items, in order: `[{ id, title }]`. */\n\t\tgroups: { type: Array, default: () => [] },\n\t\t/** A line above the list. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** The words for each state: `{ met, unmet, unknown }`. */\n\t\tstatusLabels: { type: Object, default: () => ({}) },\n\t\t/** Said when there is nothing to check. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** Said when the source could not be read. */\n\t\terrorText: { type: String, default: '' },\n\t\t/** Said before the unmet items marked `blocking` (\"Cannot run until this is installed:\"). */\n\t\tblockingText: { type: String, default: '' },\n\t},\n\n\temits: ['loaded'],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: false,\n\t\t\tloadError: '',\n\t\t\tloadedItems: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {Array<object>} The items, normalised. */\n\t\tshownItems() {\n\t\t\tconst raw = this.loadedItems !== null ? this.loadedItems : this.items\n\t\t\tconst map = { ...DEFAULT_FIELDS, ...(this.fields || {}) }\n\t\t\treturn (Array.isArray(raw) ? raw : []).map((row, index) => {\n\t\t\t\tconst read = (key) => readPath(row, map[key])\n\t\t\t\tconst met = read('met')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(read('id') ?? index),\n\t\t\t\t\tlabel: this.text(read('label') ?? read('id') ?? ''),\n\t\t\t\t\texplanation: this.text(read('explanation') ?? ''),\n\t\t\t\t\tmet: met === true || met === false ? met : null,\n\t\t\t\t\tfixHref: typeof read('fixHref') === 'string' ? read('fixHref') : '',\n\t\t\t\t\tfailure: read('failure') ? String(read('failure')) : '',\n\t\t\t\t\tgroup: read('group') ? String(read('group')) : '',\n\t\t\t\t\tblocking: read('blocking') === true,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The unmet blocking items. */\n\t\tblockingItems() {\n\t\t\treturn this.shownItems.filter((item) => item.blocking && item.met !== true)\n\t\t},\n\n\t\t/** @return {Array<{id: string, title: string, items: Array<object>}>} The groups that hold items, in order. */\n\t\tshownGroups() {\n\t\t\tconst declared = (Array.isArray(this.groups) ? this.groups : []).map((group) => ({\n\t\t\t\tid: String(group.id),\n\t\t\t\ttitle: this.text(group.title || ''),\n\t\t\t\titems: this.shownItems.filter((item) => item.group === String(group.id)),\n\t\t\t}))\n\t\t\tconst known = new Set(declared.map((group) => group.id))\n\t\t\tconst rest = this.shownItems.filter((item) => !known.has(item.group))\n\t\t\tconst all = rest.length > 0 ? [{ id: 'items', title: '', items: rest }, ...declared] : declared\n\t\t\treturn all.filter((group) => group.items.length > 0)\n\t\t},\n\t},\n\n\twatch: {\n\t\tsource() {\n\t\t\tthis.reload()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.reload()\n\t\twindow.addEventListener('focus', this.onFocus)\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('focus', this.onFocus)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A declared text through the host's translate.\n\t\t *\n\t\t * @param {string} value The text.\n\t\t * @return {string} The translated text.\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 * @param {object} item The item.\n\t\t * @return {string} `met`, `unmet` or `unknown`.\n\t\t */\n\t\tstateOf(item) {\n\t\t\tif (item.met === true) {\n\t\t\t\treturn 'met'\n\t\t\t}\n\t\t\treturn item.met === false ? 'unmet' : 'unknown'\n\t\t},\n\n\t\t/**\n\t\t * The word for an item's state.\n\t\t *\n\t\t * @param {object} item The item.\n\t\t * @return {string} The word.\n\t\t */\n\t\tstateLabel(item) {\n\t\t\tconst state = this.stateOf(item)\n\t\t\tconst own = this.statusLabels && this.statusLabels[state]\n\t\t\tif (own) {\n\t\t\t\treturn this.text(own)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmet: t('nextcloud-vue', 'Done'),\n\t\t\t\tunmet: t('nextcloud-vue', 'Still open'),\n\t\t\t\tunknown: t('nextcloud-vue', 'Unknown'),\n\t\t\t}[state]\n\t\t},\n\n\t\t/**\n\t\t * Read the items again, from the source. The source asks the instance\n\t\t * rather than a stored flag, so a fix made elsewhere shows on return.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync reload() {\n\t\t\tif (!this.source) {\n\t\t\t\tthis.loadedItems = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = this.loadedItems === null\n\t\t\tthis.loadError = ''\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(generateUrl(this.source))\n\t\t\t\tconst rows = Array.isArray(data) ? data : readPath(data, this.itemsPath)\n\t\t\t\tthis.loadedItems = Array.isArray(rows) ? rows : []\n\t\t\t\t/** @event loaded The source answered. Payload: the whole answer. */\n\t\t\t\tthis.$emit('loaded', data)\n\t\t\t} catch {\n\t\t\t\tthis.loadedItems = []\n\t\t\t\tthis.loadError = this.errorText ? this.text(this.errorText) : t('nextcloud-vue', 'The checklist could not be read.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/** Re-read when the reader comes back from fixing something. */\n\t\tonFocus() {\n\t\t\tif (this.source) {\n\t\t\t\tthis.reload()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-readiness-checklist__description,\n.cn-readiness-checklist__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0 0 12px;\n}\n\n.cn-readiness-checklist__group-title {\n\tmargin: 12px 0 4px;\n}\n\n.cn-readiness-checklist__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-readiness-checklist__item {\n\tdisplay: flex;\n\tgap: calc(var(--default-grid-baseline, 4px) * 3);\n\tpadding-block: calc(var(--default-grid-baseline, 4px) * 2);\n\tborder-block-end: 1px solid var(--color-border);\n}\n\n.cn-readiness-checklist__state {\n\tflex: 0 0 auto;\n\tmin-width: 6em;\n\tfont-weight: bold;\n}\n\n.cn-readiness-checklist__state--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-readiness-checklist__state--unmet {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-readiness-checklist__state--unknown {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-readiness-checklist__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-readiness-checklist__explanation {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-readiness-checklist__failure {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_createBlock","_createTextVNode","_openBlock","_createElementVNode","_Fragment","_renderList","_normalizeClass"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import script from './CnSchemaRulesWidget.vue2.js';
|
|
2
|
+
import { render } from './CnSchemaRulesWidget.vue3.js';
|
|
3
|
+
|
|
4
|
+
script.render = render;
|
|
5
|
+
script.__scopeId = "data-v-1d1c1c13";
|
|
6
|
+
const __sfc_main = script;
|
|
7
|
+
|
|
8
|
+
export { __sfc_main as default };
|
|
9
|
+
//# sourceMappingURL=CnSchemaRulesWidget.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnSchemaRulesWidget.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|