@conduction/nextcloud-vue 2.75.0 → 2.76.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.
Files changed (67) hide show
  1. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +6 -0
  2. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  3. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -2
  4. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  5. package/dist/esm/components/CnCredentials/CnCredentials.vue2.js +11 -10
  6. package/dist/esm/components/CnCredentials/CnCredentials.vue2.js.map +1 -1
  7. package/dist/esm/components/CnCredentials/CnCredentials.vue3.js.map +1 -1
  8. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +8 -5
  9. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  10. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  11. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +144 -6
  12. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  13. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +12 -11
  14. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  15. package/dist/esm/components/CnIndexPage/selfModeActions.js +90 -14
  16. package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
  17. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +15 -3
  18. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  19. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +26 -0
  20. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  21. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +26 -9
  22. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  23. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +19 -3
  24. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  25. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  26. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +153 -38
  27. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
  28. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +74 -56
  29. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
  30. package/dist/esm/composables/useObjectLock.js +41 -10
  31. package/dist/esm/composables/useObjectLock.js.map +1 -1
  32. package/dist/esm/index.js +1 -0
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/l10n/en.json.js +7 -1
  35. package/dist/esm/l10n/en.json.js.map +1 -1
  36. package/dist/esm/l10n/nl.json.js +8 -2
  37. package/dist/esm/l10n/nl.json.js.map +1 -1
  38. package/dist/esm/nextcloud-vue.css +88 -1
  39. package/dist/esm/store/useObjectStore.js +37 -9
  40. package/dist/esm/store/useObjectStore.js.map +1 -1
  41. package/dist/esm/utils/objectSignals.js +44 -0
  42. package/dist/esm/utils/objectSignals.js.map +1 -0
  43. package/dist/nextcloud-vue.cjs.js +871 -328
  44. package/dist/nextcloud-vue.cjs.js.map +2 -2
  45. package/dist/nextcloud-vue.css +88 -1
  46. package/l10n/en.json +7 -1
  47. package/l10n/nl.json +8 -2
  48. package/package.json +1 -1
  49. package/src/components/CnCardGrid/CnCardGrid.md +1 -0
  50. package/src/components/CnCardGrid/CnCardGrid.vue +7 -0
  51. package/src/components/CnCredentials/CnCredentials.vue +17 -16
  52. package/src/components/CnDetailPage/CnDetailPage.vue +8 -5
  53. package/src/components/CnIndexPage/CnIndexPage.md +15 -0
  54. package/src/components/CnIndexPage/CnIndexPage.vue +155 -16
  55. package/src/components/CnIndexPage/selfModeActions.js +89 -13
  56. package/src/components/CnIndexPage/useSelfFetchList.js +15 -3
  57. package/src/components/CnObjectCard/CnObjectCard.md +1 -0
  58. package/src/components/CnObjectCard/CnObjectCard.vue +89 -1
  59. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +19 -3
  60. package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +181 -38
  61. package/src/composables/__tests__/useObjectLock.endpoints.spec.js +83 -0
  62. package/src/composables/useObjectLock.js +41 -10
  63. package/src/css/look-board-index.css +35 -1
  64. package/src/index.js +1 -0
  65. package/src/store/useObjectStore.js +37 -9
  66. package/src/utils/index.js +1 -0
  67. package/src/utils/objectSignals.js +41 -0
@@ -1 +1 @@
1
- {"version":3,"file":"useObjectLock.js","sources":["../../../src/composables/useObjectLock.js"],"sourcesContent":["// SPDX-License-Identifier: EUPL-1.2\n// Copyright (C) 2026 Conduction B.V.\n\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { generateUrl } from '@nextcloud/router'\nimport { tryOnScopeDispose } from '@vueuse/core'\nimport { computed, onBeforeUnmount, onMounted, unref } from 'vue'\n\n/**\n * Thrown when an `acquire()` POST returns 409 (Conflict) or 423\n * (Locked). Carries the conflicting user info so the UI can render\n * a \"Locked by X\" banner without a second fetch.\n */\nexport class LockConflictError extends Error {\n\t/**\n\t * @param {string} message Human-readable description.\n\t * @param {object|null} info Lock metadata (`{ user, displayName, expiresAt }`) parsed from the response.\n\t */\n\tconstructor(message, info = null) {\n\t\tsuper(message)\n\t\tthis.name = 'LockConflictError'\n\t\tthis.lockedBy = info?.user ?? info?.displayName ?? null\n\t\tthis.expiresAt = info?.expiresAt ?? null\n\t}\n}\n\n/**\n * Thrown when an `acquire()` / `release()` call returns 401 / 403.\n * Consumers may fall back to \"edit without lock\" UX.\n */\nexport class PermissionError extends Error {\n\tconstructor(message) {\n\t\tsuper(message)\n\t\tthis.name = 'PermissionError'\n\t}\n}\n\n/**\n * useObjectLock — manage the OR pessimistic lock for a single object.\n *\n * Reactive lock state is **read from the object cache** populated by\n * `liveUpdatesPlugin`, NOT from a polling lock endpoint. This means\n * remote lock acquisitions are visible without explicit refetch as\n * long as the consumer also uses `useObjectSubscription` (or any\n * other path that keeps `objectStore.objects[type][id]` fresh).\n *\n * @param {object} objectStore Pinia store instance (`useObjectStore()` result).\n * @param {string|import('vue').Ref<string>} register OR register slug.\n * @param {string|import('vue').Ref<string>} schema OR schema slug (also the GraphQL field name).\n * @param {string|import('vue').Ref<string>} id Object UUID.\n * @param {object} [options] Optional config.\n * @param {boolean} [options.autoRenew] Renew the lock on a fixed interval while editing + visible.\n * @param {number} [options.renewIntervalMs] Renew every N ms (default 10 min).\n * @param {number} [options.lockDurationSec] Server-side TTL requested on acquire (default 30 min).\n * @param {string|import('vue').Ref<string>} [options.schemaSlug] The schema slug for the LOCK URL, when `schema` is the object-cache key rather than the slug (CnDetailPage passes `<register>-<schema>` as the key). Defaults to `schema`.\n * @return {{\n * locked: import('vue').ComputedRef<boolean>,\n * lockedByMe: import('vue').ComputedRef<boolean>,\n * lockedBy: import('vue').ComputedRef<string|null>,\n * expiresAt: import('vue').ComputedRef<Date|null>,\n * acquire: () => Promise<void>,\n * release: () => Promise<void>\n * }} Reactive lock state + actions.\n */\nexport function useObjectLock(objectStore, register, schema, id, options = {}) {\n\tconst autoRenew = options.autoRenew !== false\n\tconst renewIntervalMs = options.renewIntervalMs ?? 600000\n\tconst lockDurationSec = options.lockDurationSec ?? 1800\n\n\t// Use native document.visibilityState directly to avoid pinning a\n\t// minimum @vueuse/core version (`useDocumentVisibility` landed in a\n\t// later cut and isn't available everywhere).\n\tconst isVisible = () => typeof document === 'undefined' || document.visibilityState !== 'hidden'\n\tlet renewTimer = null\n\n\tfunction readType() {\n\t\treturn unref(schema)\n\t}\n\tfunction readId() {\n\t\treturn unref(id)\n\t}\n\tfunction readRegister() {\n\t\treturn unref(register)\n\t}\n\n\tfunction readSelfLock() {\n\t\tconst t = readType()\n\t\tconst i = readId()\n\t\tif (!t || !i) {\n\t\t\treturn null\n\t\t}\n\t\tconst obj = objectStore.objects?.[t]?.[i]\n\t\tconst self = obj?.['@self'] ?? obj\n\t\treturn self?.locked ?? null\n\t}\n\n\tconst locked = computed(() => {\n\t\tconst l = readSelfLock()\n\t\tif (!l) {\n\t\t\treturn false\n\t\t}\n\t\tif (l.expiresAt) {\n\t\t\tconst exp = new Date(l.expiresAt).getTime()\n\t\t\tif (Number.isFinite(exp) && exp <= Date.now()) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\n\tconst lockedBy = computed(() => readSelfLock()?.user ?? readSelfLock()?.displayName ?? null)\n\n\tconst lockedByMe = computed(() => {\n\t\tconst me = getCurrentUser()?.uid\n\t\tconst lb = readSelfLock()?.user\n\t\treturn Boolean(me && lb && me === lb)\n\t})\n\n\tconst expiresAt = computed(() => {\n\t\tconst raw = readSelfLock()?.expiresAt\n\t\tif (!raw) {\n\t\t\treturn null\n\t\t}\n\t\tconst d = new Date(raw)\n\t\treturn Number.isNaN(d.getTime()) ? null : d\n\t})\n\n\tfunction readSchemaSlug() {\n\t\treturn unref(options.schemaSlug ?? schema)\n\t}\n\n\t/**\n\t * The object's own URL, without the lock verb.\n\t *\n\t * 🔴 THE SLUG AND THE STORE KEY ARE NOT THE SAME STRING, AND THEY USED TO\n\t * BE. `schema` doubles as the key the object cache is read under, and\n\t * CnDetailPage passes it `objectType`, which is `<register>-<schema>`. Put\n\t * straight into the URL that produced\n\t * `/api/objects/dossiq/dossiq-case/<id>/lock`, a schema slug no register\n\t * has, so the acquire 404ed on every manifest-driven detail page and the\n\t * lock was never taken. `options.schemaSlug` is the URL half and defaults\n\t * to `schema`, so a caller that passes one string for both is unaffected.\n\t *\n\t * @return {string} The object's base url.\n\t */\n\tfunction objectUrl() {\n\t\tconst r = readRegister()\n\t\tconst s = readSchemaSlug()\n\t\tconst i = readId()\n\t\treturn generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(r)}/${encodeURIComponent(s)}/${encodeURIComponent(i)}`)\n\t}\n\n\tfunction endpoint() {\n\t\treturn `${objectUrl()}/lock`\n\t}\n\n\t/**\n\t * Where a lock is handed back.\n\t *\n\t * 🔴 IT IS A POST TO `/unlock`, AND IT USED TO BE `DELETE /lock`.\n\t * OpenRegister declares exactly two lock routes, `objects#lock` and\n\t * `objects#unlock`, both POST (`appinfo/routes.php`, verified 2026-09-18\n\t * against `development`); there is no DELETE on `/lock` and there never\n\t * was. So every release 404ed, and `release()` reads a 404 as \"already\n\t * released; idempotent\" and returns without a word. The lock outlived the\n\t * editor, the holder was told nothing, and the next person was refused by a\n\t * lock whose owner had closed the page twenty minutes earlier. That is the\n\t * silent no-op this line ends, and it is why the release test asserts the\n\t * URL and the METHOD rather than only that the promise resolved.\n\t *\n\t * @return {string} The unlock url.\n\t */\n\tfunction unlockEndpoint() {\n\t\treturn `${objectUrl()}/unlock`\n\t}\n\n\tasync function acquire() {\n\t\ttry {\n\t\t\tawait axios.post(endpoint(), { duration: lockDurationSec })\n\t\t} catch (e) {\n\t\t\tconst status = e?.response?.status\n\t\t\tconst data = e?.response?.data\n\t\t\tif (status === 409 || status === 423) {\n\t\t\t\tthrow new LockConflictError(\n\t\t\t\t\tdata?.message || 'Object is locked by another user',\n\t\t\t\t\tdata?.lock || data,\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (status === 401 || status === 403) {\n\t\t\t\tthrow new PermissionError(data?.message || 'No permission to lock this object')\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t\t// Refresh the cached object so `@self.locked` is populated;\n\t\t// liveUpdatesPlugin will also fire a refetch on the\n\t\t// or-object-{uuid} event, but doing it here avoids a render\n\t\t// gap between acquire() resolution and the push frame.\n\t\ttry {\n\t\t\tawait objectStore.fetchObject(readType(), readId())\n\t\t} catch {\n\t\t\t// swallow — the lock is held; the cache will catch up\n\t\t}\n\t\tif (autoRenew) {\n\t\t\tstartRenewTimer()\n\t\t}\n\t}\n\n\tasync function release() {\n\t\tstopRenewTimer()\n\t\ttry {\n\t\t\tawait axios.post(unlockEndpoint())\n\t\t} catch (e) {\n\t\t\tconst status = e?.response?.status\n\t\t\tif (status === 404) {\n\t\t\t\treturn\n\t\t\t} // already released; idempotent\n\t\t\tif (status === 401 || status === 403) {\n\t\t\t\tthrow new PermissionError(e?.response?.data?.message || 'No permission to release this lock')\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t\ttry {\n\t\t\tawait objectStore.fetchObject(readType(), readId())\n\t\t} catch { /* ignore */ }\n\t}\n\n\tfunction startRenewTimer() {\n\t\tstopRenewTimer()\n\t\tif (!autoRenew) {\n\t\t\treturn\n\t\t}\n\t\trenewTimer = setInterval(() => {\n\t\t\tif (!isVisible()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!lockedByMe.value) {\n\t\t\t\tstopRenewTimer()\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Re-issue acquire; idempotent on the server (resets TTL).\n\t\t\tacquire().catch(() => stopRenewTimer())\n\t\t}, renewIntervalMs)\n\t}\n\n\tfunction stopRenewTimer() {\n\t\tif (renewTimer) {\n\t\t\tclearInterval(renewTimer)\n\t\t\trenewTimer = null\n\t\t}\n\t}\n\n\tfunction beaconRelease() {\n\t\tif (!lockedByMe.value) {\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\t// A beacon is always a POST, which is what `/unlock` takes: the\n\t\t\t// old `?_method=DELETE` was a query parameter on a URL that had no\n\t\t\t// DELETE route to reach in the first place.\n\t\t\tnavigator.sendBeacon?.(unlockEndpoint())\n\t\t} catch { /* best-effort */ }\n\t}\n\n\tonMounted(() => {\n\t\twindow.addEventListener('beforeunload', beaconRelease)\n\t})\n\n\tonBeforeUnmount(() => {\n\t\twindow.removeEventListener('beforeunload', beaconRelease)\n\t})\n\n\ttryOnScopeDispose(() => {\n\t\tstopRenewTimer()\n\t\tif (lockedByMe.value) {\n\t\t\t// Fire-and-forget; the Vue scope is gone, no point awaiting.\n\t\t\trelease().catch(() => {})\n\t\t}\n\t})\n\n\treturn { locked, lockedByMe, lockedBy, expiresAt, acquire, release }\n}\n"],"names":["axios"],"mappings":"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"}
1
+ {"version":3,"file":"useObjectLock.js","sources":["../../../src/composables/useObjectLock.js"],"sourcesContent":["// SPDX-License-Identifier: EUPL-1.2\n// Copyright (C) 2026 Conduction B.V.\n\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { generateUrl } from '@nextcloud/router'\nimport { tryOnScopeDispose } from '@vueuse/core'\nimport { computed, onBeforeUnmount, onMounted, toValue } from 'vue'\n\n/**\n * Thrown when an `acquire()` POST returns 409 (Conflict) or 423\n * (Locked). Carries the conflicting user info so the UI can render\n * a \"Locked by X\" banner without a second fetch.\n */\nexport class LockConflictError extends Error {\n\t/**\n\t * @param {string} message Human-readable description.\n\t * @param {object|null} info Lock metadata (`{ user, displayName, expiresAt }`) parsed from the response.\n\t */\n\tconstructor(message, info = null) {\n\t\tsuper(message)\n\t\tthis.name = 'LockConflictError'\n\t\tthis.lockedBy = info?.user ?? info?.displayName ?? null\n\t\tthis.expiresAt = info?.expiresAt ?? null\n\t}\n}\n\n/**\n * Thrown when an `acquire()` / `release()` call returns 401 / 403.\n * Consumers may fall back to \"edit without lock\" UX.\n */\nexport class PermissionError extends Error {\n\tconstructor(message) {\n\t\tsuper(message)\n\t\tthis.name = 'PermissionError'\n\t}\n}\n\n/**\n * useObjectLock — manage the OR pessimistic lock for a single object.\n *\n * Reactive lock state is **read from the object cache** populated by\n * `liveUpdatesPlugin`, NOT from a polling lock endpoint. This means\n * remote lock acquisitions are visible without explicit refetch as\n * long as the consumer also uses `useObjectSubscription` (or any\n * other path that keeps `objectStore.objects[type][id]` fresh).\n *\n * @param {object} objectStore Pinia store instance (`useObjectStore()` result).\n * @param {string|import('vue').Ref<string>|function(): string} register OR register slug.\n * @param {string|import('vue').Ref<string>|function(): string} schema OR schema slug (also the GraphQL field name).\n * @param {string|import('vue').Ref<string>|function(): string} id Object UUID.\n * @param {object} [options] Optional config.\n * @param {boolean} [options.autoRenew] Renew the lock on a fixed interval while editing + visible.\n * @param {number} [options.renewIntervalMs] Renew every N ms (default 10 min).\n * @param {number} [options.lockDurationSec] Server-side TTL requested on acquire (default 30 min).\n * @param {string|import('vue').Ref<string>|function(): string} [options.schemaSlug] The schema slug for the LOCK URL, when `schema` is the object-cache key rather than the slug (CnDetailPage passes `<register>-<schema>` as the key). Defaults to `schema`.\n * @return {{\n * locked: import('vue').ComputedRef<boolean>,\n * lockedByMe: import('vue').ComputedRef<boolean>,\n * lockedBy: import('vue').ComputedRef<string|null>,\n * expiresAt: import('vue').ComputedRef<Date|null>,\n * acquire: () => Promise<void>,\n * release: () => Promise<void>\n * }} Reactive lock state + actions.\n * @spec openspec/changes/r4-object-lock-url-and-credentials-copy/specs/object-lock/spec.md\n */\nexport function useObjectLock(objectStore, register, schema, id, options = {}) {\n\tconst autoRenew = options.autoRenew !== false\n\tconst renewIntervalMs = options.renewIntervalMs ?? 600000\n\tconst lockDurationSec = options.lockDurationSec ?? 1800\n\n\t// Use native document.visibilityState directly to avoid pinning a\n\t// minimum @vueuse/core version (`useDocumentVisibility` landed in a\n\t// later cut and isn't available everywhere).\n\tconst isVisible = () => typeof document === 'undefined' || document.visibilityState !== 'hidden'\n\tlet renewTimer = null\n\n\t/*\n\t * 🔴 `toValue`, NOT `unref`. CnDetailPage passes GETTER FUNCTIONS\n\t * (`() => props.objectId`) so the lock follows the route. `unref` returns a\n\t * function untouched, so the getter's SOURCE was interpolated into the URL:\n\t * every detail page sent `/api/objects/()=>pn/()=>Ct.schema||Bn/()=>Ct.objectId/lock`\n\t * and got a 404, and the cache lookup used the function as its key, so the\n\t * banner never saw a lock either. `toValue` reads a plain value, a ref and a\n\t * getter alike.\n\t */\n\tfunction readType() {\n\t\treturn toValue(schema)\n\t}\n\tfunction readId() {\n\t\treturn toValue(id)\n\t}\n\tfunction readRegister() {\n\t\treturn toValue(register)\n\t}\n\n\tfunction readSelfLock() {\n\t\tconst t = readType()\n\t\tconst i = readId()\n\t\tif (!t || !i) {\n\t\t\treturn null\n\t\t}\n\t\tconst obj = objectStore.objects?.[t]?.[i]\n\t\tconst self = obj?.['@self'] ?? obj\n\t\treturn self?.locked ?? null\n\t}\n\n\tconst locked = computed(() => {\n\t\tconst l = readSelfLock()\n\t\tif (!l) {\n\t\t\treturn false\n\t\t}\n\t\tif (l.expiresAt) {\n\t\t\tconst exp = new Date(l.expiresAt).getTime()\n\t\t\tif (Number.isFinite(exp) && exp <= Date.now()) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\n\tconst lockedBy = computed(() => readSelfLock()?.user ?? readSelfLock()?.displayName ?? null)\n\n\tconst lockedByMe = computed(() => {\n\t\tconst me = getCurrentUser()?.uid\n\t\tconst lb = readSelfLock()?.user\n\t\treturn Boolean(me && lb && me === lb)\n\t})\n\n\tconst expiresAt = computed(() => {\n\t\tconst raw = readSelfLock()?.expiresAt\n\t\tif (!raw) {\n\t\t\treturn null\n\t\t}\n\t\tconst d = new Date(raw)\n\t\treturn Number.isNaN(d.getTime()) ? null : d\n\t})\n\n\tfunction readSchemaSlug() {\n\t\treturn toValue(options.schemaSlug ?? schema)\n\t}\n\n\t/**\n\t * Whether register, schema slug and object id are all known.\n\t *\n\t * A detail page mounts before its route params or object have resolved.\n\t * Without this a lock call would go out with `undefined` in the path, a\n\t * request OpenRegister can only answer with a 404.\n\t *\n\t * @spec openspec/changes/r4-object-lock-url-and-credentials-copy/specs/object-lock/spec.md\n\t * @return {boolean} True when a lock URL can be built.\n\t */\n\tfunction hasTarget() {\n\t\treturn [readRegister(), readSchemaSlug(), readId()]\n\t\t\t.every((part) => typeof part === 'string' ? part !== '' : typeof part === 'number')\n\t}\n\n\t/**\n\t * The object's own URL, without the lock verb.\n\t *\n\t * 🔴 THE SLUG AND THE STORE KEY ARE NOT THE SAME STRING, AND THEY USED TO\n\t * BE. `schema` doubles as the key the object cache is read under, and\n\t * CnDetailPage passes it `objectType`, which is `<register>-<schema>`. Put\n\t * straight into the URL that produced\n\t * `/api/objects/dossiq/dossiq-case/<id>/lock`, a schema slug no register\n\t * has, so the acquire 404ed on every manifest-driven detail page and the\n\t * lock was never taken. `options.schemaSlug` is the URL half and defaults\n\t * to `schema`, so a caller that passes one string for both is unaffected.\n\t *\n\t * @return {string} The object's base url.\n\t */\n\tfunction objectUrl() {\n\t\tconst r = readRegister()\n\t\tconst s = readSchemaSlug()\n\t\tconst i = readId()\n\t\treturn generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(r)}/${encodeURIComponent(s)}/${encodeURIComponent(i)}`)\n\t}\n\n\tfunction endpoint() {\n\t\treturn `${objectUrl()}/lock`\n\t}\n\n\t/**\n\t * Where a lock is handed back.\n\t *\n\t * 🔴 IT IS A POST TO `/unlock`, AND IT USED TO BE `DELETE /lock`.\n\t * OpenRegister declares exactly two lock routes, `objects#lock` and\n\t * `objects#unlock`, both POST (`appinfo/routes.php`, verified 2026-09-18\n\t * against `development`); there is no DELETE on `/lock` and there never\n\t * was. So every release 404ed, and `release()` reads a 404 as \"already\n\t * released; idempotent\" and returns without a word. The lock outlived the\n\t * editor, the holder was told nothing, and the next person was refused by a\n\t * lock whose owner had closed the page twenty minutes earlier. That is the\n\t * silent no-op this line ends, and it is why the release test asserts the\n\t * URL and the METHOD rather than only that the promise resolved.\n\t *\n\t * @return {string} The unlock url.\n\t */\n\tfunction unlockEndpoint() {\n\t\treturn `${objectUrl()}/unlock`\n\t}\n\n\tasync function acquire() {\n\t\tif (!hasTarget()) {\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tawait axios.post(endpoint(), { duration: lockDurationSec })\n\t\t} catch (e) {\n\t\t\tconst status = e?.response?.status\n\t\t\tconst data = e?.response?.data\n\t\t\tif (status === 409 || status === 423) {\n\t\t\t\tthrow new LockConflictError(\n\t\t\t\t\tdata?.message || 'Object is locked by another user',\n\t\t\t\t\tdata?.lock || data,\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (status === 401 || status === 403) {\n\t\t\t\tthrow new PermissionError(data?.message || 'No permission to lock this object')\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t\t// Refresh the cached object so `@self.locked` is populated;\n\t\t// liveUpdatesPlugin will also fire a refetch on the\n\t\t// or-object-{uuid} event, but doing it here avoids a render\n\t\t// gap between acquire() resolution and the push frame.\n\t\ttry {\n\t\t\tawait objectStore.fetchObject(readType(), readId())\n\t\t} catch {\n\t\t\t// swallow — the lock is held; the cache will catch up\n\t\t}\n\t\tif (autoRenew) {\n\t\t\tstartRenewTimer()\n\t\t}\n\t}\n\n\tasync function release() {\n\t\tstopRenewTimer()\n\t\tif (!hasTarget()) {\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tawait axios.post(unlockEndpoint())\n\t\t} catch (e) {\n\t\t\tconst status = e?.response?.status\n\t\t\tif (status === 404) {\n\t\t\t\treturn\n\t\t\t} // already released; idempotent\n\t\t\tif (status === 401 || status === 403) {\n\t\t\t\tthrow new PermissionError(e?.response?.data?.message || 'No permission to release this lock')\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t\ttry {\n\t\t\tawait objectStore.fetchObject(readType(), readId())\n\t\t} catch { /* ignore */ }\n\t}\n\n\tfunction startRenewTimer() {\n\t\tstopRenewTimer()\n\t\tif (!autoRenew) {\n\t\t\treturn\n\t\t}\n\t\trenewTimer = setInterval(() => {\n\t\t\tif (!isVisible()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!lockedByMe.value) {\n\t\t\t\tstopRenewTimer()\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Re-issue acquire; idempotent on the server (resets TTL).\n\t\t\tacquire().catch(() => stopRenewTimer())\n\t\t}, renewIntervalMs)\n\t}\n\n\tfunction stopRenewTimer() {\n\t\tif (renewTimer) {\n\t\t\tclearInterval(renewTimer)\n\t\t\trenewTimer = null\n\t\t}\n\t}\n\n\tfunction beaconRelease() {\n\t\tif (!lockedByMe.value || !hasTarget()) {\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\t// A beacon is always a POST, which is what `/unlock` takes: the\n\t\t\t// old `?_method=DELETE` was a query parameter on a URL that had no\n\t\t\t// DELETE route to reach in the first place.\n\t\t\tnavigator.sendBeacon?.(unlockEndpoint())\n\t\t} catch { /* best-effort */ }\n\t}\n\n\tonMounted(() => {\n\t\twindow.addEventListener('beforeunload', beaconRelease)\n\t})\n\n\tonBeforeUnmount(() => {\n\t\twindow.removeEventListener('beforeunload', beaconRelease)\n\t})\n\n\ttryOnScopeDispose(() => {\n\t\tstopRenewTimer()\n\t\tif (lockedByMe.value) {\n\t\t\t// Fire-and-forget; the Vue scope is gone, no point awaiting.\n\t\t\trelease().catch(() => {})\n\t\t}\n\t})\n\n\treturn { locked, lockedByMe, lockedBy, expiresAt, acquire, release }\n}\n"],"names":["axios"],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -27,6 +27,7 @@ export { BSN_ERROR_CHECKSUM, BSN_ERROR_LENGTH, maskBsn, validateBsn } from './ut
27
27
  export { registerTranslations } from './l10n/index.js';
28
28
  export { CnHttpError, cnFetch, cnFetchJson, configureCnFetch } from './utils/cnFetch.js';
29
29
  export { isLockedByCurrentUser, isObjectLocked, lockHolder, readLockPayload, resolveObjectLock } from './utils/objectLock.js';
30
+ export { dispatchObjectsChanged } from './utils/objectSignals.js';
30
31
  export { validateManifest, validateManifestV2 } from './utils/validateManifest.js';
31
32
  export { clearResolveCache, resolveManifestSentinels } from './utils/resolveManifestSentinels.js';
32
33
  export { clearRouteSentinelWarnings, resolveRouteSentinels } from './utils/resolveRouteSentinels.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,4 +1,9 @@
1
1
  var translations = {
2
+ "Linked items": "Linked items",
3
+ "Still loading: {sections}": "Still loading: {sections}",
4
+ "Could not load {section}.": "Could not load {section}.",
5
+ "Organisation credentials let apps act for your organisation. Keepiq, the credential vault of Nextcloud, keeps the secret. The app never holds it. You choose which apps may use each credential.": "Organisation credentials let apps act for your organisation. Keepiq, the credential vault of Nextcloud, keeps the secret. The app never holds it. You choose which apps may use each credential.",
6
+ "Apps sometimes act for you on an external service. You give the secret to Nextcloud once, and Keepiq keeps it in an encrypted vault. Apps make the call through Keepiq and never see the secret. You choose which apps may use each credential. Share one across apps, or keep one per app.": "Apps sometimes act for you on an external service. You give the secret to Nextcloud once, and Keepiq keeps it in an encrypted vault. Apps make the call through Keepiq and never see the secret. You choose which apps may use each credential. Share one across apps, or keep one per app.",
2
7
  "Show as": "Show as",
3
8
  Board: "Board",
4
9
  Calendar: "Calendar",
@@ -1283,7 +1288,8 @@ var translations = {
1283
1288
  "Development environment": "Development environment",
1284
1289
  "Test environment": "Test environment",
1285
1290
  "Acceptance environment": "Acceptance environment",
1286
- "About {label}": "About {label}"
1291
+ "About {label}": "About {label}",
1292
+ "Could not load the form for this item.": "Could not load the form for this item."
1287
1293
  };
1288
1294
  var plurals = {
1289
1295
  "%n year": [
@@ -1 +1 @@
1
- {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,4 +1,9 @@
1
1
  var translations = {
2
+ "Linked items": "Gekoppelde items",
3
+ "Still loading: {sections}": "Nog aan het laden: {sections}",
4
+ "Could not load {section}.": "{section} kon niet worden geladen.",
5
+ "Organisation credentials let apps act for your organisation. Keepiq, the credential vault of Nextcloud, keeps the secret. The app never holds it. You choose which apps may use each credential.": "Met inloggegevens van de organisatie handelen apps namens je organisatie. Keepiq, de kluis voor inloggegevens van Nextcloud, bewaart het geheim. De app krijgt het nooit in handen. Je kiest welke apps elk inloggegeven mogen gebruiken.",
6
+ "Apps sometimes act for you on an external service. You give the secret to Nextcloud once, and Keepiq keeps it in an encrypted vault. Apps make the call through Keepiq and never see the secret. You choose which apps may use each credential. Share one across apps, or keep one per app.": "Apps handelen soms namens jou bij een externe dienst. Je geeft het geheim één keer aan Nextcloud, en Keepiq bewaart het in een versleutelde kluis. Apps doen de aanvraag via Keepiq en zien het geheim nooit. Je kiest welke apps elk inloggegeven mogen gebruiken. Deel er één tussen apps, of houd er één per app.",
2
7
  "Show as": "Toon als",
3
8
  Board: "Bord",
4
9
  Calendar: "Kalender",
@@ -170,7 +175,7 @@ var translations = {
170
175
  "This integration is not set up yet. Configure a connection to start using it.": "Deze integratie is nog niet ingesteld. Configureer een verbinding om hem te gebruiken.",
171
176
  "This integration is not set up yet. Install and enable the {app} app to start using it.": "Deze integratie is nog niet ingesteld. Installeer en activeer de app {app} om hem te gebruiken.",
172
177
  "Set up {app}": "{app} instellen",
173
- "Add {type}": "Nieuwe {type}",
178
+ "Add {type}": "{type} toevoegen",
174
179
  "Add new contact": "Nieuw contact toevoegen",
175
180
  "Link contact": "Contact koppelen",
176
181
  "Search contacts": "Contacten zoeken",
@@ -1334,7 +1339,8 @@ var translations = {
1334
1339
  "Development environment": "Ontwikkelomgeving",
1335
1340
  "Test environment": "Testomgeving",
1336
1341
  "Acceptance environment": "Acceptatieomgeving",
1337
- "About {label}": "Over {label}"
1342
+ "About {label}": "Over {label}",
1343
+ "Could not load the form for this item.": "Kon het formulier voor dit item niet laden."
1338
1344
  };
1339
1345
  var plurals = {
1340
1346
  "%n year": [
@@ -1 +1 @@
1
- {"version":3,"file":"nl.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"nl.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1490,7 +1490,10 @@
1490
1490
  }
1491
1491
 
1492
1492
  .cn-look-board .cn-index-page__main.cn-index-page__main--table {
1493
- flex: 0 1 auto;
1493
+ /* The body is a row: the card takes the width left beside a sidebar (the
1494
+ board's table card spans the content), and keeps the height of its rows. */
1495
+ flex: 1 1 auto;
1496
+ align-self: flex-start;
1494
1497
  border: 1px solid var(--color-border);
1495
1498
  border-radius: var(--cn-board-card-radius, 12px);
1496
1499
  background: var(--color-main-background);
@@ -1680,6 +1683,37 @@
1680
1683
  background: var(--color-primary-element-light);
1681
1684
  }
1682
1685
 
1686
+ /* The status accent outranks the board's border, also on hover and selection. */
1687
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent {
1688
+ border-inline-start-width: 4px;
1689
+ padding-inline-start: 13px;
1690
+ }
1691
+
1692
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-success,
1693
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-success:hover {
1694
+ border-inline-start-color: var(--color-element-success);
1695
+ }
1696
+
1697
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-warning,
1698
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-warning:hover {
1699
+ border-inline-start-color: var(--color-element-warning);
1700
+ }
1701
+
1702
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-error,
1703
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-error:hover {
1704
+ border-inline-start-color: var(--color-element-error);
1705
+ }
1706
+
1707
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-info,
1708
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-info:hover {
1709
+ border-inline-start-color: var(--color-element-info);
1710
+ }
1711
+
1712
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-primary,
1713
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-primary:hover {
1714
+ border-inline-start-color: var(--color-primary-element);
1715
+ }
1716
+
1683
1717
  .cn-look-board .cn-object-card--board .cn-object-card__content {
1684
1718
  display: flex;
1685
1719
  flex: 1 1 auto;
@@ -19425,6 +19459,18 @@ a.cn-xwiki-card__title[data-v-54e85fe3]:hover {
19425
19459
  color: var(--color-text-maxcontrast);
19426
19460
  font-style: italic;
19427
19461
  }
19462
+ .cn-related-objects-widget__section-status[data-v-44c2988f] {
19463
+ margin: 0;
19464
+ padding: var(--default-grid-baseline, 4px) calc(2 * var(--default-grid-baseline, 4px));
19465
+ color: var(--color-text-maxcontrast);
19466
+ font-size: 0.85em;
19467
+ }
19468
+ .cn-related-objects-widget__section-error[data-v-44c2988f] {
19469
+ margin: 0;
19470
+ padding: var(--default-grid-baseline, 4px) calc(2 * var(--default-grid-baseline, 4px));
19471
+ color: var(--color-error-text, var(--color-error));
19472
+ font-size: 0.85em;
19473
+ }
19428
19474
  .cn-related-objects-widget__empty-state[data-v-44c2988f] {
19429
19475
  /* NcEmptyContent defaults to filling tall parents; inside a widget cell a
19430
19476
  compact block reads better. */
@@ -22090,6 +22136,47 @@ to { background-position: -200% 0;
22090
22136
  border-color: var(--color-primary-element);
22091
22137
  background: var(--color-primary-element-light);
22092
22138
  }
22139
+
22140
+ /* The 4px start border takes 3px of padding, so the content stays put. */
22141
+ .cn-object-card.cn-object-card--accent[data-v-6f672c8f] {
22142
+ border-inline-start-width: 4px;
22143
+ padding-inline-start: 13px;
22144
+ }
22145
+ .cn-object-card.cn-object-card--accent-success[data-v-6f672c8f] {
22146
+ border-inline-start-color: var(--color-element-success);
22147
+ }
22148
+ .cn-object-card.cn-object-card--accent-warning[data-v-6f672c8f] {
22149
+ border-inline-start-color: var(--color-element-warning);
22150
+ }
22151
+ .cn-object-card.cn-object-card--accent-error[data-v-6f672c8f] {
22152
+ border-inline-start-color: var(--color-element-error);
22153
+ }
22154
+ .cn-object-card.cn-object-card--accent-info[data-v-6f672c8f] {
22155
+ border-inline-start-color: var(--color-element-info);
22156
+ }
22157
+ .cn-object-card.cn-object-card--accent-primary[data-v-6f672c8f] {
22158
+ border-inline-start-color: var(--color-primary-element);
22159
+ }
22160
+ .cn-object-card__accent-icon[data-v-6f672c8f] {
22161
+ display: inline-flex;
22162
+ vertical-align: -2px;
22163
+ margin-inline-end: 4px;
22164
+ }
22165
+ .cn-object-card--accent-success .cn-object-card__accent-icon[data-v-6f672c8f] {
22166
+ color: var(--color-text-success);
22167
+ }
22168
+ .cn-object-card--accent-warning .cn-object-card__accent-icon[data-v-6f672c8f] {
22169
+ color: var(--color-text-warning);
22170
+ }
22171
+ .cn-object-card--accent-error .cn-object-card__accent-icon[data-v-6f672c8f] {
22172
+ color: var(--color-text-error);
22173
+ }
22174
+ .cn-object-card--accent-info .cn-object-card__accent-icon[data-v-6f672c8f] {
22175
+ color: var(--color-element-info);
22176
+ }
22177
+ .cn-object-card--accent-primary .cn-object-card__accent-icon[data-v-6f672c8f] {
22178
+ color: var(--color-primary-element);
22179
+ }
22093
22180
  .cn-object-card__checkbox[data-v-6f672c8f] {
22094
22181
  flex-shrink: 0;
22095
22182
  padding-top: 2px;
@@ -240,9 +240,10 @@ const baseActions = {
240
240
  * @param {object} [slugs] Optional slug hints for live-updates transport
241
241
  * @param {string|null} [slugs.registerSlug] Canonical register slug (e.g. 'zaken')
242
242
  * @param {string|null} [slugs.schemaSlug] Canonical schema slug (e.g. 'meldingen')
243
+ * @param {string} [slugs.collectionUrl] Endpoint `fetchCollection` lists from instead of `/register/schema`; every other call keeps the pair URL
243
244
  */
244
245
  registerObjectType(slug, schemaId, registerId, slugs = {}) {
245
- const { registerSlug = null, schemaSlug = null } = slugs;
246
+ const { registerSlug = null, schemaSlug = null, collectionUrl = null } = slugs;
246
247
  // Replace entire objects so Vue 2 reactivity detects the change
247
248
  // (Vue 2 cannot track new properties added to existing reactive objects)
248
249
  this.objectTypeRegistry = {
@@ -252,6 +253,7 @@ const baseActions = {
252
253
  register: registerId,
253
254
  registerSlug,
254
255
  schemaSlug,
256
+ ...(collectionUrl ? { collectionUrl } : {}),
255
257
  },
256
258
  };
257
259
  this.collections = { ...this.collections, [slug]: [] };
@@ -496,6 +498,31 @@ const baseActions = {
496
498
  return url
497
499
  },
498
500
 
501
+ /**
502
+ * Build the list URL for a type registered with a `collectionUrl`. `_order`
503
+ * goes in the bracket form (`_order[key]=dir`) every PHP endpoint parses.
504
+ *
505
+ * @param {object} config The type config
506
+ * @param {object} [params] Query parameters
507
+ * @return {string} Full URL including query string
508
+ */
509
+ _buildCollectionUrl(config, params = {}) {
510
+ const { _order: order, ...rest } = params;
511
+ const merged = { ...rest };
512
+ if (order && typeof order === 'object') {
513
+ for (const [key, dir] of Object.entries(order)) {
514
+ merged[`_order[${key}]`] = dir;
515
+ }
516
+ }
517
+ const lang = this._resolveLanguage();
518
+ if (lang) {
519
+ merged._lang = lang;
520
+ }
521
+ const url = prefixUrl(config.collectionUrl);
522
+ const query = buildQueryString(merged);
523
+ return query && url.includes('?') ? url + '&' + query.slice(1) : url + query
524
+ },
525
+
499
526
  /**
500
527
  * Fetch for a type, retrying ONCE with its `schemaFallback` when the
501
528
  * schema path segment answers 404. A type without a fallback (every type
@@ -685,14 +712,15 @@ const baseActions = {
685
712
  }
686
713
  }
687
714
 
688
- const response = await this._fetchWithSchemaFallback(
689
- type,
690
- (schema) => this._buildUrlWithParams(type, fetchParams, null, schema),
691
- {
692
- method: 'GET',
693
- headers: this._buildHeaders(),
694
- },
695
- );
715
+ const config = this._getTypeConfig(type);
716
+ const init = { method: 'GET', headers: this._buildHeaders() };
717
+ const response = config.collectionUrl
718
+ ? await trackedFetch(this._buildCollectionUrl(config, fetchParams), init)
719
+ : await this._fetchWithSchemaFallback(
720
+ type,
721
+ (schema) => this._buildUrlWithParams(type, fetchParams, null, schema),
722
+ init,
723
+ );
696
724
 
697
725
  if (!response.ok) {
698
726
  const failure = await parseResponseError(response, type);