From c61a82d4a50203397807a0ce3aaf353aa0299842 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:05:57 +0200 Subject: [PATCH 001/289] chore(release): sync beta back into development (#511) Co-authored-by: Ruben van der Linde Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> From b9547b50c5b4dd625661c6c6cfbb92721f56abae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:06:11 +0200 Subject: [PATCH 002/289] chore(release): 0.2.17-unstable.20260831115322 (#509) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 209a3bd1..8401e48a 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -42,7 +42,7 @@ Vrij en open source onder de EUPL-1.2-licentie. **Ondersteuning:** Voor ondersteuning, neem contact op via support@conduction.nl. ]]> - 0.2.16-unstable.20260831045747 + 0.2.17-unstable.20260831115322 EUPL-1.2 Conduction Planninq From 48b18796993bd15b2c4b40459c6c3366ffebdb87 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 31 Aug 2026 18:11:43 +0200 Subject: [PATCH 003/289] fix(deps): make the npm overrides follow their direct dependency (#523) Dependabot cannot update these packages. Its npm updater fails with: npm error code EOVERRIDE npm error Override for conflicts with direct dependency The package is listed BOTH as a direct dependency and as a literal overrides entry pinned to the same range. Dependabot bumps the direct dependency and does not move the override, npm rejects the mismatched pair, and the update is abandoned -- so these packages can never be updated, security advisories included. npm's self-reference is the idiom for exactly this: $ tells the override to follow whatever the direct dependency resolves to, instead of a range that has to be kept in lockstep by hand. This repo already uses it (see the vue entry under @vue/test-utils). Transitive consumers still get pinned to one version -- the direct dependency's -- which is what the override was there to do. What changes is that the pin tracks the dependency instead of duplicating it. Verified in a clean container: bumping the direct dependency with the literal override reproduces the EOVERRIDE above; with the self-reference the same bump resolves. Co-authored-by: Conduction Release Bot --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a90f35d0..41ffeda1 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,8 @@ }, "overrides": { "libxmljs2": "^0.37.0", - "@nextcloud/axios": "~2.5.2", - "@nextcloud/l10n": "3.4.1", + "@nextcloud/axios": "$@nextcloud/axios", + "@nextcloud/l10n": "$@nextcloud/l10n", "eslint-plugin-import-x": "4.13.1" }, "devDependencies": { From a3d75e07e81f2c8968c52f40ddc6f3289f2c1912 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:58:39 +0200 Subject: [PATCH 004/289] chore(deps-dev): bump @eslint/config-helpers from 0.4.2 to 0.7.0 (#515) Bumps [@eslint/config-helpers](https://github.com/eslint/rewrite/tree/HEAD/packages/config-helpers) from 0.4.2 to 0.7.0. - [Release notes](https://github.com/eslint/rewrite/releases) - [Changelog](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/CHANGELOG.md) - [Commits](https://github.com/eslint/rewrite/commits/core-v0.7.0/packages/config-helpers) --- updated-dependencies: - dependency-name: "@eslint/config-helpers" dependency-version: 0.7.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 94 ++++++----------------------------------------- package.json | 2 +- 2 files changed, 13 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index de4478d9..2ddacce9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "@babel/core": "^7.29.7", "@babel/preset-env": "^7.29.5", "@cyclonedx/cyclonedx-npm": "^6.0.1", - "@eslint/config-helpers": "^0.4.2", + "@eslint/config-helpers": "^0.7.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.39.1", "@nextcloud/browserslist-config": "^3.0.1", @@ -2243,7 +2243,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2260,7 +2259,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2277,7 +2275,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2294,7 +2291,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2311,7 +2307,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2328,7 +2323,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2345,7 +2339,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2362,7 +2355,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2379,7 +2371,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2396,7 +2387,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2413,7 +2403,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2430,7 +2419,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2447,7 +2435,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2464,7 +2451,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2481,7 +2467,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2498,7 +2483,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2515,7 +2499,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2532,7 +2515,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2549,7 +2531,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2566,7 +2547,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2583,7 +2563,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2600,7 +2579,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2617,7 +2595,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2634,7 +2611,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2651,7 +2627,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2668,7 +2643,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2721,29 +2695,29 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@eslint/core": "^1.2.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/eslintrc": { @@ -3570,7 +3544,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4421,7 +4394,6 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4461,7 +4433,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4483,7 +4454,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4505,7 +4475,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4527,7 +4496,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4549,7 +4517,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4571,7 +4538,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4593,7 +4559,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4615,7 +4580,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4637,7 +4601,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4659,7 +4622,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4681,7 +4643,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4703,7 +4664,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4722,7 +4682,6 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -4978,7 +4937,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4992,7 +4950,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5006,7 +4963,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5020,7 +4976,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5034,7 +4989,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5048,7 +5002,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5062,7 +5015,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5076,7 +5028,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5090,7 +5041,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5104,7 +5054,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5118,7 +5067,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5132,7 +5080,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5146,7 +5093,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5160,7 +5106,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5174,7 +5119,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5188,7 +5132,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5202,7 +5145,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5216,7 +5158,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5230,7 +5171,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5244,7 +5184,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5258,7 +5197,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5272,7 +5210,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5286,7 +5223,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5300,7 +5236,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5314,7 +5249,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9517,7 +9451,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, "license": "Apache-2.0", "optional": true, "engines": { @@ -11643,7 +11576,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -12525,7 +12457,7 @@ "version": "5.1.9", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true }, @@ -15178,7 +15110,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, "license": "MIT", "optional": true, "peer": true @@ -17665,7 +17596,7 @@ "version": "1.103.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.103.1.tgz", "integrity": "sha512-9icZURbP51S6S0QGoyaeqk9uB06GNWxsFYWfH5RgpFgqK5FA8tJcM3AdVxrZEVJ7dz+L87nG95gBKf4VuaMHGw==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -20800,7 +20731,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, diff --git a/package.json b/package.json index 41ffeda1..f7cb82ce 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@babel/core": "^7.29.7", "@babel/preset-env": "^7.29.5", "@cyclonedx/cyclonedx-npm": "^6.0.1", - "@eslint/config-helpers": "^0.4.2", + "@eslint/config-helpers": "^0.7.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.39.1", "@nextcloud/browserslist-config": "^3.0.1", From 2a974fca40cabec688c3008973c5c5fee2f11bc7 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 31 Aug 2026 19:39:54 +0200 Subject: [PATCH 005/289] build(lint): move planninq to eslint 10, and fix what it then found (#527) planninq was the last app in the fleet on eslint 8. Dependabot's @nextcloud/eslint-config 8.4.2 -> 9.0.1 bump could not install at all: that release declares `peer eslint: ">=10"`, so twelve checks went red at npm ci, before a single file was linted. Bumping the config alone was never going to work. eslint 8 was held in three more places at once: @typescript-eslint/parser@7 peers eslint ^8.56.0 eslint-plugin-import@2.32.0 peers eslint up to ^9 @eslint/eslintrc exists only to bridge the eslintrc era So the stack moves together, to the versions hermiq, versioniq and keepiq already run green: eslint ^10.9.1, @nextcloud/eslint-config ^9.0.1, @typescript-eslint/parser ^8.68.0. eslint-plugin-vue is dropped because v9 bundles its own v10, and a stale ^9 hoists over it and makes every SFC parse as TypeScript. conductionVue3Fixes is dropped too: it patched a Vue-2 ruleset, and against v9's recommended all 21 vue/no-deprecated-* rules are already on while the two it existed to disable are not enabled at all. Spreading it does not merely duplicate work, it BREAKS the run, naming vue/* rules while registering no plugin. THE CHECK THAT MATTERS HERE IS THE FILE COUNT, NOT THE ERROR COUNT. An eslint 10 install pointed at a config it cannot read lints NOTHING and exits 2 in a way that reads as a lint failure. That is how versioniq's whole Vue and TypeScript layer went unchecked with 266 real problems behind it. This migration hit exactly that on the first attempt, exit 2 and zero files, and it was caught because the file count was the check. eslint 8 linted 46 files; this lints 47. Those 47 files carried 284 errors nobody could see. --fix cleared 222. The remaining 62 are fixed here rather than deferred: 21 no-console all console.error/warn, i.e. real diagnostics, so the rule now allows those two by name instead of being switched off. A stray console.log is still an error, which is what the rule is for. 18 max-statements single-line `if (c) { stmt }` expanded. 14 unused catch every one of the 8 blocks handles the failure and simply does not read the binding, so they become optional catch binding. None was an empty swallow. 5 multi-word-names route views only. They are mounted by path and never written as a tag, so they cannot collide with an HTML element, which is the rule's purpose. Scoped off for views/, kept everywhere else. 2 deprecated props can-close -> no-close, which is its INVERSE, so `:canClose="!loading"` becomes `:noClose="loading"`. And Vue Router 4 removed `exact`. 2 jsdoc tags {@see X} is not a valid inline tag, {@link X} is. 1 event casing MainMenu emitted 'open-settings' while App.vue listens with @openSettings. That matched only through Vue 3's camelize fallback in emit(); the emit is now camelCase and the two agree directly. 1 dead assignment every branch assigns or returns, so the null initializer was never read. Verified: eslint 47 files 0/0, build exits 0, stylelint exits 0, and the unit suite is 98/98 both before and after, run on the same checkout. Co-authored-by: Conduction Release Bot --- eslint.config.js | 73 - eslint.config.mjs | 108 + package-lock.json | 6382 ++++++--------------- package.json | 12 +- src/App.vue | 16 +- src/components/MemberSearch.vue | 19 +- src/components/ProjectListItem.vue | 2 +- src/components/ProjectSettingsSidebar.vue | 20 +- src/components/TaskCard.vue | 10 +- src/components/TaskDependencies.vue | 19 +- src/dialogs/LabelDeleteDialog.vue | 10 +- src/dialogs/LabelEditDialog.vue | 8 +- src/dialogs/ProjectCreationDialog.vue | 15 +- src/dialogs/ProjectDeleteDialog.vue | 10 +- src/dialogs/ProjectLeaveDialog.vue | 8 +- src/dialogs/TimeEntryDialog.vue | 9 +- src/main.js | 7 +- src/navigation/MainMenu.vue | 7 +- src/router/index.js | 2 +- src/settings.js | 4 +- src/store/dependencies.js | 6 +- src/store/labels.js | 6 +- src/store/modules/settings.js | 2 +- src/store/projects.js | 52 +- src/store/store.js | 2 +- src/store/timeEntries.js | 5 +- src/utils/durationParser.js | 2 +- src/utils/taskHelpers.js | 4 +- src/utils/timelineHelpers.js | 2 +- src/views/Dashboard.vue | 7 +- src/views/Portfolio.vue | 2 +- src/views/ProjectBacklog.vue | 1 + src/views/ProjectBoard.vue | 48 +- src/views/ProjectList.vue | 25 +- src/views/ProjectTimeline.vue | 26 +- src/views/TaskDetail.vue | 30 +- src/views/Timesheet.vue | 16 +- src/views/settings/AdminRoot.vue | 12 +- src/views/settings/Settings.vue | 46 +- src/views/settings/UserSettings.vue | 7 +- 40 files changed, 2100 insertions(+), 4942 deletions(-) delete mode 100644 eslint.config.js create mode 100644 eslint.config.mjs diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 4c368972..00000000 --- a/eslint.config.js +++ /dev/null @@ -1,73 +0,0 @@ -const { - defineConfig, -} = require('@eslint/config-helpers') - -const js = require('@eslint/js') - -const { - FlatCompat, -} = require('@eslint/eslintrc') - -// Shared Vue 3 correction layer from @conduction/nextcloud-vue. -// -// `@nextcloud/eslint-config@8` (pulled in via FlatCompat below) resolves -// eslint-plugin-vue's **Vue 2** preset. That is not merely stale: two of its -// rules are INVERTED under Vue 3, and NONE of the `vue/no-deprecated-*` rules -// are active — verified on this repo before the migration, where -// `eslint --print-config src/App.vue` listed **zero** `vue/no-deprecated-*` -// rules while `vue/no-v-model-argument` and `vue/no-v-for-template-key` were -// both at severity 2. So Vue 2 idioms survive a migration silently: -// `beforeDestroy` is the dangerous case, because Vue 3 never calls that hook, -// and planninq cleaned up live-update subscriptions, a debounce timer and an -// AbortController there — four real leaks with zero console output. -// -// `conductionVue3Fixes` is an ARRAY of three flat-config objects (language -// level, SFC parser, deprecation rules). It deliberately registers no plugins, -// so it layers cleanly onto the `@nextcloud` base — and must be spread **last** -// to win over the preset it is correcting. -// -// It enables `vue/v-on-event-hyphenation` with `ignore: ['update:modelValue']`. -// That exception is load-bearing — Nextcloud Vue 3 field components read -// `onUpdate:modelValue` directly via `useModel`, so the hyphenated -// `@update:model-value` form is silently dead. -const { - conductionVue3Fixes, -} = require('@conduction/nextcloud-vue/eslint') - -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all, -}) - -module.exports = defineConfig([{ - extends: compat.extends('@nextcloud'), - - settings: { - 'import/resolver': { - alias: { - map: [ - ['@', './src'], - ['@floating-ui/dom-actual', './node_modules/@floating-ui/dom'], - ['@conduction/nextcloud-vue', '../nextcloud-vue/src'], - ], - extensions: ['.js', '.ts', '.vue', '.json', '.css'], - }, - }, - }, - - rules: { - // Allow unused i18n functions (t, n) — imported for future translation wiring - 'no-unused-vars': ['error', { varsIgnorePattern: '^(t|n)$', argsIgnorePattern: '^_' }], - 'jsdoc/require-jsdoc': 'off', - // Allow the ADR-008 spec-coverage annotation tag (@spec) in JSDoc blocks - 'jsdoc/check-tag-names': ['warn', { definedTags: ['spec'] }], - 'vue/first-attribute-linebreak': 'off', - '@typescript-eslint/no-explicit-any': 'off', - 'n/no-missing-import': 'off', - 'import/namespace': 'off', // disable namespace checking to avoid parser requirement - 'import/default': 'off', // disable default import checking to avoid parser requirement - 'import/no-named-as-default': 'off', // disable named-as-default checking to avoid parser requirement - 'import/no-named-as-default-member': 'off', // disable named-as-default-member checking to avoid parser requirement - }, -}, ...conductionVue3Fixes]) diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..766e1b32 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,108 @@ +// Flat config for eslint 10. +// +// WHY THIS REPLACED eslint.config.js +// ---------------------------------- +// planninq was the last app in the fleet still on eslint 8. `@nextcloud/eslint-config@9` +// declares `peer eslint: ">=10"`, so the dependabot bump to 9.0.1 could not install at +// all: twelve checks went red at `npm ci`, before a single file was linted. Bumping the +// config alone was never going to work, because the eslint 8 constraint was held in three +// more places at once: +// +// @typescript-eslint/parser@7 peers eslint ^8.56.0 +// eslint-plugin-import@2.32.0 peers eslint up to ^9 +// @eslint/eslintrc exists only to bridge the eslintrc era +// +// So the stack moved together, to the versions hermiq, versioniq and keepiq already run +// green: eslint ^10.9.1, @nextcloud/eslint-config ^9.0.1, @typescript-eslint/parser +// ^8.68.0. +// +// `eslint-plugin-vue` is deliberately NOT a dependency any more. v9 bundles its own +// `eslint-plugin-vue@10`, and a stale `^9` left in devDependencies hoists over the +// bundled copy, which makes `**/*.vue` parse as TypeScript and fails every SFC. +// `vue-eslint-parser` stays, at `^10`, because it is a peer the app must supply. +// +// `conductionVue3Fixes` is gone too. That preset existed to patch a Vue-2 ruleset, and +// against v9's `recommended` every one of its jobs is already done: all 21 +// `vue/no-deprecated-*` rules are enabled, and the two it had to switch off because they +// are inverted under Vue 3 are not enabled at all. Spreading it here is not merely +// redundant, it BREAKS the run: it names `vue/*` rules while registering no plugin, and +// eslint aborts with "could not find plugin vue" — exit 2, nothing linted. +// +// 🔴 THE FAILURE MODE THIS FILE MUST AVOID. An eslint 10 install pointed at a config it +// cannot read does not report zero problems loudly. It lints NOTHING and exits non-zero +// in a way that reads as a lint failure, which is how versioniq's entire Vue and +// TypeScript layer went unchecked with 266 real problems behind it. So the check after +// any change here is the FILE COUNT, never the error count: `npx eslint src --format +// json` must report the same 46 files it reported under eslint 8, not 0. +import { recommended } from '@nextcloud/eslint-config' + +export default [ + ...recommended, + { + // 🔴 SCOPED, and it must be. Flat config resolves a rule's plugin from the config + // object the rule sits in, and v9 also lints `.json`, where `jsdoc` is not + // registered. An unscoped `jsdoc/*` override aborts the whole run with "The jsdoc + // plugin is not defined in your configuration file" — a config error, not a + // finding. The ignores are part of that scope rather than an opinion: v9 registers + // jsdoc only inside `nextcloud/documentation/*`, and those blocks carry exactly + // this list, because Nextcloud does not require JSDoc in tests. + files: ['**/*.js', '**/*.mjs', '**/*.ts', '**/*.tsx', '**/*.vue'], + ignores: [ + '**/*.test.*', + '**/*.spec.*', + '**/*.cy.*', + '**/test/**', + '**/tests/**', + '**/__tests__/**', + '**/__mocks__/**', + ], + + settings: { + // `@conduction/nextcloud-vue` resolves to the sibling checkout when one is + // present, which is how a local nextcloud-vue is linted against this app. + 'import/resolver': { + alias: { + map: [ + ['@', './src'], + ['@floating-ui/dom-actual', './node_modules/@floating-ui/dom'], + ['@conduction/nextcloud-vue', '../nextcloud-vue/src'], + ], + extensions: ['.js', '.ts', '.vue', '.json', '.css'], + }, + }, + }, + + rules: { + // `t` and `n` are imported ahead of the translation wiring that will use them. + 'no-unused-vars': [ + 'error', + { varsIgnorePattern: '^(t|n)$', argsIgnorePattern: '^_' }, + ], + 'jsdoc/require-jsdoc': 'off', + // ADR-008 spec-coverage annotation. + 'jsdoc/check-tag-names': ['warn', { definedTags: ['spec'] }], + 'vue/first-attribute-linebreak': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'n/no-missing-import': 'off', + + // `error` and `warn` are how this app reports failures it cannot show in + // the UI, and all 21 existing calls are one of those two. Deleting them to + // satisfy the rule would remove diagnostics, so they are allowed by name + // rather than the rule being switched off: a stray `console.log` left + // behind while debugging is still an error, which is what the rule is for. + 'no-console': ['error', { allow: ['error', 'warn'] }], + }, + }, + { + // Route views are mounted by the router under their path, never written as a + // tag in a template, so they cannot collide with a current or future HTML + // element — which is the entire reason `vue/multi-word-component-names` + // exists. Renaming `Dashboard` to `PlanninqDashboard` would churn five files + // and every import of them to satisfy a rule that is not describing a risk + // here. Scoped to `views/` so ordinary components keep the rule. + files: ['src/views/**/*.vue'], + rules: { + 'vue/multi-word-component-names': 'off', + }, + }, +] diff --git a/package-lock.json b/package-lock.json index 2ddacce9..9fd1b62c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,26 +36,22 @@ "@babel/preset-env": "^7.29.5", "@cyclonedx/cyclonedx-npm": "^6.0.1", "@eslint/config-helpers": "^0.7.0", - "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.39.1", "@nextcloud/browserslist-config": "^3.0.1", - "@nextcloud/eslint-config": "^8.4.1", + "@nextcloud/eslint-config": "^9.0.1", "@nextcloud/stylelint-config": "^3.2.2", "@nextcloud/webpack-vue-config": "^7.0.2", "@playwright/test": "^1.49.0", - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/parser": "^8.68.0", "@vue/compiler-sfc": "^3.5.18", "babel-loader": "^10.1.1", "css-loader": "~7.1.1", - "eslint": "^8.56.0", - "eslint-import-resolver-alias": "^1.1.2", - "eslint-plugin-vue": "^9.21.1", + "eslint": "^10.9.1", "node-polyfill-webpack-plugin": "4.0.0", "style-loader": "~4.0.0", "stylelint": "^17.9.1", "vitest": "^4.1.11", - "vue-eslint-parser": "^10.4.1", + "vue-eslint-parser": "^10.3.0", "vue-loader": "^17.4.2", "webpack": "^5.94.0", "webpack-cli": "^6.0.1" @@ -121,26 +117,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/eslint-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz", - "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0", - "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" - } - }, "node_modules/@babel/generator": { "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", @@ -1827,9 +1803,9 @@ } }, "node_modules/@codemirror/search": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz", - "integrity": "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.2.tgz", + "integrity": "sha512-gUYkYhT2+n/+VGZ+8EzE5WFkYZUZYm1VOKDudIsNqh42uRVQJ0a6Yss9sdKT3MeOYfuL1N6AZA57oza0Oyr0LA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -1838,18 +1814,18 @@ } }, "node_modules/@codemirror/state": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz", - "integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.2.tgz", + "integrity": "sha512-U3RiPX62Wl/Gx4ftQ7UxLlloSfsFTQqKa+7vFBYteZGCzkp6oBqcsD7iSwniWRGobCAmDcwZSY+Six3+3ztdfg==", "license": "MIT", "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "node_modules/@codemirror/view": { - "version": "6.43.9", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.9.tgz", - "integrity": "sha512-sTuUzTpPMFebRhg6dawChoKKgndIwfjmJgKVxBefPElcU2NwQ6AFroupk0SFqEerQyZOGRfDNnSN8Dw/lMAsXw==", + "version": "6.43.10", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.10.tgz", + "integrity": "sha512-vVWLvd4fKWYN/pMcwUrg6dWeublxmSz+V+52ZjW3h64IVN9cRUYLk5Km4JDT9vifxAFfDtNOIFxKYENthcolJQ==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.7.0", @@ -2014,9 +1990,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.10.tgz", - "integrity": "sha512-xBja6gaAaH2R2c7eNyl0TY4dhnnZ2uhj+KXpLdEQ6M/wuk9bYFZM8wY0ykw3VO4TgEJ56KGlerXS/9KBKVR/Cg==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.11.tgz", + "integrity": "sha512-+a7SqvwQfLl8OAp/C/B8PhfV2UjRUIeCRvcZQEXjhTkM2nTxg8r9nbB7mdxkEGT67VbR6pREsAIlsx65N2KR6Q==", "dev": true, "funding": [ { @@ -2082,6 +2058,52 @@ "@csstools/css-tokenizer": "^4.0.0" } }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.1.tgz", + "integrity": "sha512-j3vdQu0XwLME5qOTWxm8cnmvsf423R2YL6DbKklCHZwkDm7UdKNu6RPlw4REIJhSlKBICY3B70/7QZdicLqZgg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.0.tgz", + "integrity": "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, "node_modules/@ctrl/tinycolor": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", @@ -2183,602 +2205,188 @@ "node": ">=14.17.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "node_modules/@es-joy/jsdoccomment": { + "version": "0.91.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.91.0.tgz", + "integrity": "sha512-vgqlMGNNhZxwDYbUNIHj3Hskb4R28iqdXx90ufHyt/NeuTQkeqjTDslAs9I0/GCAfbxP5BpH5WsL1R1fht5Lxg==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" + "@types/estree": "^1.0.9", + "@typescript-eslint/types": "^8.65.0", + "comment-parser": "1.4.7", + "esquery": "^1.7.0", + "jsdoc-type-pratt-parser": "~8.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "node_modules/@es-joy/resolve.exports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", + "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" + "engines": { + "node": ">=10" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "devOptional": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "tslib": "^2.4.0" + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@es-joy/jsdoccomment": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.41.0.tgz", - "integrity": "sha512-aKUhyn1QI5Ksbqcr3fFJj16p99QdjUxXAEuFst1Z47DRyoiMwivIH9MV/ARcJOCXVjPfjITciej8ZD2O/6qUmw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "comment-parser": "1.4.1", - "esquery": "^1.5.0", - "jsdoc-type-pratt-parser": "~4.0.0" - }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "devOptional": true, + "license": "Apache-2.0", "engines": { - "node": ">=16" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "devOptional": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "node_modules/@eslint/compat": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.1.0.tgz", + "integrity": "sha512-LgaSCymEpw7tF53xvDw9SNsraPb1IBHxpdABIOM0hW8UAlP8znrjYtuxfR58FSJ3L9BhwD+FaPRFQpZq84Nh6g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "peerDependencies": { + "eslint": "^8.40 || 9 || 10" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "node_modules/@eslint/json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/json/-/json-2.0.1.tgz", + "integrity": "sha512-Thz2j92ceUF3Bq/0TuWb3MWn3Z+Cwc8k5ptF0fakl2D4Mp8mSx07Xr1aQM4R5NoihzarWUdxfOmQ8DesGy4jOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanwhocodes/momoa": "^3.3.10", + "natural-compare": "^1.4.0" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "devOptional": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", - "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", - "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", - "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.3.0", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/js": { - "version": "9.39.5", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", - "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@file-type/xml": { @@ -2816,20 +2424,42 @@ "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", "license": "MIT" }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -2846,13 +2476,29 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@humanwhocodes/momoa": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.12.tgz", + "integrity": "sha512-xS9xl4ieqTqhSZfKV3YXj/htwJCUxbgvkTVaK1fkESgrOzvoVSOTRQeQ8tTLOZUS0Csi2xqtJQXgVTRH5OEbHQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "devOptional": true, - "license": "BSD-3-Clause" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -2873,37 +2519,6 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", - "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -3537,45 +3152,6 @@ "integrity": "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==", "license": "MIT" }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", - "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" - } - }, "node_modules/@nextcloud/auth": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@nextcloud/auth/-/auth-2.6.0.tgz", @@ -3728,60 +3304,60 @@ } }, "node_modules/@nextcloud/eslint-config": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@nextcloud/eslint-config/-/eslint-config-8.4.2.tgz", - "integrity": "sha512-zsDcBxvp2Vr/BgasK/vNYJ84LOXjl4RseJPrcp93zcnaB2WnygV50Sd0nQ5JN0ngTyPjiIlGd92MMzrMTofjRA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@nextcloud/eslint-config/-/eslint-config-9.0.1.tgz", + "integrity": "sha512-TzJrZdrIJgnoW9V0U31sLuhot/i1kWB3A9VMFUiuMXEKYQUTwgL4LTA5DbJ6Dnlsx+6XuR3Bi6d4RWfaeLzlJw==", "dev": true, "license": "AGPL-3.0-or-later", + "dependencies": { + "@eslint/js": "^10.0.1", + "@eslint/json": "^2.0.0", + "@stylistic/eslint-plugin": "^5.10.0", + "eslint-config-flat-gitignore": "^2.3.0", + "eslint-plugin-antfu": "^3.2.3", + "eslint-plugin-jsdoc": "^63.0.10", + "eslint-plugin-perfectionist": "^5.9.1", + "eslint-plugin-vue": "^10.9.2", + "fast-xml-parser": "^5.9.3", + "globals": "^17.7.0", + "semver": "^7.8.5", + "sort-package-json": "^4.0.0", + "typescript-eslint": "^8.62.1" + }, "engines": { - "node": "^20.0.0", - "npm": "^10.0.0" + "node": "^22.14 || ^24 || >=26" }, "peerDependencies": { - "@babel/core": "^7.26.9", - "@babel/eslint-parser": "^7.16.5", - "@nextcloud/eslint-plugin": "^2.2.1", - "@vue/eslint-config-typescript": "^13.0.0", - "eslint": "^8.27.0", - "eslint-config-standard": "^17.1.0", - "eslint-import-resolver-exports": "^1.0.0-beta.5", - "eslint-import-resolver-typescript": "^3.8.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsdoc": "^46.2.6", - "eslint-plugin-n": "^16.0.0", - "eslint-plugin-promise": "^6.6.0", - "eslint-plugin-vue": "^9.7.0", - "typescript": "^5.0.2" - } - }, - "node_modules/@nextcloud/eslint-plugin": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@nextcloud/eslint-plugin/-/eslint-plugin-2.2.1.tgz", - "integrity": "sha512-RX+0FxpL1h2EzjNLeW0VSGTkbyWIq7WgV7QAjtyUmDbSGwf1ds9Zy5OcRkgXRHRIu/W0gB0DhS2iz9qXHphCzA==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "eslint": ">=10" + } + }, + "node_modules/@nextcloud/eslint-config/node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "fast-xml-parser": "^4.2.5", - "requireindex": "^1.2.0", - "semver": "^7.5.3" - }, + "license": "MIT", "engines": { - "node": "^20.0.0", - "npm": "^10.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" }, "peerDependencies": { - "eslint": ">=7.0.0" + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/@nextcloud/eslint-plugin/node_modules/semver": { + "node_modules/@nextcloud/eslint-config/node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -4085,18 +3661,6 @@ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", "license": "MIT" }, - "node_modules/@nextcloud/vue/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@nextcloud/vue/node_modules/vue-router": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.3.0.tgz", @@ -4172,17 +3736,6 @@ "webpack-dev-server": "^6.0.0" } }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "eslint-scope": "5.1.1" - } - }, "node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", @@ -4213,7 +3766,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -4227,7 +3780,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -4237,7 +3790,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -4247,17 +3800,6 @@ "node": ">= 8" } }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12.4.0" - } - }, "node_modules/@npmcli/agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", @@ -4390,10 +3932,21 @@ "node": ">=20.0" } }, + "node_modules/@oxc-project/types": { + "version": "0.147.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.147.0.tgz", + "integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@parcel/watcher": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4433,6 +3986,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4454,6 +4008,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4475,6 +4030,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4496,6 +4052,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4517,6 +4074,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4538,6 +4096,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4559,6 +4118,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4580,6 +4140,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4601,6 +4162,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4622,6 +4184,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4643,6 +4206,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4664,6 +4228,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4678,20 +4243,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@peculiar/asn1-cms": { "version": "2.9.4", "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.9.4.tgz", @@ -4924,344 +4475,286 @@ "playwright": "1.62.1" }, "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", - "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", - "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", - "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", - "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", - "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", - "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", - "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz", + "integrity": "sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", - "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", - "cpu": [ - "arm" + "android" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", - "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.6.tgz", + "integrity": "sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", - "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.6.tgz", + "integrity": "sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", - "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.6.tgz", + "integrity": "sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==", "cpu": [ - "loong64" + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", - "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.6.tgz", + "integrity": "sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==", "cpu": [ - "loong64" + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", - "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.6.tgz", + "integrity": "sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==", "cpu": [ - "ppc64" + "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", - "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.6.tgz", + "integrity": "sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==", "cpu": [ - "ppc64" + "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", - "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.6.tgz", + "integrity": "sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==", "cpu": [ - "riscv64" + "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", - "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.6.tgz", + "integrity": "sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==", "cpu": [ - "riscv64" + "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", - "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.6.tgz", + "integrity": "sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==", "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", - "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.6.tgz", + "integrity": "sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", - "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.6.tgz", + "integrity": "sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", - "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", - "cpu": [ - "x64" ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", - "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.6.tgz", + "integrity": "sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", - "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.6.tgz", + "integrity": "sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", - "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", - "cpu": [ - "ia32" ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", - "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.6.tgz", + "integrity": "sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", - "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", - "cpu": [ - "x64" ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/base62": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz", + "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==", "dev": true, "license": "MIT", - "peer": true + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", @@ -5283,6 +4776,27 @@ "dev": true, "license": "MIT" }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", + "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/types": "^8.56.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.0.0 || ^10.0.0" + } + }, "node_modules/@svgdotjs/svg.draggable.js": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@svgdotjs/svg.draggable.js/-/svg.draggable.js-3.0.6.tgz", @@ -5367,18 +4881,6 @@ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -5539,14 +5041,6 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -5673,628 +5167,304 @@ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.68.0.tgz", + "integrity": "sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/type-utils": "8.68.0", + "@typescript-eslint/utils": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "@typescript-eslint/parser": "^8.68.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", + "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">= 4" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "node_modules/@typescript-eslint/parser": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.68.0.tgz", + "integrity": "sha512-fHq2VC1kpyYfvEcbiMjOpySY4WS7voEp89yAThrHRX5sm9j2lzYppCb2umFMEed4fWcyeLjHxrz0mpjNBaBxMQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", + "debug": "^4.4.3" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.68.0.tgz", + "integrity": "sha512-5GQtWZCXFcFYux955pvoS02WLc49pXNlvIxocKjS0clvwo3in1RdlzVKyiqQH9vE5AKWFLTaUgeQkOrTS+0Qxw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "@typescript-eslint/tsconfig-utils": "^8.68.0", + "@typescript-eslint/types": "^8.68.0", + "debug": "^4.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.68.0.tgz", + "integrity": "sha512-T5eXpcaJNg8bhjHJ8Rjp68Vq/QBteYtTKY8TZqVNPaUbuz0f6jI9t6aDkylwvalpAB9XTTFeFOjrjXAZ3YvmVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.68.0.tgz", + "integrity": "sha512-F7zrGQfiJHojPwi8vhxZQC1tWtJzvL74cK/nqri2lk8YUXvYaYwl263xOJ69jDWPUk1hmcdoayFwk9lX09npVw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@uiw/codemirror-theme-github": { - "version": "4.25.11", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-github/-/codemirror-theme-github-4.25.11.tgz", - "integrity": "sha512-3s0LK3gX2mvGI996z3G0tEHZqshbeJRly+QMRsKGAI1Tfr1V475bfaW9NvnoDdINuQHt+RB+ri6q57+WWF8d+A==", - "license": "MIT", - "dependencies": { - "@uiw/codemirror-themes": "4.25.11" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - } - }, - "node_modules/@uiw/codemirror-themes": { - "version": "4.25.11", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.11.tgz", - "integrity": "sha512-SBNCOgRsCtewGNocRbmjbCkltGXlFcPJsvhxQ351VynQjnWUiPbUrFcEU/haQ3HanROdAAjWXZJPk5bMBxl2jw==", "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@codemirror/language": ">=6.0.0", - "@codemirror/state": ">=6.0.0", - "@codemirror/view": ">=6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", - "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", - "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", - "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", - "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", - "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", - "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", - "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", - "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", - "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", - "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", - "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-loong64-musl": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", - "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", - "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", - "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", - "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", - "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", - "cpu": [ - "s390x" - ], + "node_modules/@typescript-eslint/type-utils": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.68.0.tgz", + "integrity": "sha512-X77zqoY1EjeWGs/0JNxeaMfp5C5lIz4Tw8y66F1Ne8Faq6g424sBNYM6xBAqElfGZPLpWS+CZAp0DXyKDzWiHg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true + "dependencies": { + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0", + "@typescript-eslint/utils": "8.68.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", - "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", - "cpu": [ - "x64" - ], + "node_modules/@typescript-eslint/types": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.68.0.tgz", + "integrity": "sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", - "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", - "cpu": [ - "x64" - ], + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.68.0.tgz", + "integrity": "sha512-OKKsD0tYmoNiU5PW2zehO1yO56jYOm1ShYlxon/Z0SJNidAkdVg86eg9ruRuoXf8xfnuWZGbwDsStkoXbZtIIA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true + "dependencies": { + "@typescript-eslint/project-service": "8.68.0", + "@typescript-eslint/tsconfig-utils": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } }, - "node_modules/@unrs/resolver-binding-openharmony-arm64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", - "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", - "cpu": [ - "arm64" - ], + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", - "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", - "cpu": [ - "wasm32" - ], + "node_modules/@typescript-eslint/utils": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.68.0.tgz", + "integrity": "sha512-PB5gJMMOg0Q5P1tsgWtEAqQacJXq0qEqRHDX/YJ4FaTMLfZPpHB3gjl2EJuiZyPABxmj4ZQYiY9m1bdAJ5y7tQ==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0" }, "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", - "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", - "cpu": [ - "arm64" - ], + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.68.0.tgz", + "integrity": "sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true + "dependencies": { + "@typescript-eslint/types": "8.68.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", - "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", - "cpu": [ - "ia32" - ], + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@uiw/codemirror-theme-github": { + "version": "4.25.11", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-github/-/codemirror-theme-github-4.25.11.tgz", + "integrity": "sha512-3s0LK3gX2mvGI996z3G0tEHZqshbeJRly+QMRsKGAI1Tfr1V475bfaW9NvnoDdINuQHt+RB+ri6q57+WWF8d+A==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true + "dependencies": { + "@uiw/codemirror-themes": "4.25.11" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", - "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@uiw/codemirror-themes": { + "version": "4.25.11", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.11.tgz", + "integrity": "sha512-SBNCOgRsCtewGNocRbmjbCkltGXlFcPJsvhxQ351VynQjnWUiPbUrFcEU/haQ3HanROdAAjWXZJPk5bMBxl2jw==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/language": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", + "license": "ISC" }, "node_modules/@vitest/expect": { "version": "4.1.11", @@ -6616,141 +5786,13 @@ "superjson": "^2.2.2" } }, - "node_modules/@vue/devtools-shared": { - "version": "7.7.10", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", - "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", - "license": "MIT", - "dependencies": { - "rfdc": "^1.4.1" - } - }, - "node_modules/@vue/eslint-config-typescript": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-13.0.0.tgz", - "integrity": "sha512-MHh9SncG/sfqjVqjcuFLOLD6Ed4dRAis4HNt0dXASeAuLqIAx4YMB1/m2o4pUKK1vCt8fUvYG8KKX2Ot3BVZTg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@typescript-eslint/eslint-plugin": "^7.1.1", - "@typescript-eslint/parser": "^7.1.1", - "vue-eslint-parser": "^9.3.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "peerDependencies": { - "eslint": "^8.56.0", - "eslint-plugin-vue": "^9.0.0", - "typescript": ">=4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@vue/eslint-config-typescript/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@vue/eslint-config-typescript/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@vue/eslint-config-typescript/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@vue/eslint-config-typescript/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@vue/eslint-config-typescript/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@vue/eslint-config-typescript/node_modules/vue-eslint-parser": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", - "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", - "dev": true, + "node_modules/@vue/devtools-shared": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", + "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", "license": "MIT", - "peer": true, "dependencies": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=6.0.0" + "rfdc": "^1.4.1" } }, "node_modules/@vue/reactivity": { @@ -7257,20 +6299,23 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "devOptional": true, + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -7314,7 +6359,6 @@ "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=14" } @@ -7323,146 +6367,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "devOptional": true, - "license": "Python-2.0" - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "Python-2.0" }, "node_modules/asn1.js": { "version": "4.10.1", @@ -7566,17 +6472,6 @@ "node": ">=8" } }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -7699,10 +6594,14 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base-64": { "version": "1.0.0", @@ -7903,14 +6802,16 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "devOptional": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/braces": { @@ -8137,20 +7038,6 @@ "dev": true, "license": "MIT" }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -8158,31 +7045,6 @@ "dev": true, "license": "MIT" }, - "node_modules/builtins": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", - "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "semver": "^7.0.0" - } - }, - "node_modules/builtins/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -8338,7 +7200,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8394,7 +7256,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -8407,6 +7269,19 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -8562,7 +7437,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -8575,7 +7450,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/colord": { @@ -8625,12 +7500,11 @@ } }, "node_modules/comment-parser": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", - "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.7.tgz", + "integrity": "sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 12.0.0" } @@ -8699,13 +7573,6 @@ "node": ">= 0.6" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true, - "license": "MIT" - }, "node_modules/confbox": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", @@ -9176,66 +8043,9 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 12" } }, "node_modules/date-fns": { @@ -9447,16 +8257,42 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/detect-indent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8" } }, + "node_modules/detect-newline": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz", + "integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -9495,19 +8331,6 @@ "dev": true, "license": "MIT" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/discontinuous-range": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", @@ -9530,19 +8353,6 @@ "node": ">=6" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -9673,9 +8483,9 @@ "peer": true }, "node_modules/electron-to-chromium": { - "version": "1.5.416", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.416.tgz", - "integrity": "sha512-K6bvB2BjnNrugtIih6ewlbBI9DXa976jIdiIlRLHhBoEI9a4JaQjjHyF+A1IQI543aQYR4LnmOrT/K5fZj0aPA==", + "version": "1.5.417", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.417.tgz", + "integrity": "sha512-4T+DTDWuMPM4aHlHwWdAVCVWwp7LDilnhzkj+c/Lbj91XSQrLuOmZSLtS9Q4iIqjlPUbPOnC624zDVVHCHaolQ==", "license": "ISC" }, "node_modules/elliptic": { @@ -9821,96 +8631,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-abstract": { - "version": "1.24.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", - "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-abstract-get": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", - "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.2", - "is-callable": "^1.2.7", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -9962,84 +8682,6 @@ "node": ">= 0.4" } }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", - "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "es-abstract-get": "^1.0.0", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "is-callable": "^1.2.7", - "is-date-object": "^1.1.0", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "devOptional": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -10069,444 +8711,159 @@ } }, "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.9.1.tgz", + "integrity": "sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A==", "devOptional": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-compat-utils": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", - "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/eslint-compat-utils/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-config-standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", - "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", - "eslint-plugin-promise": "^6.0.0" - } - }, - "node_modules/eslint-import-resolver-alias": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz", - "integrity": "sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - }, - "peerDependencies": { - "eslint-plugin-import": ">=1.4.0" - } - }, - "node_modules/eslint-import-resolver-exports": { - "version": "1.0.0-beta.5", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-exports/-/eslint-import-resolver-exports-1.0.0-beta.5.tgz", - "integrity": "sha512-o6t0w7muUpXr7MkUVzD5igQoDfAQvTmcPp8HEAJdNF8eOuAO+yn6I/TTyMxz9ecCwzX7e02vzlkHURoScUuidg==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "resolve.exports": "^2.0.0" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", - "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.16.1", - "resolve": "^2.0.0-next.6" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/resolve": { - "version": "2.0.0-next.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", - "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.2", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", - "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", - "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" }, "peerDependenciesMeta": { - "eslint": { + "jiti": { "optional": true } } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-es-x": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", - "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "node_modules/eslint-config-flat-gitignore": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-config-flat-gitignore/-/eslint-config-flat-gitignore-2.4.0.tgz", + "integrity": "sha512-JhYC+V7qEDiCDsbJcVP8fxNc62fk4+i91YLP/YvmVHlkaQ8Xo99olYN8MO5COdxl7onGlsrfZVONe5NYsZP1UA==", "dev": true, - "funding": [ - "https://github.com/sponsors/ota-meshi", - "https://opencollective.com/eslint" - ], "license": "MIT", - "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.11.0", - "eslint-compat-utils": "^0.5.1" + "@eslint/compat": "^2.1.0" }, - "engines": { - "node": "^14.18.0 || >=16.0.0" + "funding": { + "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "eslint": ">=8" + "eslint": "^9.5.0 || ^10.0.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "node_modules/eslint-plugin-antfu": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-antfu/-/eslint-plugin-antfu-3.2.3.tgz", + "integrity": "sha512-U2fnz/H0gFPxpuC7QpaHa0Jv2AgCZ5hunp36SOP/yWo8yFzgvMh8X4pZ4uN4IKoqtBhk7G3HuVa93Urf51+sZg==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.9", - "array.prototype.findlastindex": "^1.2.6", - "array.prototype.flat": "^1.3.3", - "array.prototype.flatmap": "^1.3.3", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "is-core-module": "^2.16.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" + "eslint": "*" } }, "node_modules/eslint-plugin-jsdoc": { - "version": "46.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.10.1.tgz", - "integrity": "sha512-x8wxIpv00Y50NyweDUpa+58ffgSAI5sqe+zcZh33xphD0AVh+1kqr1ombaTRb7Fhpove1zfUuujlX9DWWBP5ag==", + "version": "63.3.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.3.3.tgz", + "integrity": "sha512-xI4IeVRzRFA2DGHrPLIxF3U+oJHU3FE+P9Zb27fVs5dPHgfcpoAs0PyCbznVhK7pwR+9BPUztFeXSgpw/CL4Yg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.41.0", + "@es-joy/jsdoccomment": "~0.91.0", + "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", - "comment-parser": "1.4.1", - "debug": "^4.3.4", + "comment-parser": "1.4.7", + "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", - "esquery": "^1.5.0", - "is-builtin-module": "^3.2.1", - "semver": "^7.5.4", - "spdx-expression-parse": "^4.0.0" + "espree": "^11.2.0", + "esquery": "^1.7.0", + "html-entities": "^2.6.0", + "object-deep-merge": "^2.0.1", + "parse-imports-exports": "^0.2.4", + "semver": "^7.8.5", + "spdx-expression-parse": "^5.0.0", + "to-valid-identifier": "^1.0.0" }, "engines": { - "node": ">=16" + "node": "^22.13.0 || >=24" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-jsdoc/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, - "node_modules/eslint-plugin-n": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", - "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "eslint-plugin-es-x": "^7.5.0", - "get-tsconfig": "^4.7.0", - "globals": "^13.24.0", - "ignore": "^5.2.4", - "is-builtin-module": "^3.2.1", - "is-core-module": "^2.12.1", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", - "semver": "^7.5.3" - }, + "license": "Apache-2.0", "engines": { - "node": ">=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-plugin-n/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, - "license": "MIT", - "peer": true, + "license": "BSD-2-Clause", "dependencies": { - "type-fest": "^0.20.2" + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": ">=8" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-plugin-n/node_modules/semver": { + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -10514,132 +8871,64 @@ "node": ">=10" } }, - "node_modules/eslint-plugin-n/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/eslint-plugin-jsdoc/node_modules/spdx-expression-parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-5.0.0.tgz", + "integrity": "sha512-vngmw3Rgn+o2arXNbnZaj5UtOEBuWBfvaI+Wc8GFfykIhA5/vdK9/Sp/XkLv63dykz2rxKDvKEHupF5P0FORcQ==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/eslint-plugin-promise": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", - "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", + "node_modules/eslint-plugin-perfectionist": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.11.0.tgz", + "integrity": "sha512-kZV1otBcu4xT5R1p+0x1N1wRv4pS+OIbxrA47n5a1fTnN3MWbexOx5eQgbQhGyCNbpvF/rqrbnpkGjumHF+Y0Q==", "dev": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.68.0", + "natural-orderby": "^5.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": "^20.0.0 || >=22.0.0" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + "eslint": "^8.45.0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-vue": { - "version": "9.33.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz", - "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.10.0.tgz", + "integrity": "sha512-dL9x9rBHqqNcByWiLOHK6L0SB97V82/NC0cZRn9cXPjM7pCuWlpQQP9bFH4vjBv80ej1ZpzAkuD8zWH1o9bZbA==", "devOptional": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "globals": "^13.24.0", + "@eslint-community/eslint-utils": "^4.9.1", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", - "postcss-selector-parser": "^6.0.15", - "semver": "^7.6.3", - "vue-eslint-parser": "^9.4.3", - "xml-name-validator": "^4.0.0" + "postcss-selector-parser": "^7.1.4", + "semver": "^7.8.5", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": "^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-vue/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-vue/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-vue/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-vue/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-vue/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" + "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "vue-eslint-parser": "^10.3.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "@stylistic/eslint-plugin": { + "optional": true + }, + "@typescript-eslint/parser": { + "optional": true + } } }, "node_modules/eslint-plugin-vue/node_modules/semver": { @@ -10655,104 +8944,38 @@ "node": ">=10" } }, - "node_modules/eslint-plugin-vue/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "devOptional": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-vue/node_modules/vue-eslint-parser": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", - "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "devOptional": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "devOptional": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/eslint/node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -10770,80 +8993,37 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "devOptional": true, "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "devOptional": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -10851,19 +9031,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "devOptional": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -10882,19 +9049,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", @@ -10908,16 +9062,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -10931,7 +9075,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -10941,17 +9085,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/estree-util-is-identifier-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", @@ -11234,10 +9367,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.7.tgz", - "integrity": "sha512-a6Qh1RMCNbSrU1+sAyAAZH3rTe+OaWJbNZIq0S+ifZciUUOQtlVxBJwoTUE2bYhysmG/RYyI5WJFIKdBahJdrQ==", - "dev": true, + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.1.tgz", + "integrity": "sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==", "funding": [ { "type": "github", @@ -11245,9 +9377,13 @@ } ], "license": "MIT", - "peer": true, "dependencies": { - "strnum": "^1.0.5" + "@nodable/entities": "^3.0.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.2", + "xml-naming": "^0.3.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -11267,12 +9403,29 @@ "version": "1.20.3", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -11297,16 +9450,16 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "devOptional": true, "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-uri-to-path": { @@ -11381,18 +9534,17 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "devOptional": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -11565,17 +9717,11 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "devOptional": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -11595,42 +9741,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", - "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2", - "hasown": "^2.0.4", - "is-callable": "^1.2.7", - "is-document.all": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -11701,37 +9811,14 @@ "node": ">= 0.4" } }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", - "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", + "node_modules/git-hooks-list": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.2.1.tgz", + "integrity": "sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "url": "https://github.com/fisker/git-hooks-list?sponsor=1" } }, "node_modules/github-from-package": { @@ -11796,6 +9883,14 @@ "tslib": "2" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", @@ -11866,9 +9961,9 @@ } }, "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz", + "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", "dev": true, "license": "MIT", "engines": { @@ -11878,43 +9973,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/globby": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.4.tgz", + "integrity": "sha512-c8B/VNLmxRcmqqenRA9t+9IyOjf9+V6lTxPaUJLqOCONdQkWZ0ETYgX0qbtJqPsgCNusT9MZ5Jeidw8Eb9tn2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "is-path-inside": "^4.0.0", + "micromatch": "^4.0.8", + "slash": "^5.1.0", + "unicorn-magic": "^0.4.0" }, "engines": { - "node": ">= 0.4" + "node": ">=20" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/globby/node_modules/ignore": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", + "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", "dev": true, "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 4" } }, "node_modules/globjoin": { @@ -11942,13 +10030,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "devOptional": true, - "license": "MIT" - }, "node_modules/gridstack": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/gridstack/-/gridstack-13.2.0.tgz", @@ -11965,20 +10046,6 @@ ], "license": "MIT" }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -12001,23 +10068,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -12234,6 +10284,23 @@ "integrity": "sha512-ECg1JFG0YzehicQaogenlcs2qg6WsXQsxtnbr1i696u5tLUjtJdQAh0u2g0Q5YV45f263Ta1GnUJsc8WIfJf4Q==", "license": "MIT" }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, "node_modules/html-tags": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-5.1.0.tgz", @@ -12457,7 +10524,7 @@ "version": "5.1.9", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", - "devOptional": true, + "dev": true, "license": "MIT", "peer": true }, @@ -12465,7 +10532,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -12519,23 +10586,11 @@ "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "devOptional": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -12543,29 +10598,13 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true, - "license": "ISC" - }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } + "license": "ISC" + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" }, "node_modules/interpret": { "version": "3.1.1", @@ -12652,25 +10691,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -12678,110 +10698,12 @@ "dev": true, "license": "MIT" }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "license": "MIT" }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "semver": "^7.7.1" - } - }, - "node_modules/is-bun-module/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -12811,43 +10733,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-decimal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", @@ -12875,23 +10760,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-document.all": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", - "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -12902,23 +10770,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -13006,20 +10857,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -13037,20 +10874,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-network-error": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", @@ -13075,32 +10898,17 @@ "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-plain-obj": { @@ -13153,55 +10961,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-svg": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-6.1.0.tgz", @@ -13214,26 +10973,7 @@ "node": ">=20" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-typed-array": { @@ -13264,55 +11004,6 @@ ], "license": "MIT" }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-wsl": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", @@ -13385,21 +11076,6 @@ "node": ">= 10.13.0" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -13411,7 +11087,7 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -13431,14 +11107,13 @@ } }, "node_modules/jsdoc-type-pratt-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", - "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-8.0.0.tgz", + "integrity": "sha512-uQu/fXVqVaMg6gM8/E5G5+eygVcZ1NV0Z51CvqhNa2bDWxvHMl484ETr6vph4oPyC+KUcbP/w2W2pewfCiR9aQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { - "node": ">=12.0.0" + "node": ">=20.0.0" } }, "node_modules/jsesc": { @@ -13574,17 +11249,278 @@ "resolved": "https://registry.npmjs.org/libxmljs2/-/libxmljs2-0.37.0.tgz", "integrity": "sha512-Xb78V8GZouoZFrq8cCwx7+G3WYOcJG0xb3YUbweSyE4z2EIrQCZMr3Ye/dHn4mESs6YxUMeQeUZm5IXg+iLHog==", "dev": true, - "hasInstallScript": true, - "license": "MIT", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bindings": "~1.5.0", + "nan": "~2.22.2", + "node-gyp": "^11.2.0", + "prebuild-install": "^7.1.3" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "devOptional": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", "optional": true, - "dependencies": { - "bindings": "~1.5.0", - "nan": "~2.22.2", - "node-gyp": "^11.2.0", - "prebuild-install": "^7.1.3" - }, + "os": [ + "win32" + ], "engines": { - "node": ">=22" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lines-and-columns": { @@ -13646,13 +11582,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "devOptional": true, - "license": "MIT" - }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -14562,6 +12491,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -14633,16 +12575,19 @@ "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "devOptional": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.8" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -14651,6 +12596,7 @@ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "license": "MIT", + "optional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -14993,23 +12939,6 @@ "license": "MIT", "optional": true }, - "node_modules/napi-postinstall": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -15017,6 +12946,16 @@ "devOptional": true, "license": "MIT" }, + "node_modules/natural-orderby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz", + "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/nearley": { "version": "2.20.1", "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", @@ -15110,6 +13049,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, "license": "MIT", "optional": true, "peer": true @@ -15134,26 +13074,6 @@ "node": ">=10.5.0" } }, - "node_modules/node-exports-info": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", - "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/node-fetch": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", @@ -15362,6 +13282,13 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/object-deep-merge": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.1.tgz", + "integrity": "sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==", + "dev": true, + "license": "MIT" + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -15423,79 +13350,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/obug": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", @@ -15539,7 +13393,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -15598,26 +13452,6 @@ "dev": true, "license": "MIT" }, - "node_modules/own-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", - "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.4", - "get-intrinsic": "^1.3.0", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -15745,7 +13579,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -15796,6 +13630,16 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -15815,6 +13659,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -15857,16 +13708,6 @@ "node": ">=14.0.0" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -15928,16 +13769,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -15975,13 +13806,12 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -16241,20 +14071,6 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz", - "integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-modules-scope": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", @@ -16271,20 +14087,6 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz", - "integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-modules-values": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", @@ -16356,9 +14158,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", - "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz", + "integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -16738,7 +14540,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -16866,17 +14668,6 @@ "rc": "cli.js" } }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", @@ -16917,41 +14708,17 @@ "resolve": "^1.20.0" }, "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "dev": true, - "license": "Apache-2.0", - "peer": true - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 10.13.0" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -16972,28 +14739,6 @@ "node": ">=4" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/regexpu-core": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", @@ -17150,23 +14895,25 @@ "node": ">=0.10.0" } }, - "node_modules/requireindex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", - "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.5" - } - }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -17216,32 +14963,10 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", - "peer": true, "engines": { - "node": ">=10" + "node": ">=4" } }, "node_modules/ret": { @@ -17270,7 +14995,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -17283,45 +15008,6 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "devOptional": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "devOptional": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/ripemd160": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", @@ -17399,50 +15085,38 @@ "dev": true, "license": "MIT" }, - "node_modules/rollup": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", - "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", + "node_modules/rolldown": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.6.tgz", + "integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==", "devOptional": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.9" + "@oxc-project/types": "=0.147.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.1", - "@rollup/rollup-android-arm64": "4.63.1", - "@rollup/rollup-darwin-arm64": "4.63.1", - "@rollup/rollup-darwin-x64": "4.63.1", - "@rollup/rollup-freebsd-arm64": "4.63.1", - "@rollup/rollup-freebsd-x64": "4.63.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", - "@rollup/rollup-linux-arm-musleabihf": "4.63.1", - "@rollup/rollup-linux-arm64-gnu": "4.63.1", - "@rollup/rollup-linux-arm64-musl": "4.63.1", - "@rollup/rollup-linux-loong64-gnu": "4.63.1", - "@rollup/rollup-linux-loong64-musl": "4.63.1", - "@rollup/rollup-linux-ppc64-gnu": "4.63.1", - "@rollup/rollup-linux-ppc64-musl": "4.63.1", - "@rollup/rollup-linux-riscv64-gnu": "4.63.1", - "@rollup/rollup-linux-riscv64-musl": "4.63.1", - "@rollup/rollup-linux-s390x-gnu": "4.63.1", - "@rollup/rollup-linux-x64-gnu": "4.63.1", - "@rollup/rollup-linux-x64-musl": "4.63.1", - "@rollup/rollup-openbsd-x64": "4.63.1", - "@rollup/rollup-openharmony-arm64": "4.63.1", - "@rollup/rollup-win32-arm64-msvc": "4.63.1", - "@rollup/rollup-win32-ia32-msvc": "4.63.1", - "@rollup/rollup-win32-x64-gnu": "4.63.1", - "@rollup/rollup-win32-x64-msvc": "4.63.1", - "fsevents": "~2.3.2" + "@rolldown/binding-android-arm-eabi": "1.2.6", + "@rolldown/binding-android-arm64": "1.2.6", + "@rolldown/binding-darwin-arm64": "1.2.6", + "@rolldown/binding-darwin-x64": "1.2.6", + "@rolldown/binding-freebsd-x64": "1.2.6", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.6", + "@rolldown/binding-linux-arm64-gnu": "1.2.6", + "@rolldown/binding-linux-arm64-musl": "1.2.6", + "@rolldown/binding-linux-ppc64-gnu": "1.2.6", + "@rolldown/binding-linux-s390x-gnu": "1.2.6", + "@rolldown/binding-linux-x64-gnu": "1.2.6", + "@rolldown/binding-linux-x64-musl": "1.2.6", + "@rolldown/binding-openharmony-arm64": "1.2.6", + "@rolldown/binding-win32-arm64-msvc": "1.2.6", + "@rolldown/binding-win32-x64-msvc": "1.2.6" } }, "node_modules/rope-sequence": { @@ -17487,7 +15161,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -17507,27 +15181,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-array-concat": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", - "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "get-intrinsic": "^1.3.0", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -17549,24 +15202,6 @@ ], "license": "MIT" }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", @@ -17596,7 +15231,7 @@ "version": "1.103.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.103.1.tgz", "integrity": "sha512-9icZURbP51S6S0QGoyaeqk9uB06GNWxsFYWfH5RgpFgqK5FA8tJcM3AdVxrZEVJ7dz+L87nG95gBKf4VuaMHGw==", - "devOptional": true, + "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -17941,39 +15576,6 @@ "node": ">= 0.4" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -18206,13 +15808,16 @@ } }, "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/slice-ansi": { @@ -18302,6 +15907,48 @@ "node": ">= 14" } }, + "node_modules/sort-object-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz", + "integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/sort-package-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-4.0.0.tgz", + "integrity": "sha512-6aYOlYI9AWioZ+rzu+4zKLmoFqJP0/fHDxrd7X04yqEibikY+5YVF0EYlyGn4v6X2PJY7yAUWV7oeP+i5rOm/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-indent": "^7.0.2", + "detect-newline": "^4.0.1", + "git-hooks-list": "^4.1.1", + "is-plain-obj": "^4.1.0", + "semver": "^7.7.3", + "sort-object-keys": "^2.0.1", + "tinyglobby": "^0.2.15" + }, + "bin": { + "sort-package-json": "cli.js" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/sort-package-json/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/sortablejs": { "version": "1.14.0", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", @@ -18428,14 +16075,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -18461,21 +16100,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -18533,149 +16157,80 @@ "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", - "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", - "optional": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "ansi-regex": "^6.2.2" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", - "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", - "peer": true, + "optional": true, "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.2", - "es-object-atoms": "^1.1.2", - "has-property-descriptors": "^1.0.2", - "safe-regex-test": "^1.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", - "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.2" - }, + "optional": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT", - "peer": true, + "optional": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, "node_modules/stringify-entities": { @@ -18693,16 +16248,19 @@ } }, "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-ansi-cjs": { @@ -18720,28 +16278,26 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "peer": true, + "optional": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "devOptional": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, "license": "MIT", + "optional": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/striptags": { @@ -18751,10 +16307,9 @@ "license": "MIT" }, "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "dev": true, + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.2.tgz", + "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==", "funding": [ { "type": "github", @@ -18762,7 +16317,9 @@ } ], "license": "MIT", - "peer": true + "dependencies": { + "anynum": "^1.0.1" + } }, "node_modules/strtok3": { "version": "10.3.5", @@ -19012,21 +16569,6 @@ "stylelint": "^16.8.2 || ^17.0.0" } }, - "node_modules/stylelint-scss/node_modules/postcss-selector-parser": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz", - "integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/stylelint-use-logical": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/stylelint-use-logical/-/stylelint-use-logical-2.1.3.tgz", @@ -19040,65 +16582,6 @@ "stylelint": ">= 11 < 18" } }, - "node_modules/stylelint/node_modules/@csstools/selector-resolve-nested": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.1.tgz", - "integrity": "sha512-j3vdQu0XwLME5qOTWxm8cnmvsf423R2YL6DbKklCHZwkDm7UdKNu6RPlw4REIJhSlKBICY3B70/7QZdicLqZgg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.1.1" - } - }, - "node_modules/stylelint/node_modules/@csstools/selector-specificity": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.0.tgz", - "integrity": "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.1.1" - } - }, - "node_modules/stylelint/node_modules/ansi-regex": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", - "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/stylelint/node_modules/file-entry-cache": { "version": "11.1.5", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", @@ -19121,51 +16604,16 @@ "hookified": "^1.15.0" } }, - "node_modules/stylelint/node_modules/globby": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.4.tgz", - "integrity": "sha512-c8B/VNLmxRcmqqenRA9t+9IyOjf9+V6lTxPaUJLqOCONdQkWZ0ETYgX0qbtJqPsgCNusT9MZ5Jeidw8Eb9tn2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "fast-glob": "^3.3.3", - "ignore": "^7.0.5", - "is-path-inside": "^4.0.0", - "micromatch": "^4.0.8", - "slash": "^5.1.0", - "unicorn-magic": "^0.4.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stylelint/node_modules/ignore": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.7.tgz", - "integrity": "sha512-dML0wP6oak21rsNYCJpJB6O1BJIEwNpGrTw0URPfAk4hm0e3pRfCtzkfB6olBcXcVlU2rouCyz7lCyRB0OMVCA==", + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", + "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/stylelint/node_modules/is-path-inside": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stylelint/node_modules/postcss-safe-parser": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", @@ -19182,42 +16630,15 @@ }, { "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/stylelint/node_modules/postcss-selector-parser": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz", - "integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/stylelint/node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "dev": true, + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "engines": { - "node": ">=14.16" + "node": ">=18.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "postcss": "^8.4.31" } }, "node_modules/stylelint/node_modules/string-width": { @@ -19237,22 +16658,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stylelint/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/superjson": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", @@ -19266,16 +16671,18 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "devOptional": true, + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/supports-hyperlinks": { @@ -19363,6 +16770,16 @@ "node": ">=10.0.0" } }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/table/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -19385,6 +16802,19 @@ "node": ">=8" } }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tapable": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", @@ -19567,13 +16997,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "devOptional": true, - "license": "MIT" - }, "node_modules/thingies": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.1.tgz", @@ -19646,35 +17069,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tinyrainbow": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", @@ -19713,6 +17107,23 @@ "node": ">=8.0" } }, + "node_modules/to-valid-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", + "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/base62": "^1.0.0", + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -19769,16 +17180,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-loader": { @@ -19807,20 +17218,6 @@ } } }, - "node_modules/ts-loader/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/ts-loader/node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -19841,34 +17238,6 @@ "node": ">=18" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -20010,85 +17379,43 @@ "node": ">= 0.4" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "license": "MIT", + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "devOptional": true, + "license": "Apache-2.0", "peer": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.17" } }, - "node_modules/typed-array-length": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", - "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "node_modules/typescript-eslint": { + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.68.0.tgz", + "integrity": "sha512-MHy0Y0ynqeEbx/S45+i/bBssdy3X6KNBfmJAP35GrgtNxu2TQ5K5xsFDhAnmsq1jvpdoZOPG1LGtJo0HWqYCrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "call-bind": "^1.0.9", - "for-each": "^0.3.5", - "gopd": "^1.2.0", - "is-typed-array": "^1.1.15", - "possible-typed-array-names": "^1.1.0", - "reflect.getprototypeof": "^1.0.10" + "@typescript-eslint/eslint-plugin": "8.68.0", + "@typescript-eslint/parser": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0", + "@typescript-eslint/utils": "8.68.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "engines": { - "node": ">=14.17" + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-event-target": { @@ -20103,26 +17430,6 @@ "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", "license": "MIT" }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/undici-types": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", @@ -20376,100 +17683,37 @@ "esbuild": { "optional": true }, - "rolldown": { - "optional": true - }, - "rollup": { - "optional": true - }, - "unloader": { - "optional": true - }, - "vite": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/unplugin-utils": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", - "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", - "license": "MIT", - "dependencies": { - "pathe": "^2.0.3", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - } - }, - "node_modules/unplugin-utils/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/unplugin/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/unrs-resolver": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", - "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", - "dev": true, - "hasInstallScript": true, + "node_modules/unplugin-utils": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", + "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", "license": "MIT", - "peer": true, "dependencies": { - "napi-postinstall": "^0.3.4" + "pathe": "^2.0.3", + "picomatch": "^4.0.4" }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" + "engines": { + "node": ">=20.19.0" }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.12.2", - "@unrs/resolver-binding-android-arm64": "1.12.2", - "@unrs/resolver-binding-darwin-arm64": "1.12.2", - "@unrs/resolver-binding-darwin-x64": "1.12.2", - "@unrs/resolver-binding-freebsd-x64": "1.12.2", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", - "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", - "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", - "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", - "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-x64-musl": "1.12.2", - "@unrs/resolver-binding-openharmony-arm64": "1.12.2", - "@unrs/resolver-binding-wasm32-wasi": "1.12.2", - "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", - "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", - "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + "funding": { + "url": "https://github.com/sponsors/sxzz" } }, "node_modules/update-browserslist-db": { @@ -20635,18 +17879,17 @@ } }, "node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", "devOptional": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -20662,9 +17905,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -20677,13 +17921,16 @@ "@types/node": { "optional": true }, - "jiti": { + "@vitejs/devtools": { "optional": true }, - "less": { + "esbuild": { "optional": true }, - "lightningcss": { + "jiti": { + "optional": true + }, + "less": { "optional": true }, "sass": { @@ -20709,28 +17956,11 @@ } } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -20741,19 +17971,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vitest": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", @@ -20844,19 +18061,6 @@ } } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -20956,48 +18160,6 @@ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" } }, - "node_modules/vue-eslint-parser/node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/vue-eslint-parser/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/vue-eslint-parser/node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -21144,6 +18306,12 @@ "node": ">=14" } }, + "node_modules/webdav/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, "node_modules/webdav/node_modules/brace-expansion": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", @@ -21165,29 +18333,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/webdav/node_modules/fast-xml-parser": { - "version": "5.11.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.1.tgz", - "integrity": "sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "@nodable/entities": "^3.0.0", - "fast-xml-builder": "^1.2.0", - "is-unsafe": "^2.0.0", - "path-expression-matcher": "^1.6.2", - "strnum": "^2.4.2", - "xml-naming": "^0.3.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/webdav/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", @@ -21203,21 +18348,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/webdav/node_modules/strnum": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.2.tgz", - "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "anynum": "^1.0.1" - } - }, "node_modules/webpack": { "version": "5.110.2", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.110.2.tgz", @@ -21482,76 +18612,6 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/which-typed-array": { "version": "1.1.22", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", @@ -21647,6 +18707,17 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -21671,18 +18742,18 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", - "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "optional": true, - "engines": { - "node": ">=12" + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { @@ -21699,28 +18770,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -21792,13 +18846,13 @@ } }, "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "devOptional": true, "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/xml-naming": { diff --git a/package.json b/package.json index f7cb82ce..e2cd49ad 100644 --- a/package.json +++ b/package.json @@ -62,26 +62,22 @@ "@babel/preset-env": "^7.29.5", "@cyclonedx/cyclonedx-npm": "^6.0.1", "@eslint/config-helpers": "^0.7.0", - "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.39.1", "@nextcloud/browserslist-config": "^3.0.1", - "@nextcloud/eslint-config": "^8.4.1", + "@nextcloud/eslint-config": "^9.0.1", "@nextcloud/stylelint-config": "^3.2.2", "@nextcloud/webpack-vue-config": "^7.0.2", "@playwright/test": "^1.49.0", - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/parser": "^8.68.0", "@vue/compiler-sfc": "^3.5.18", "babel-loader": "^10.1.1", "css-loader": "~7.1.1", - "eslint": "^8.56.0", - "eslint-import-resolver-alias": "^1.1.2", - "eslint-plugin-vue": "^9.21.1", + "eslint": "^10.9.1", "node-polyfill-webpack-plugin": "4.0.0", "style-loader": "~4.0.0", "stylelint": "^17.9.1", "vitest": "^4.1.11", - "vue-eslint-parser": "^10.4.1", + "vue-eslint-parser": "^10.3.0", "vue-loader": "^17.4.2", "webpack": "^5.94.0", "webpack-cli": "^6.0.1" diff --git a/src/App.vue b/src/App.vue index c699a698..0f8fce09 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,5 +1,5 @@ @@ -199,14 +257,27 @@ import { showError } from '@nextcloud/dialogs' * @spec openspec/specs/admin-user-settings.md */ import { NcActionButton, NcActions, NcButton, NcChip, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue' +import ArrowDownIcon from 'vue-material-design-icons/ArrowDown.vue' import ArrowRightIcon from 'vue-material-design-icons/ArrowRight.vue' +import ArrowUpIcon from 'vue-material-design-icons/ArrowUp.vue' import CogIcon from 'vue-material-design-icons/Cog.vue' import LockOutline from 'vue-material-design-icons/LockOutline.vue' +import PlusIcon from 'vue-material-design-icons/Plus.vue' +import ColumnActions from '../components/ColumnActions.vue' import ProjectSettingsSidebar from '../components/ProjectSettingsSidebar.vue' import TaskCard from '../components/TaskCard.vue' +import ColumnEditDialog from '../dialogs/ColumnEditDialog.vue' +import ColumnRemoveDialog from '../dialogs/ColumnRemoveDialog.vue' import { useProjectsStore } from '../store/projects.js' +import { + buildMovePatch, + groupTasksByColumn, + orderPatchesForStep, + sortColumns, + swapColumnPatches, + wipState, +} from '../utils/columnHelpers.js' import { filterTasksByLabel, labelId, resolveTaskLabels, sortLabelsByTitle } from '../utils/labelHelpers.js' -import { BOARD_STATUSES, groupTasksByStatus } from '../utils/taskHelpers.js' export default { name: 'ProjectBoard', @@ -218,9 +289,15 @@ export default { NcChip, NcEmptyContent, NcLoadingIcon, + ArrowDownIcon, ArrowRightIcon, + ArrowUpIcon, CogIcon, + ColumnActions, + ColumnEditDialog, + ColumnRemoveDialog, LockOutline, + PlusIcon, TaskCard, }, @@ -241,8 +318,14 @@ export default { tasksLoading: false, /** @type {object|null} The task currently being dragged. */ draggingTask: null, - /** @type {string|null} The status column currently hovered during a drag. */ - dropTargetStatus: null, + /** @type {string|null} Id of the lane currently hovered during a drag. */ + dropTargetId: null, + /** @type {Array} The project's column objects. */ + boardColumns: [], + /** @type {object|null} The column being edited; `{}` while adding one. */ + editingColumn: null, + /** @type {object|null} The column being removed. */ + removingColumn: null, /** @type {Array} Every app-wide label, for the card chips and the filter. */ labels: [], /** @type {string|null} Id of the label the board is filtered by, null for all. */ @@ -273,35 +356,46 @@ export default { }, /** - * The board's columns, in display order. One column per task status — - * the status enum is the single source of truth for the board lanes. + * The board's lanes: the project's column objects in `order`. * - * @return {Array<{status: string, label: string}>} + * @return {Array} * - * @spec openspec/specs/kanban-board.md + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.1 */ columns() { - const labels = { - open: this.t('planninq', 'Open'), - in_progress: this.t('planninq', 'In Progress'), - blocked: this.t('planninq', 'Blocked'), - done: this.t('planninq', 'Done'), - cancelled: this.t('planninq', 'Cancelled'), - } - return BOARD_STATUSES.map((status) => ({ status, label: labels[status] })) + return sortColumns(this.boardColumns) }, /** - * Tasks grouped by their status. Every column key is always present so - * empty columns render gracefully; a task with an unknown status falls - * back to the "open" lane. + * Tasks grouped by the column they reference, in `columnOrder`. Every + * lane is a key; a column-less task is in the backlog, not here. * - * @return {{[status: string]: Array}} + * @return {{[columnId: string]: Array}} * - * @spec openspec/specs/kanban-board.md + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.1 */ - tasksByStatus() { - return groupTasksByStatus(this.visibleTasks, BOARD_STATUSES) + tasksByColumn() { + return groupTasksByColumn(this.visibleTasks, this.columns) + }, + + /** + * Whether the current user may manage the columns: the project owner or + * an admin. The server enforces the same rule (ColumnOwnerGuardListener). + * + * @return {boolean} + * + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-4.1 + */ + isOwner() { + const user = getCurrentUser() + return !!user && (user.isAdmin === true || this.project?.owner === user.uid) + }, + + /** + * @spec exclude Display helper: the `order` a new column gets. + */ + nextColumnOrder() { + return this.columns.reduce((max, column) => Math.max(max, Number(column.order) || 0), -1) + 1 }, /** @@ -380,6 +474,7 @@ export default { this.highlightTaskId = taskId } + await this.loadColumns(id) await this.loadTasks(id) await this.loadLabels() }, @@ -409,6 +504,21 @@ export default { } }, + /** + * Load the project's columns, the board's lanes. + * + * @param {string} projectId Parent project UUID + * @return {Promise} + * + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.1 + */ + async loadColumns(projectId) { + if (!projectId || this.accessDenied) { + return + } + this.boardColumns = await this.projectsStore.fetchColumns(projectId) + }, + /** * Load every app-wide label, so cards can render their chips and the * filter can offer them. @@ -474,45 +584,46 @@ export default { */ onDragEnd() { this.draggingTask = null - this.dropTargetStatus = null + this.dropTargetId = null }, /** - * @param {string} status The hovered column's status. - * @spec exclude Drag glue — marks the hovered column as the drop target. + * @param {string} columnId The hovered lane's column id. + * @spec exclude Drag glue — marks the hovered lane as the drop target. */ - onDragOver(status) { - this.dropTargetStatus = status + onDragOver(columnId) { + this.dropTargetId = columnId }, /** - * @param {string} status The column being left. + * @param {string} columnId The lane being left. * @spec exclude Drag glue — clears the drop-target highlight on leave. */ - onDragLeave(status) { - if (this.dropTargetStatus === status) { - this.dropTargetStatus = null + onDragLeave(columnId) { + if (this.dropTargetId === columnId) { + this.dropTargetId = null } }, /** - * Drop a dragged task into a column, changing its status. - * - * Applies the move optimistically (the card jumps to the new column - * immediately) and persists it via the RBAC-scoped store action; on a - * failed write the task reverts to its original status and an error toast - * is shown. + * Drop the dragged card into a lane: at the bottom, or in front of the + * card it was dropped on. The drop is never refused, over a WIP limit + * too. * - * @param {string} newStatus The target column's status + * @param {object} column The target column. + * @param {object|null} [beforeTask] The card it was dropped on. * @return {Promise} * - * @spec openspec/specs/kanban-board.md + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.2 */ - async onDrop(newStatus) { + async onDrop(column, beforeTask = null) { const task = this.draggingTask - this.dropTargetStatus = null + this.dropTargetId = null this.draggingTask = null - await this.applyStatusMove(task, newStatus) + if (!task || (beforeTask && beforeTask.id === task.id)) { + return + } + await this.applyMove(task, column, beforeTask) }, /** @@ -535,61 +646,137 @@ export default { }, /** - * The board columns other than `currentStatus` — the valid keyboard - * "Move to…" targets for a card in that column. + * The lanes other than `column`: the keyboard "Move to" targets. * - * @param {string} currentStatus The card's current status - * @return {Array<{status: string, label: string}>} + * @param {object} column The card's current column. + * @return {Array} * - * @spec exclude Display helper — filters the column list for the move menu. + * @spec exclude Display helper — filters the lane list for the move menu. */ - otherColumns(currentStatus) { - return this.columns.filter((column) => column.status !== currentStatus) + otherColumns(column) { + return this.columns.filter((other) => other.id !== column.id) }, /** - * Keyboard-operable status change: the accessible equivalent of a - * drag-and-drop move. Delegates to the exact same optimistic-update + - * rollback path the drag handler uses, so behaviour and RBAC are - * identical between the two. + * @param {object} column A lane. + * @return {Array} Its cards, in order. + * @spec exclude Display helper — the cards of one lane. + */ + tasksOfColumn(column) { + return this.tasksByColumn[column.id] || [] + }, + + /** + * @param {object} column A lane. + * @return {{text: string, over: boolean}} + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.3 + */ + wipFor(column) { + return wipState(this.tasksOfColumn(column).length, column.wipLimit) + }, + + /** + * Keyboard move to another lane: the same path as a drop. * - * @param {object} task The task to move - * @param {string} newStatus The target column's status + * @param {object} task The task to move. + * @param {object} column The target column. * @return {Promise} * - * @spec openspec/specs/kanban-board.md + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.2 */ - async moveTask(task, newStatus) { - await this.applyStatusMove(task, newStatus) + async moveTask(task, column) { + await this.applyMove(task, column, null) }, /** - * Optimistically move a task to `newStatus` and persist it via the - * RBAC-scoped store action; revert + toast on a failed write. Shared by - * both the drag-and-drop drop handler and the keyboard "Move to…" menu. + * Optimistically put a task in a lane and PATCH its `column`, + * `columnOrder` and the lane's mapped status; revert and toast when the + * write fails. The server stamps `completedAt` when the status becomes + * done (TaskCompletionListener). + * + * @param {object} task The task to move. + * @param {object} column The target column. + * @param {object|null} beforeTask The card to land in front of. + * @return {Promise} * - * @param {object} task The task to move (no-op when null/same status) - * @param {string} newStatus The target column's status + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.2 + */ + async applyMove(task, column, beforeTask) { + const lane = this.tasksOfColumn(column).filter((card) => card.id !== task.id) + const patch = buildMovePatch(column, lane, beforeTask) + await this.patchTasks([{ id: task.id, ...patch }]) + }, + + /** + * Move a card one step up (-1) or down (+1) in its lane. + * + * @param {object} task The card. + * @param {object} column Its lane. + * @param {number} direction -1 or +1. * @return {Promise} * - * @spec openspec/specs/kanban-board.md + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.4 */ - async applyStatusMove(task, newStatus) { - if (!task || task.status === newStatus) { + async stepCard(task, column, direction) { + await this.patchTasks(orderPatchesForStep(this.tasksOfColumn(column), task, direction)) + }, + + /** + * Apply task patches optimistically and persist them; revert all on a failure. + * + * @param {Array} patches Each `{ id, ...fields }`. + * @return {Promise} + * + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.2 + */ + async patchTasks(patches) { + if (!patches.length) { return } + const previous = this.tasks + const byId = new Map(patches.map((patch) => [patch.id, patch])) + this.tasks = this.tasks.map((task) => byId.has(task.id) ? { ...task, ...byId.get(task.id) } : task) + for (const { id, ...fields } of patches) { + const updated = await this.projectsStore.updateTask(id, fields) + if (!updated) { + this.tasks = previous + showError(this.t('planninq', 'Could not move the task. Please try again.')) + return + } + } + }, - const previousStatus = task.status - - // Optimistic update. - this.tasks = this.tasks.map((existing) => existing.id === task.id ? { ...existing, status: newStatus } : existing) - - const updated = await this.projectsStore.updateTaskStatus(task.id, newStatus) - if (!updated) { - // Revert on failure. - this.tasks = this.tasks.map((existing) => existing.id === task.id ? { ...existing, status: previousStatus } : existing) - showError(this.t('planninq', 'Could not move the task. Please try again.')) + /** + * Move a column one place left or right. + * + * @param {object} column The column. + * @param {number} direction -1 or +1. + * @return {Promise} + * + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-4.1 + */ + async moveColumn(column, direction) { + for (const patch of swapColumnPatches(this.columns, column, direction)) { + if (!(await this.projectsStore.saveColumn(patch))) { + showError(this.t('planninq', 'Could not save the column. Please try again.')) + break + } } + await this.loadColumns(this.project.id) + }, + + /** + * Reload lanes and cards after a column was added, edited or removed. + * + * @return {Promise} + * + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-4.1 + */ + async onColumnsChanged() { + this.editingColumn = null + this.removingColumn = null + await this.loadColumns(this.project.id) + await this.loadTasks(this.project.id) }, /** @@ -607,6 +794,7 @@ export default { on: { close: () => this.closeSidebar?.(), archived: () => this.$router.push({ name: 'Projects' }), + columnsChanged: () => this.onColumnsChanged(), deleted: () => this.$router.push({ name: 'Projects' }), }, }) @@ -723,8 +911,13 @@ export default { .kanban-column__header { display: flex; align-items: center; - justify-content: space-between; + gap: 8px; padding: 4px 8px 8px; + border-top: 3px solid transparent; +} + +.kanban-column__title { + flex: 1; } .kanban-column__title { @@ -743,6 +936,19 @@ export default { padding: 1px 8px; } +/* Over the WIP limit: warning colour AND the words "over limit", so colour + is not the only signal (WCAG 1.4.1). The drop is never refused. */ +.kanban-column__header--over .kanban-column__count { + color: var(--color-warning-text); + background: var(--color-warning-hover, var(--color-background-hover)); + border: 1px solid var(--color-warning); +} + +.project-board__add-column { + flex: 0 0 auto; + padding-top: 4px; +} + .kanban-column__body { display: flex; flex-direction: column; diff --git a/tests/e2e/board-columns.spec.ts b/tests/e2e/board-columns.spec.ts new file mode 100644 index 00000000..052ab6f4 --- /dev/null +++ b/tests/e2e/board-columns.spec.ts @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * E2E coverage for the board-columns capability (boards-configurable-columns): + * the board runs on the project's own column objects. + * + * @e2e board-columns::a-project-with-a-review-column + * @e2e board-columns::add-and-rename-a-column + * @e2e board-columns::remove-a-column-with-cards + * @e2e board-columns::drop-a-card-on-done + * @e2e board-columns::go-over-the-limit + * @e2e board-columns::reorder-with-the-keyboard + * + * The API-level scenarios (a member cannot change columns, a move through the + * API is stamped, the admin changed the defaults, an upgraded board) carry a + * reason-bearing `@e2e exclude` in the spec: PHPUnit covers each on the real + * listener, service or repair step. + * + * The fixture seed (fixtures/seed.ts) creates one project owned by the admin, + * with its four default columns and tasks placed in To do and In progress. + */ + +import { expect, test } from '@playwright/test' +import { openFixtureProjectBoard } from './nav.ts' + +async function openBoard(page) { + await openFixtureProjectBoard(page) + const board = page.locator('[data-cy="kanban-board"]') + await expect(board).toHaveCount(1) + return board +} + +function lane(board, title: string) { + return board.locator(`section.kanban-column[data-column="${title}"]`) +} + +test.describe('Board columns', () => { + // @e2e board-columns::a-project-with-a-review-column + test('the board shows the project\'s own columns in order', async ({ page }) => { + const board = await openBoard(page) + const titles = await board.locator('section.kanban-column h3').allTextContents() + expect(titles.map((title) => title.trim()).slice(0, 4)).toEqual(['To do', 'In progress', 'Review', 'Done']) + }) + + // @e2e board-columns::add-and-rename-a-column + // @e2e board-columns::remove-a-column-with-cards + test('the owner adds, renames and removes a column', async ({ page }) => { + const board = await openBoard(page) + await page.getByTestId('add-column').click() + await page.getByTestId('column-name').locator('input').fill('Waiting for applicant') + await page.getByTestId('column-save').click() + await expect(lane(board, 'Waiting for applicant')).toHaveCount(1) + + await lane(board, 'Waiting for applicant').getByTestId('column-actions').click() + await page.getByRole('menuitem', { name: 'Edit column' }).click() + await page.getByTestId('column-name').locator('input').fill('Waiting') + await page.getByTestId('column-save').click() + const titles = await board.locator('section.kanban-column h3').allTextContents() + expect(titles.map((title) => title.trim()).at(-1)).toBe('Waiting') + + await lane(board, 'Waiting').getByTestId('column-actions').click() + await page.getByRole('menuitem', { name: 'Remove column' }).click() + await page.getByTestId('column-remove-confirm').click() + await expect(lane(board, 'Waiting')).toHaveCount(0) + }) + + // @e2e board-columns::drop-a-card-on-done + test('moving a card to Done finishes it, also after a reload', async ({ page }) => { + const board = await openBoard(page) + const card = lane(board, 'In progress').getByTestId('task-card').first() + const title = await card.getAttribute('aria-label') + await card.getByRole('button', { name: 'Move task to another column' }).click() + await page.getByRole('menuitem', { name: 'Done' }).click() + await expect(lane(board, 'Done').locator(`[aria-label="${title}"]`)).toHaveCount(1) + + await page.reload() + const reloaded = await openBoard(page) + const done = lane(reloaded, 'Done').locator(`[aria-label="${title}"]`) + await expect(done).toHaveCount(1) + + // Put the card back, so the due-date badge tests still find it in progress. + await done.getByRole('button', { name: 'Move task to another column' }).click() + await page.getByRole('menuitem', { name: 'In progress' }).click() + await expect(lane(reloaded, 'In progress').locator(`[aria-label="${title}"]`)).toHaveCount(1) + }) + + // @e2e board-columns::go-over-the-limit + test('a lane over its WIP limit says so and still takes the card', async ({ page }) => { + const board = await openBoard(page) + const review = lane(board, 'Review') + // Review has a WIP limit of 2 in the seed; fill it to three. + for (let i = 0; i < 3; i++) { + const card = lane(board, 'To do').getByTestId('task-card').first() + if (await card.count() === 0) { + break + } + await card.getByRole('button', { name: 'Move task to another column' }).click() + await page.getByRole('menuitem', { name: 'Review' }).click() + } + const count = await review.getByTestId('task-card').count() + test.skip(count <= 2, 'the seed holds fewer than three movable cards') + await expect(review.getByTestId('column-count')).toContainText(`${count} / 2`) + await expect(review.getByTestId('column-count')).toContainText('over limit') + + // Put the cards back in To do for the other tests. + while (await review.getByTestId('task-card').count() > 0) { + await review.getByTestId('task-card').first().getByRole('button', { name: 'Move task to another column' }).click() + await page.getByRole('menuitem', { name: 'To do' }).click() + } + }) + + // @e2e board-columns::reorder-with-the-keyboard + test('Move up puts a card above its neighbour, also after a reload', async ({ page }) => { + const board = await openBoard(page) + const cards = lane(board, 'To do').getByTestId('task-card') + test.skip(await cards.count() < 2, 'the seed holds fewer than two cards in To do') + const second = await cards.nth(1).getAttribute('aria-label') + await cards.nth(1).getByRole('button', { name: 'Move task to another column' }).click() + await page.getByRole('menuitem', { name: 'Move up' }).click() + await expect(cards.first()).toHaveAttribute('aria-label', second as string) + + await page.reload() + const reloaded = await openBoard(page) + await expect(lane(reloaded, 'To do').getByTestId('task-card').first()).toHaveAttribute('aria-label', second as string) + }) +}) diff --git a/tests/e2e/fixtures/seed.ts b/tests/e2e/fixtures/seed.ts index 1787a2f2..d4093f44 100644 --- a/tests/e2e/fixtures/seed.ts +++ b/tests/e2e/fixtures/seed.ts @@ -285,11 +285,14 @@ export async function seedFixtures( } // ── Columns (default set) ─────────────────────────────────────────── + // ProjectController::create makes these on the server + // (boards-configurable-columns); the seed only fills a gap, so an + // older instance still gets a board. Titles match the server's. const wantedColumns = [ - { title: 'To Do', order: 0, wipLimit: null, type: 'active' }, - { title: 'In Progress', order: 1, wipLimit: 3, type: 'active' }, - { title: 'Review', order: 2, wipLimit: 2, type: 'active' }, - { title: 'Done', order: 3, wipLimit: null, type: 'done' }, + { title: 'To do', order: 0, wipLimit: null, type: 'active', status: 'open' }, + { title: 'In progress', order: 1, wipLimit: 3, type: 'active', status: 'in_progress' }, + { title: 'Review', order: 2, wipLimit: 2, type: 'active', status: 'in_progress' }, + { title: 'Done', order: 3, wipLimit: null, type: 'done', status: 'done' }, ] const existingColumns = await list( 'column', @@ -359,7 +362,7 @@ export async function seedFixtures( title, status: 'open', project: projectId, - column: columnIdByTitle['To Do'], + column: columnIdByTitle['To do'], assignedTo: username, ...extra, }, @@ -372,21 +375,21 @@ export async function seedFixtures( priority: 'high', dueDate: isoDate(1), labels: labelId ? [labelId] : [], - column: columnIdByTitle['In Progress'] ?? columnIdByTitle['To Do'], + column: columnIdByTitle['In progress'] ?? columnIdByTitle['To do'], columnOrder: 0, }) // Overdue: due yesterday → "Overdue" badge. await ensureTask(FIXTURE.tasks.overdue, { priority: 'urgent', dueDate: isoDate(-1), - column: columnIdByTitle['To Do'], + column: columnIdByTitle['To do'], columnOrder: 1, }) // Normal: far-future due date → no warning badge. await ensureTask(FIXTURE.tasks.normal, { priority: 'normal', dueDate: isoDate(60), - column: columnIdByTitle['To Do'], + column: columnIdByTitle['To do'], columnOrder: 2, }) diff --git a/tests/unit/AppInfo/BoardColumnWiringTest.php b/tests/unit/AppInfo/BoardColumnWiringTest.php new file mode 100644 index 00000000..6a6e6dbc --- /dev/null +++ b/tests/unit/AppInfo/BoardColumnWiringTest.php @@ -0,0 +1,79 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\OpenRegister\Event { + if (class_exists(ObjectEventSubscription::class) === false) { + /** + * Records subscriptions; same static signature as OpenRegister's + * lib/Event/ObjectEventSubscription.php subscribe(). + */ + class ObjectEventSubscription { + /** @var array> */ + public static array $calls = []; + public static function subscribe( + \OCP\EventDispatcher\IEventDispatcher $dispatcher, + string $event, + string $listener, + ?array $registers = null, + ?array $schemas = null, + ): void { + self::$calls[] = ['event' => $event, 'listener' => $listener, 'registers' => $registers, 'schemas' => $schemas]; + } + } + } +} + +namespace OCA\Planninq\Tests\Unit\AppInfo { + + use OCA\OpenRegister\Event\ObjectEventSubscription; + use OCA\Planninq\AppInfo\Application; + use OCP\EventDispatcher\IEventDispatcher; + use PHPUnit\Framework\TestCase; + + class BoardColumnWiringTest extends TestCase { + + public function testBootSubscribesBothBoardColumnListeners(): void { + ObjectEventSubscription::$calls = []; + $app = (new \ReflectionClass(Application::class))->newInstanceWithoutConstructor(); + $method = new \ReflectionMethod(Application::class, 'registerBoardColumnListeners'); + $method->invoke($app, $this->createMock(originalClassName: IEventDispatcher::class)); + + $byListener = []; + foreach (ObjectEventSubscription::$calls as $call) { + self::assertTrue(class_exists($call['listener']), $call['listener'] . ' must exist'); + self::assertTrue(class_exists($call['event']), $call['event'] . ' must exist'); + $byListener[$call['listener']][] = [substr($call['event'], strrpos($call['event'], '\\') + 1), $call['schemas']]; + } + + self::assertSame( + [['ObjectCreatingEvent', ['task']], ['ObjectUpdatingEvent', ['task']]], + $byListener['OCA\\Planninq\\Listener\\TaskCompletionListener'] + ); + self::assertSame( + [['ObjectCreatingEvent', ['column']], ['ObjectUpdatingEvent', ['column']], ['ObjectDeletingEvent', ['column']]], + $byListener['OCA\\Planninq\\Listener\\ColumnOwnerGuardListener'] + ); + + $boot = (string)file_get_contents(__DIR__ . '/../../../lib/AppInfo/Application.php'); + self::assertStringContainsString('$this->registerBoardColumnListeners(dispatcher: $dispatcher);', $boot, 'boot() calls the registration'); + }//end testBootSubscribesBothBoardColumnListeners() + }//end class +} diff --git a/tests/unit/Controller/ProjectControllerTest.php b/tests/unit/Controller/ProjectControllerTest.php index a4a769e8..ca2010ed 100644 --- a/tests/unit/Controller/ProjectControllerTest.php +++ b/tests/unit/Controller/ProjectControllerTest.php @@ -31,6 +31,7 @@ require_once __DIR__ . '/../Support/ObjectServiceDouble.php'; use OCA\Planninq\Controller\ProjectController; +use OCA\Planninq\Service\BoardColumnService; use OCA\Planninq\Service\SettingsService; use OCA\Planninq\Tests\Unit\Support\ObjectServiceDouble; use OCP\AppFramework\Http; @@ -83,6 +84,8 @@ class ProjectControllerTest extends TestCase { */ private LoggerInterface&MockObject $logger; + private BoardColumnService&MockObject $boardColumns; + /** * The controller under test. * @@ -103,6 +106,7 @@ protected function setUp(): void { $this->userSession = $this->createMock(originalClassName: IUserSession::class); $this->container = $this->createMock(originalClassName: ContainerInterface::class); $this->logger = $this->createMock(originalClassName: LoggerInterface::class); + $this->boardColumns = $this->createMock(originalClassName: BoardColumnService::class); $this->controller = new ProjectController( request: $this->request, @@ -110,6 +114,7 @@ protected function setUp(): void { userSession: $this->userSession, container: $this->container, logger: $this->logger, + boardColumns: $this->boardColumns, ); }//end setUp() @@ -271,6 +276,13 @@ public function jsonSerialize(): array { $this->request->method('getParams')->willReturn(['title' => 'My Project']); + // Scenario "The admin changed the defaults": the new project gets its + // columns on the server, in the same request that creates it. + $this->boardColumns->expects($this->once()) + ->method('createDefaultColumns') + ->with('new-uuid') + ->willReturn([]); + $result = $this->controller->create(); self::assertSame(expected: Http::STATUS_CREATED, actual: $result->getStatus()); diff --git a/tests/unit/Listener/ColumnOwnerGuardListenerTest.php b/tests/unit/Listener/ColumnOwnerGuardListenerTest.php new file mode 100644 index 00000000..3f33e34e --- /dev/null +++ b/tests/unit/Listener/ColumnOwnerGuardListenerTest.php @@ -0,0 +1,124 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\Planninq\Tests\Unit\Listener; + +require_once __DIR__ . '/../Support/ObjectServiceDouble.php'; +require_once __DIR__ . '/../Support/InMemoryObjectService.php'; +require_once __DIR__ . '/../Support/MembershipFixture.php'; + +use OCA\OpenRegister\Db\ObjectEntity; +use OCA\OpenRegister\Event\ObjectCreatingEvent; +use OCA\OpenRegister\Event\ObjectDeletingEvent; +use OCA\OpenRegister\Event\ObjectUpdatingEvent; +use OCA\Planninq\Listener\ColumnOwnerGuardListener; +use OCA\Planninq\Tests\Unit\Support\InMemoryObjectService; +use OCA\Planninq\Tests\Unit\Support\MembershipFixture; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserSession; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class ColumnOwnerGuardListenerTest extends TestCase { + use MembershipFixture; + + private const COLUMN = ['title' => 'Review', 'project' => 'proj-a', 'order' => 2, 'members' => ['bob', 'carol']]; + + protected function setUp(): void { + parent::setUp(); + $this->objects = new InMemoryObjectService(); + $this->objects->seed('project', 'proj-a', ['title' => 'A', 'members' => ['bob'], 'owner' => 'carol']); + }//end setUp() + + private function listener(string $actor, bool $isAdmin = false): ColumnOwnerGuardListener { + $user = $this->createMock(originalClassName: IUser::class); + $user->method('getUID')->willReturn($actor); + $session = $this->createMock(originalClassName: IUserSession::class); + $session->method('getUser')->willReturn($actor === '' ? null : $user); + $groups = $this->createMock(originalClassName: IGroupManager::class); + $groups->method('isAdmin')->willReturnCallback(static fn (string $uid): bool => ($isAdmin === true && $uid === $actor)); + + return new ColumnOwnerGuardListener( + membership: $this->membershipService(), + scopeResolver: $this->scopeResolver(), + userSession: $session, + groupManager: $groups, + logger: $this->createMock(originalClassName: LoggerInterface::class) + ); + }//end listener() + + private function column(array $data, string $schema = 'column'): ObjectEntity { + return InMemoryObjectService::entity(uuid: 'col-1', data: $data, register: '1', schema: $this->schemaId(slug: $schema)); + }//end column() + + /** + * Scenario "A member cannot change columns": an update through the API is refused. + */ + public function testAMemberWhoIsNotTheOwnerCannotRenameAColumn(): void { + $event = new ObjectUpdatingEvent($this->column(data: ['title' => 'Waiting'] + self::COLUMN), $this->column(data: self::COLUMN)); + + $this->listener(actor: 'bob')->handle($event); + + self::assertTrue($event->isPropagationStopped()); + self::assertSame(ColumnOwnerGuardListener::ERROR_CODE, $event->getErrors()['code']); + }//end testAMemberWhoIsNotTheOwnerCannotRenameAColumn() + + public function testAMemberCannotAddOrRemoveAColumn(): void { + $create = new ObjectCreatingEvent($this->column(data: self::COLUMN)); + $this->listener(actor: 'bob')->handle($create); + self::assertTrue($create->isPropagationStopped(), 'create'); + + $delete = new ObjectDeletingEvent($this->column(data: self::COLUMN)); + $this->listener(actor: 'bob')->handle($delete); + self::assertTrue($delete->isPropagationStopped(), 'delete'); + }//end testAMemberCannotAddOrRemoveAColumn() + + /** + * Scenario "Add and rename a column": the owner may. + */ + public function testTheOwnerAndAnAdminMayChangeColumns(): void { + foreach ([['carol', false], ['root', true]] as [$actor, $admin]) { + $event = new ObjectUpdatingEvent($this->column(data: ['title' => 'Waiting'] + self::COLUMN), $this->column(data: self::COLUMN)); + $this->listener(actor: $actor, isAdmin: $admin)->handle($event); + self::assertFalse($event->isPropagationStopped(), $actor); + } + }//end testTheOwnerAndAnAdminMayChangeColumns() + + /** + * A member leaving the project rewrites every column's members list as that member. + */ + public function testAMembersOnlyRewriteIsAllowed(): void { + $event = new ObjectUpdatingEvent($this->column(data: ['members' => ['carol']] + self::COLUMN), $this->column(data: self::COLUMN)); + + $this->listener(actor: 'bob')->handle($event); + + self::assertFalse($event->isPropagationStopped()); + }//end testAMembersOnlyRewriteIsAllowed() + + public function testNoSessionAndOtherSchemasPass(): void { + $event = new ObjectUpdatingEvent($this->column(data: ['title' => 'Waiting'] + self::COLUMN), $this->column(data: self::COLUMN)); + $this->listener(actor: '')->handle($event); + self::assertFalse($event->isPropagationStopped(), 'a repair step or occ has no session'); + + $task = new ObjectUpdatingEvent($this->column(data: ['title' => 'x', 'project' => 'proj-a'], schema: 'task'), $this->column(data: ['title' => 'y', 'project' => 'proj-a'], schema: 'task')); + $this->listener(actor: 'bob')->handle($task); + self::assertFalse($task->isPropagationStopped(), 'members still move cards'); + }//end testNoSessionAndOtherSchemasPass() +}//end class diff --git a/tests/unit/Listener/TaskCompletionListenerTest.php b/tests/unit/Listener/TaskCompletionListenerTest.php new file mode 100644 index 00000000..434d56b0 --- /dev/null +++ b/tests/unit/Listener/TaskCompletionListenerTest.php @@ -0,0 +1,116 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\Planninq\Tests\Unit\Listener; + +require_once __DIR__ . '/../Support/ObjectServiceDouble.php'; +require_once __DIR__ . '/../Support/InMemoryObjectService.php'; +require_once __DIR__ . '/../Support/MembershipFixture.php'; +require_once __DIR__ . '/../Support/RegisterSchemaValidation.php'; + +use OCA\OpenRegister\Db\ObjectEntity; +use OCA\OpenRegister\Event\ObjectCreatingEvent; +use OCA\OpenRegister\Event\ObjectUpdatingEvent; +use OCA\Planninq\Listener\TaskCompletionListener; +use OCA\Planninq\Tests\Unit\Support\InMemoryObjectService; +use OCA\Planninq\Tests\Unit\Support\MembershipFixture; +use OCA\Planninq\Tests\Unit\Support\RegisterSchemaValidation; +use OCP\AppFramework\Utility\ITimeFactory; +use PHPUnit\Framework\TestCase; + +class TaskCompletionListenerTest extends TestCase { + use MembershipFixture; + use RegisterSchemaValidation; + + private const NOW = '2026-09-28T21:30:00+00:00'; + + private function listener(): TaskCompletionListener { + $time = $this->createMock(originalClassName: ITimeFactory::class); + $time->method('getDateTime')->willReturn(new \DateTime(self::NOW)); + + return new TaskCompletionListener(scopeResolver: $this->scopeResolver(), timeFactory: $time); + }//end listener() + + private function task(array $data, string $schema = 'task'): ObjectEntity { + return InMemoryObjectService::entity(uuid: 't1', data: $data, register: '1', schema: $this->schemaId(slug: $schema)); + }//end task() + + /** + * Scenario "A move through the API is stamped too": status becomes done on update. + */ + public function testEnteringDoneStampsCompletedAt(): void { + $event = new ObjectUpdatingEvent( + $this->task(data: ['title' => 'T', 'status' => 'done']), + $this->task(data: ['title' => 'T', 'status' => 'in_progress']) + ); + + $this->listener()->handle($event); + + self::assertSame(['completedAt' => self::NOW], $event->getModifiedData()); + self::assertSame([], $this->registerSchemaErrors(slug: 'task', payload: ['title' => 'T', 'status' => 'done'] + $event->getModifiedData())); + }//end testEnteringDoneStampsCompletedAt() + + public function testLeavingDoneClearsCompletedAt(): void { + $event = new ObjectUpdatingEvent( + $this->task(data: ['title' => 'T', 'status' => 'open', 'completedAt' => self::NOW]), + $this->task(data: ['title' => 'T', 'status' => 'done', 'completedAt' => self::NOW]) + ); + + $this->listener()->handle($event); + + self::assertSame(['completedAt' => null], $event->getModifiedData()); + self::assertSame([], $this->registerSchemaErrors(slug: 'task', payload: ['title' => 'T', 'status' => 'open'] + $event->getModifiedData())); + }//end testLeavingDoneClearsCompletedAt() + + public function testAnUnrelatedUpdateWritesNothing(): void { + $event = new ObjectUpdatingEvent( + $this->task(data: ['title' => 'Renamed', 'status' => 'done', 'completedAt' => '2026-09-01T10:00:00+00:00']), + $this->task(data: ['title' => 'T', 'status' => 'done', 'completedAt' => '2026-09-01T10:00:00+00:00']) + ); + + $this->listener()->handle($event); + + self::assertSame([], $event->getModifiedData(), 'a task that stays done keeps its finish time'); + }//end testAnUnrelatedUpdateWritesNothing() + + public function testACreateWithStatusDoneIsStamped(): void { + $event = new ObjectCreatingEvent($this->task(data: ['title' => 'T', 'status' => 'done'])); + + $this->listener()->handle($event); + + self::assertSame(['completedAt' => self::NOW], $event->getModifiedData()); + }//end testACreateWithStatusDoneIsStamped() + + public function testItMergesWithWhatOtherListenersSet(): void { + $event = new ObjectCreatingEvent($this->task(data: ['title' => 'T', 'status' => 'done'])); + $event->setModifiedData(['members' => ['alice']]); + + $this->listener()->handle($event); + + self::assertSame(['members' => ['alice'], 'completedAt' => self::NOW], $event->getModifiedData()); + }//end testItMergesWithWhatOtherListenersSet() + + public function testOtherSchemasAreIgnored(): void { + $event = new ObjectCreatingEvent($this->task(data: ['title' => 'T', 'status' => 'done'], schema: 'column')); + + $this->listener()->handle($event); + + self::assertSame([], $event->getModifiedData()); + }//end testOtherSchemasAreIgnored() +}//end class diff --git a/tests/unit/Repair/AssignBoardColumnsTest.php b/tests/unit/Repair/AssignBoardColumnsTest.php new file mode 100644 index 00000000..cb406764 --- /dev/null +++ b/tests/unit/Repair/AssignBoardColumnsTest.php @@ -0,0 +1,92 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\Planninq\Tests\Unit\Repair; + +require_once __DIR__ . '/../Support/ObjectServiceDouble.php'; +require_once __DIR__ . '/../Support/InMemoryObjectService.php'; +require_once __DIR__ . '/../Support/MembershipFixture.php'; + +use OCA\Planninq\Repair\AssignBoardColumns; +use OCA\Planninq\Service\BoardColumnService; +use OCA\Planninq\Service\SettingsService; +use OCA\Planninq\Tests\Unit\Support\InMemoryObjectService; +use OCA\Planninq\Tests\Unit\Support\MembershipFixture; +use OCP\App\IAppManager; +use OCP\Migration\IOutput; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class AssignBoardColumnsTest extends TestCase { + use MembershipFixture; + + protected function setUp(): void { + parent::setUp(); + $this->objects = new InMemoryObjectService(); + $this->objects->seed('project', 'proj-a', ['title' => 'A', 'members' => ['bob'], 'owner' => 'alice']); + $this->objects->seed('column', 'col-todo', ['title' => 'To do', 'project' => 'proj-a', 'order' => 0, 'type' => 'active', 'status' => 'open']); + $this->objects->seed('column', 'col-done', ['title' => 'Done', 'project' => 'proj-a', 'order' => 1, 'type' => 'done', 'status' => 'done']); + $this->objects->seed('task', 'ta', ['title' => 'TA', 'status' => 'open', 'project' => 'proj-a']); + $this->objects->seed('task', 'tb', ['title' => 'TB', 'status' => 'done', 'project' => 'proj-a']); + $this->objects->seed('task', 'tc', ['title' => 'TC', 'status' => 'open', 'project' => 'proj-a', 'column' => 'col-done']); + }//end setUp() + + private function step(bool $openRegisterInstalled = true): AssignBoardColumns { + $membership = $this->membershipService(openRegisterInstalled: $openRegisterInstalled); + $settings = $this->createMock(originalClassName: SettingsService::class); + $settings->method('getAdminSettings')->willReturn(['default_columns' => '["To do","Done"]']); + + return new AssignBoardColumns( + columns: new BoardColumnService( + container: $this->container(), + membership: $membership, + settings: $settings, + logger: $this->createMock(originalClassName: LoggerInterface::class), + appManager: $this->openRegisterInstalled(installed: $openRegisterInstalled) + ), + membership: $membership, + logger: $this->createMock(originalClassName: LoggerInterface::class) + ); + }//end step() + + public function testRunPlacesColumnLessTasksAndLeavesPlacedOnesAlone(): void { + $output = $this->createMock(originalClassName: IOutput::class); + $output->expects($this->once())->method('info')->with(self::stringContains('Placed 2 task(s)')); + + $this->step()->run($output); + + self::assertSame('col-todo', $this->objects->rows['task']['ta']['column']); + self::assertSame('col-done', $this->objects->rows['task']['tb']['column']); + self::assertSame('col-done', $this->objects->rows['task']['tc']['column'], 'a task that has a column keeps it'); + }//end testRunPlacesColumnLessTasksAndLeavesPlacedOnesAlone() + + public function testWithoutOpenRegisterItWarnsAndWritesNothing(): void { + $output = $this->createMock(originalClassName: IOutput::class); + $output->expects($this->once())->method('warning'); + + $this->step(openRegisterInstalled: false)->run($output); + + self::assertSame([], $this->objects->saves); + }//end testWithoutOpenRegisterItWarnsAndWritesNothing() + private function openRegisterInstalled(bool $installed = true): IAppManager { + $appManager = $this->createMock(originalClassName: IAppManager::class); + $appManager->method('isInstalled')->willReturnCallback(static fn (string $app): bool => ($app === 'openregister' && $installed === true)); + return $appManager; + }//end openRegisterInstalled() +}//end class diff --git a/tests/unit/Service/BoardColumnServiceTest.php b/tests/unit/Service/BoardColumnServiceTest.php new file mode 100644 index 00000000..239d37be --- /dev/null +++ b/tests/unit/Service/BoardColumnServiceTest.php @@ -0,0 +1,169 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\Planninq\Tests\Unit\Service; + +require_once __DIR__ . '/../Support/ObjectServiceDouble.php'; +require_once __DIR__ . '/../Support/InMemoryObjectService.php'; +require_once __DIR__ . '/../Support/MembershipFixture.php'; +require_once __DIR__ . '/../Support/RegisterSchemaValidation.php'; + +use OCA\Planninq\Service\BoardColumnService; +use OCA\Planninq\Service\SettingsService; +use OCA\Planninq\Tests\Unit\Support\InMemoryObjectService; +use OCA\Planninq\Tests\Unit\Support\MembershipFixture; +use OCP\App\IAppManager; +use OCA\Planninq\Tests\Unit\Support\RegisterSchemaValidation; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class BoardColumnServiceTest extends TestCase { + use MembershipFixture; + use RegisterSchemaValidation; + + private const PROJECT = '11111111-1111-4111-8111-111111111111'; + + protected function setUp(): void { + parent::setUp(); + // Assigns a UUID to a create, as OpenRegister does. + $this->objects = new class extends InMemoryObjectService { + private int $next = 0; + public function saveObject( + array|object $object, + ?array $extend = [], + mixed $register = null, + mixed $schema = null, + ?string $uuid = null, + bool $_rbac = true, + bool $_multitenancy = true, + bool $silent = false, + bool $_validation = true, + ): ?object { + if ($uuid === null) { + $this->next++; + $uuid = sprintf('00000000-0000-4000-8000-%012d', $this->next); + } + return parent::saveObject($object, $extend, $register, $schema, $uuid, $_rbac, $_multitenancy, $silent, $_validation); + } + }; + $this->objects->seed('project', self::PROJECT, ['title' => 'Vergunningen', 'owner' => 'carol', 'members' => ['carol']]); + }//end setUp() + + private function service(string $defaultColumns = '["Intake","Work","Closed"]'): BoardColumnService { + $settings = $this->createMock(originalClassName: SettingsService::class); + $settings->method('getAdminSettings')->willReturn(['default_columns' => $defaultColumns]); + + return new BoardColumnService( + container: $this->container(), + membership: $this->membershipService(), + settings: $settings, + logger: $this->createMock(originalClassName: LoggerInterface::class), + appManager: $this->openRegisterInstalled() + ); + }//end service() + + /** + * Scenario "The admin changed the defaults": Intake, Work and Closed, with Closed the done column. + */ + public function testAdminDefaultsBecomeColumnsWithTheLastAsDone(): void { + $saved = $this->service()->createDefaultColumns(projectId: self::PROJECT); + + self::assertSame(['Intake', 'Work', 'Closed'], array_column($saved, 'title')); + self::assertSame(['active', 'active', 'done'], array_column($saved, 'type')); + self::assertSame(['open', 'in_progress', 'done'], array_column($saved, 'status')); + self::assertSame([0, 1, 2], array_column($saved, 'order')); + self::assertCount(3, $this->objects->rows['column']); + }//end testAdminDefaultsBecomeColumnsWithTheLastAsDone() + + public function testEmptySettingFallsBackToFourColumns(): void { + $saved = $this->service(defaultColumns: '[]')->createDefaultColumns(projectId: self::PROJECT); + + self::assertSame(['To do', 'In progress', 'Review', 'Done'], array_column($saved, 'title')); + self::assertSame('done', $saved[3]['type']); + }//end testEmptySettingFallsBackToFourColumns() + + /** + * The exact payload written into OpenRegister validates against the real column schema. + */ + public function testEveryColumnPayloadValidatesAgainstTheRegisterSchema(): void { + $this->service()->createDefaultColumns(projectId: self::PROJECT); + + foreach ($this->objects->saves as $save) { + self::assertSame('column', $save['schema']); + self::assertSame([], $this->registerSchemaErrors(slug: 'column', payload: $save['object'])); + } + }//end testEveryColumnPayloadValidatesAgainstTheRegisterSchema() + + /** + * Control: the validator refuses what the register refuses, so a green above means something. + */ + public function testTheValidatorRefusesAColumnTheRegisterRefuses(): void { + self::assertNotSame([], $this->registerSchemaErrors(slug: 'column', payload: ['title' => 'x', 'project' => self::PROJECT, 'order' => 0, 'type' => 'weird'])); + self::assertNotSame([], $this->registerSchemaErrors(slug: 'column', payload: ['title' => 'x', 'project' => self::PROJECT, 'order' => 0, 'status' => 'finished'])); + }//end testTheValidatorRefusesAColumnTheRegisterRefuses() + + /** + * Scenario "An upgraded board": every status lands in the matching column, and a rerun writes nothing. + */ + public function testAssignColumnsPlacesTasksByStatusAndIsIdempotent(): void { + $statuses = ['open', 'in_progress', 'blocked', 'done', 'cancelled']; + foreach ($statuses as $i => $status) { + $this->objects->seed('task', 'task-' . $status, ['title' => 'T' . $i, 'status' => $status, 'project' => self::PROJECT]); + } + + $first = $this->service()->assignColumns(projectId: self::PROJECT); + + self::assertSame(['created' => 3, 'assigned' => 4], $first); + $byTitle = []; + foreach ($this->objects->rows['column'] as $id => $column) { + $byTitle[$column['title']] = $id; + } + + $tasks = $this->objects->rows['task']; + self::assertSame($byTitle['Intake'], $tasks['task-open']['column']); + self::assertSame($byTitle['Work'], $tasks['task-in_progress']['column']); + self::assertSame($byTitle['Intake'], $tasks['task-blocked']['column'], 'no blocked column: the first active one'); + self::assertSame($byTitle['Closed'], $tasks['task-done']['column']); + self::assertArrayNotHasKey('column', $tasks['task-cancelled'], 'a cancelled task stays in the backlog'); + + foreach ($this->objects->saves as $save) { + if ($save['schema'] === 'task') { + self::assertSame([], $this->registerSchemaErrors(slug: 'task', payload: $save['object'])); + } + } + + $savesBefore = count($this->objects->saves); + $second = $this->service()->assignColumns(projectId: self::PROJECT); + self::assertSame(['created' => 0, 'assigned' => 0], $second); + self::assertCount($savesBefore, $this->objects->saves, 'the second run writes nothing'); + }//end testAssignColumnsPlacesTasksByStatusAndIsIdempotent() + + public function testAssignAllWalksEveryProject(): void { + $this->objects->seed('task', 'task-1', ['title' => 'A', 'status' => 'open', 'project' => self::PROJECT]); + + $totals = $this->service()->assignAll(); + + self::assertSame(['projects' => 1, 'created' => 3, 'assigned' => 1], $totals); + }//end testAssignAllWalksEveryProject() + private function openRegisterInstalled(bool $installed = true): IAppManager { + $appManager = $this->createMock(originalClassName: IAppManager::class); + $appManager->method('isInstalled')->willReturnCallback(static fn (string $app): bool => ($app === 'openregister' && $installed === true)); + return $appManager; + }//end openRegisterInstalled() +}//end class diff --git a/tests/unit/Service/SettingsServiceTest.php b/tests/unit/Service/SettingsServiceTest.php index a58f77b3..933e23a6 100644 --- a/tests/unit/Service/SettingsServiceTest.php +++ b/tests/unit/Service/SettingsServiceTest.php @@ -151,7 +151,7 @@ function (string $appId, string $key, string $default = '') use (&$args): string $columns = json_decode($result['default_columns'], true); self::assertIsArray(actual: $columns); - self::assertContains(needle: 'To Do', haystack: $columns); + self::assertContains(needle: 'To do', haystack: $columns); self::assertContains(needle: 'Done', haystack: $columns); self::assertSame(expected: 'all', actual: $result['allow_project_creation']); diff --git a/tests/unit/Support/RegisterSchemaValidation.php b/tests/unit/Support/RegisterSchemaValidation.php new file mode 100644 index 00000000..1deb5293 --- /dev/null +++ b/tests/unit/Support/RegisterSchemaValidation.php @@ -0,0 +1,109 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\Planninq\Tests\Unit\Support; + +use Opis\JsonSchema\Validator; + +trait RegisterSchemaValidation { + + /** + * The validation errors for a payload, empty when it is valid. + * + * @param string $slug The schema slug in lib/Settings/planninq_register.json. + * @param array $payload The payload that would be written. + * + * @return array + */ + protected function registerSchemaErrors(string $slug, array $payload): array { + $register = json_decode((string)file_get_contents(__DIR__ . '/../../../lib/Settings/planninq_register.json'), true); + $schema = $register['components']['schemas'][$slug]; + + $properties = []; + foreach ($schema['properties'] as $name => $property) { + $properties[$name] = self::prepareProperty(property: $property); + } + + $jsonSchema = [ + 'type' => 'object', + 'required' => ($schema['required'] ?? []), + 'properties' => $properties, + ]; + + $validator = new Validator(); + $result = $validator->validate( + json_decode((string)json_encode($payload)), + json_decode((string)json_encode($jsonSchema)) + ); + if ($result->isValid() === true) { + return []; + } + + $formatter = new \Opis\JsonSchema\Errors\ErrorFormatter(); + $errors = []; + foreach ($formatter->format($result->error(), true) as $path => $messages) { + $errors[] = $path . ': ' . implode('; ', (array)$messages); + } + + return $errors; + }//end registerSchemaErrors() + + /** + * Rewrite one register property the way OpenRegister does before validating. + * + * @param array $property The property as declared. + * + * @return array + */ + private static function prepareProperty(array $property): array { + $out = []; + foreach ($property as $key => $value) { + if ($key === 'visible' || $key === 'nullable' || str_starts_with((string)$key, 'x-') === true) { + continue; + } + + if ($key === '$ref' && is_string($value) === true && str_starts_with($value, '#') === false) { + continue; + } + + if ($key === 'items' && is_array($value) === true) { + $value = self::prepareProperty(property: $value); + } + + $out[$key] = $value; + } + + if (($property['nullable'] ?? false) === true && isset($out['type']) === true) { + $out['type'] = array_values(array_unique(array_merge((array)$out['type'], ['null']))); + if (isset($out['enum']) === true) { + $out['enum'][] = null; + } + } + + return $out; + }//end prepareProperty() +}//end trait diff --git a/tests/vitest/boardColumns.spec.js b/tests/vitest/boardColumns.spec.js new file mode 100644 index 00000000..d37742a8 --- /dev/null +++ b/tests/vitest/boardColumns.spec.js @@ -0,0 +1,174 @@ +/** + * Vitest unit tests for the board-column helpers (boards-configurable-columns). + * + * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.1 + */ +import Ajv from 'ajv' +import addFormats from 'ajv-formats' +import { readFileSync } from 'node:fs' +import { describe, expect, it } from 'vitest' +import { + buildMovePatch, + canRemoveColumn, + columnPayload, + groupTasksByColumn, + orderPatchesForStep, + sortColumns, + swapColumnPatches, + wipState, +} from '../../src/utils/columnHelpers.js' + +const columns = [ + { id: 'done', title: 'Done', order: 3, type: 'done', status: 'done' }, + { id: 'todo', title: 'To do', order: 0, type: 'active', status: 'open' }, + { id: 'doing', title: 'In progress', order: 1, type: 'active', status: 'in_progress' }, + { id: 'review', title: 'Review', order: 2, type: 'active', wipLimit: 2 }, +] + +describe('sortColumns', () => { + it('orders lanes by their order field (scenario: a project with a Review column)', () => { + expect(sortColumns(columns).map((c) => c.title)).toEqual(['To do', 'In progress', 'Review', 'Done']) + }) +}) + +describe('groupTasksByColumn', () => { + it('places each task in the column it references, sorted by columnOrder', () => { + const grouped = groupTasksByColumn([ + { id: 'b', column: 'todo', columnOrder: 2000 }, + { id: 'a', column: 'todo', columnOrder: 1000 }, + { id: 'c', column: 'done', columnOrder: 0 }, + ], columns) + expect(Object.keys(grouped)).toEqual(['todo', 'doing', 'review', 'done']) + expect(grouped.todo.map((t) => t.id)).toEqual(['a', 'b']) + expect(grouped.done.map((t) => t.id)).toEqual(['c']) + expect(grouped.doing).toEqual([]) + }) + + it('leaves column-less tasks off the board: they are the backlog', () => { + const grouped = groupTasksByColumn([{ id: 'x', column: null }, { id: 'y' }], columns) + expect(Object.values(grouped).flat()).toEqual([]) + }) + + it('puts a task whose column no longer exists in the first lane, so no card is lost', () => { + const grouped = groupTasksByColumn([{ id: 'x', column: 'gone' }], columns) + expect(grouped.todo.map((t) => t.id)).toEqual(['x']) + }) +}) + +describe('buildMovePatch', () => { + it('drops a card at the bottom of a done lane with status done (scenario: drop a card on Done)', () => { + const lane = [{ id: 'c', columnOrder: 4000 }] + expect(buildMovePatch(columns[0], lane)).toEqual({ column: 'done', columnOrder: 5000, status: 'done' }) + }) + + it('gives the lane its mapped status, and no status when the lane maps none', () => { + expect(buildMovePatch(columns[2], [])).toEqual({ column: 'doing', columnOrder: 1000, status: 'in_progress' }) + expect(buildMovePatch(columns[3], [])).toEqual({ column: 'review', columnOrder: 1000 }) + }) + + it('a done lane without a mapped status still finishes the task', () => { + expect(buildMovePatch({ id: 'd2', type: 'done' }, []).status).toBe('done') + }) + + it('drops before a card by taking the midpoint', () => { + const lane = [{ id: 'a', columnOrder: 1000 }, { id: 'b', columnOrder: 2000 }] + expect(buildMovePatch(columns[1], lane, lane[1]).columnOrder).toBe(1500) + expect(buildMovePatch(columns[1], lane, lane[0]).columnOrder).toBe(500) + }) +}) + +describe('orderPatchesForStep', () => { + it('moves B above A with one write (scenario: reorder with the keyboard)', () => { + const lane = [{ id: 'a', columnOrder: 1000 }, { id: 'b', columnOrder: 2000 }, { id: 'c', columnOrder: 3000 }] + expect(orderPatchesForStep(lane, lane[1], -1)).toEqual([{ id: 'b', columnOrder: 0 }]) + expect(orderPatchesForStep(lane, lane[0], 1)).toEqual([{ id: 'a', columnOrder: 2500 }]) + }) + + it('renumbers the lane when no integer fits between the neighbours', () => { + const lane = [{ id: 'a', columnOrder: 1 }, { id: 'b', columnOrder: 2 }, { id: 'c', columnOrder: 3 }] + expect(orderPatchesForStep(lane, lane[2], -1)).toEqual([ + { id: 'a', columnOrder: 1000 }, + { id: 'c', columnOrder: 2000 }, + { id: 'b', columnOrder: 3000 }, + ]) + }) + + it('does nothing at the edge of the lane', () => { + const lane = [{ id: 'a', columnOrder: 1000 }] + expect(orderPatchesForStep(lane, lane[0], -1)).toEqual([]) + }) +}) + +describe('wipState', () => { + it('reads count over limit and warns past it (scenario: go over the limit)', () => { + expect(wipState(4, 3)).toEqual({ text: '4 / 3', over: true }) + expect(wipState(3, 3)).toEqual({ text: '3 / 3', over: false }) + }) + + it('shows only the count when the lane has no limit', () => { + expect(wipState(4, null)).toEqual({ text: '4', over: false }) + expect(wipState(4, 0)).toEqual({ text: '4', over: false }) + }) +}) + +describe('canRemoveColumn', () => { + it('refuses to remove the last done column', () => { + expect(canRemoveColumn(columns, columns[0])).toBe(false) + expect(canRemoveColumn([...columns, { id: 'done2', type: 'done' }], columns[0])).toBe(true) + expect(canRemoveColumn(columns, columns[3])).toBe(true) + }) +}) + +describe('swapColumnPatches', () => { + it('moves a column one place left or right by swapping orders', () => { + expect(swapColumnPatches(columns, columns[3], -1)).toEqual([ + { id: 'review', order: 1 }, + { id: 'doing', order: 2 }, + ]) + expect(swapColumnPatches(columns, columns[1], -1)).toEqual([]) + }) +}) + +describe('columnPayload', () => { + // The payload is validated against the real `column` schema of + // lib/Settings/planninq_register.json, prepared the way OpenRegister + // prepares it (slug `$ref` dropped, `nullable` admits null). + const register = JSON.parse(readFileSync(new URL('../../lib/Settings/planninq_register.json', import.meta.url), 'utf8')) + const schema = register.components.schemas.column + const properties = {} + for (const [name, property] of Object.entries(schema.properties)) { + const { $ref, visible, nullable, ...rest } = property + for (const key of Object.keys(rest)) { + if (key.startsWith('x-')) { + delete rest[key] + } + } + if (nullable) { + rest.type = [rest.type, 'null'] + if (rest.enum) { + rest.enum = [...rest.enum, null] + } + } + properties[name] = rest + } + const ajv = new Ajv({ strict: false }) + addFormats(ajv) + const validate = ajv.compile({ type: 'object', required: schema.required, properties }) + const project = '11111111-1111-4111-8111-111111111111' + + it('writes a valid column for a new lane (scenario: add and rename a column)', () => { + const payload = { ...columnPayload({ title: ' Waiting ', wipLimit: '', color: '#aabbcc' }), project, order: 4 } + expect(payload).toMatchObject({ title: 'Waiting', wipLimit: null, status: null, type: 'active', color: '#AABBCC' }) + expect(validate(payload), JSON.stringify(validate.errors)).toBe(true) + }) + + it('writes a valid done column with a limit', () => { + const payload = { ...columnPayload({ title: 'Closed', wipLimit: '3', done: true }), project, order: 2 } + expect(payload).toMatchObject({ wipLimit: 3, status: 'done', type: 'done' }) + expect(validate(payload), JSON.stringify(validate.errors)).toBe(true) + }) + + it('control: the validator refuses a status the schema does not know', () => { + expect(validate({ title: 'x', project, order: 0, status: 'finished' })).toBe(false) + }) +}) From 4806ed34342ec1ed667cb1f007f761df9b86155d Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 28 Sep 2026 22:36:25 +0200 Subject: [PATCH 155/289] feat(backlog): members work the backlog: add, rank, sort, filter and plan tasks onto the board (#702) * feat(backlog): a working backlog with create, rank, sort, filter and moves to and from the board backlog-list: the placeholder page becomes the list of the project's tasks in no board column. New task field, Move up and Move down and drag to rank, sort by rank, priority, due date or created, a priority filter and a Cancelled switch in the query string, Move to board with a column choice, and Move to backlog in the board card menu. Strings in all 37 catalogues, plus the three board strings no catalogue carried. * chore(openspec): archive backlog-list, four backlog rows built --- l10n/be.js | 16 +- l10n/be.json | 16 +- l10n/bg.js | 16 +- l10n/bg.json | 16 +- l10n/bs.js | 16 +- l10n/bs.json | 16 +- l10n/ca.js | 16 +- l10n/ca.json | 16 +- l10n/cs.js | 16 +- l10n/cs.json | 16 +- l10n/da.js | 16 +- l10n/da.json | 16 +- l10n/de.js | 16 +- l10n/de.json | 16 +- l10n/el.js | 16 +- l10n/el.json | 16 +- l10n/en.js | 16 +- l10n/en.json | 16 +- l10n/es.js | 16 +- l10n/es.json | 16 +- l10n/et.js | 16 +- l10n/et.json | 16 +- l10n/fi.js | 16 +- l10n/fi.json | 16 +- l10n/fr.js | 16 +- l10n/fr.json | 16 +- l10n/ga.js | 16 +- l10n/ga.json | 16 +- l10n/hr.js | 16 +- l10n/hr.json | 16 +- l10n/hu.js | 16 +- l10n/hu.json | 16 +- l10n/is.js | 16 +- l10n/is.json | 16 +- l10n/it.js | 16 +- l10n/it.json | 16 +- l10n/lb.js | 16 +- l10n/lb.json | 16 +- l10n/lt.js | 16 +- l10n/lt.json | 16 +- l10n/lv.js | 16 +- l10n/lv.json | 16 +- l10n/mk.js | 16 +- l10n/mk.json | 16 +- l10n/mt.js | 16 +- l10n/mt.json | 16 +- l10n/nb.js | 16 +- l10n/nb.json | 16 +- l10n/nl.js | 16 +- l10n/nl.json | 16 +- l10n/pl.js | 16 +- l10n/pl.json | 16 +- l10n/pt.js | 16 +- l10n/pt.json | 16 +- l10n/rm.js | 16 +- l10n/rm.json | 16 +- l10n/ro.js | 16 +- l10n/ro.json | 16 +- l10n/ru.js | 16 +- l10n/ru.json | 16 +- l10n/sk.js | 16 +- l10n/sk.json | 16 +- l10n/sl.js | 16 +- l10n/sl.json | 16 +- l10n/sq.js | 16 +- l10n/sq.json | 16 +- l10n/sr.js | 16 +- l10n/sr.json | 16 +- l10n/sv.js | 16 +- l10n/sv.json | 16 +- l10n/tr.js | 16 +- l10n/tr.json | 16 +- l10n/uk.js | 16 +- l10n/uk.json | 16 +- .../2026-09-28-backlog-list}/design.md | 16 + .../2026-09-28-backlog-list}/proposal.md | 0 .../specs/backlog/spec.md | 2 + .../archive/2026-09-28-backlog-list/tasks.md | 22 + openspec/changes/backlog-list/tasks.md | 22 - openspec/parity/capabilities.json | 62 ++- openspec/specs/backlog/spec.md | 67 +++ openspec/specs/projects.md | 12 +- src/store/projects.js | 30 ++ src/utils/backlogHelpers.js | 122 +++++ src/utils/columnHelpers.js | 34 +- src/views/ProjectBacklog.vue | 469 +++++++++++++++++- src/views/ProjectBoard.vue | 24 + tests/e2e/backlog.spec.ts | 95 ++++ tests/vitest/backlog.spec.js | 126 +++++ 89 files changed, 2130 insertions(+), 157 deletions(-) rename openspec/changes/{backlog-list => archive/2026-09-28-backlog-list}/design.md (75%) rename openspec/changes/{backlog-list => archive/2026-09-28-backlog-list}/proposal.md (100%) rename openspec/changes/{backlog-list => archive/2026-09-28-backlog-list}/specs/backlog/spec.md (94%) create mode 100644 openspec/changes/archive/2026-09-28-backlog-list/tasks.md delete mode 100644 openspec/changes/backlog-list/tasks.md create mode 100644 openspec/specs/backlog/spec.md create mode 100644 src/utils/backlogHelpers.js create mode 100644 tests/e2e/backlog.spec.ts create mode 100644 tests/vitest/backlog.spec.js diff --git a/l10n/be.js b/l10n/be.js index 66676939..6f6758de 100644 --- a/l10n/be.js +++ b/l10n/be.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Апошні слупок выкананых нельга выдаліць.", "WIP limit": "Ліміт WIP", "over limit": "вышэй за ліміт", - "The status a card gets when it is moved into this column.": "Статус, які атрымлівае картка, калі яе перамяшчаюць у гэты слупок." + "The status a card gets when it is moved into this column.": "Статус, які атрымлівае картка, калі яе перамяшчаюць у гэты слупок.", + "All priorities": "Усе прыярытэты", + "Could not create the task. Please try again.": "Не ўдалося стварыць задачу. Паспрабуйце яшчэ раз.", + "Created": "Створана", + "Move to backlog": "У беклог", + "Move to board": "На дошку", + "New task": "Новая задача", + "Nothing in the backlog": "У беклогу нічога няма", + "Rank": "Ранг", + "Sort by": "Сартаваць па", + "Sort by rank to reorder": "Сартуйце па рангу, каб змяніць парадак", + "Task actions": "Дзеянні з задачай", + "All labels": "Усе меткі", + "Filter tasks by label": "Фільтраваць задачы па метцы", + "View timeline": "Праглядзець часовую шкалу" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/be.json b/l10n/be.json index a44c7369..31cdb272 100644 --- a/l10n/be.json +++ b/l10n/be.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Апошні слупок выкананых нельга выдаліць.", "WIP limit": "Ліміт WIP", "over limit": "вышэй за ліміт", - "The status a card gets when it is moved into this column.": "Статус, які атрымлівае картка, калі яе перамяшчаюць у гэты слупок." + "The status a card gets when it is moved into this column.": "Статус, які атрымлівае картка, калі яе перамяшчаюць у гэты слупок.", + "All priorities": "Усе прыярытэты", + "Could not create the task. Please try again.": "Не ўдалося стварыць задачу. Паспрабуйце яшчэ раз.", + "Created": "Створана", + "Move to backlog": "У беклог", + "Move to board": "На дошку", + "New task": "Новая задача", + "Nothing in the backlog": "У беклогу нічога няма", + "Rank": "Ранг", + "Sort by": "Сартаваць па", + "Sort by rank to reorder": "Сартуйце па рангу, каб змяніць парадак", + "Task actions": "Дзеянні з задачай", + "All labels": "Усе меткі", + "Filter tasks by label": "Фільтраваць задачы па метцы", + "View timeline": "Праглядзець часовую шкалу" }, "plurals": {} } diff --git a/l10n/bg.js b/l10n/bg.js index 4903dc28..7cd57f15 100644 --- a/l10n/bg.js +++ b/l10n/bg.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Последната колона за завършени не може да бъде премахната.", "WIP limit": "WIP лимит", "over limit": "над лимита", - "The status a card gets when it is moved into this column.": "Статусът, който картата получава, когато бъде преместена в тази колона." + "The status a card gets when it is moved into this column.": "Статусът, който картата получава, когато бъде преместена в тази колона.", + "All priorities": "Всички приоритети", + "Could not create the task. Please try again.": "Задачата не можа да бъде създадена. Опитайте отново.", + "Created": "Създадена", + "Move to backlog": "Към беклога", + "Move to board": "Към таблото", + "New task": "Нова задача", + "Nothing in the backlog": "Няма нищо в беклога", + "Rank": "Ранг", + "Sort by": "Сортиране по", + "Sort by rank to reorder": "Сортирайте по ранг, за да пренаредите", + "Task actions": "Действия със задачата", + "All labels": "Всички етикети", + "Filter tasks by label": "Филтриране на задачите по етикет", + "View timeline": "Преглед на времевата линия" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/bg.json b/l10n/bg.json index 31b1f2c6..04731566 100644 --- a/l10n/bg.json +++ b/l10n/bg.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Последната колона за завършени не може да бъде премахната.", "WIP limit": "WIP лимит", "over limit": "над лимита", - "The status a card gets when it is moved into this column.": "Статусът, който картата получава, когато бъде преместена в тази колона." + "The status a card gets when it is moved into this column.": "Статусът, който картата получава, когато бъде преместена в тази колона.", + "All priorities": "Всички приоритети", + "Could not create the task. Please try again.": "Задачата не можа да бъде създадена. Опитайте отново.", + "Created": "Създадена", + "Move to backlog": "Към беклога", + "Move to board": "Към таблото", + "New task": "Нова задача", + "Nothing in the backlog": "Няма нищо в беклога", + "Rank": "Ранг", + "Sort by": "Сортиране по", + "Sort by rank to reorder": "Сортирайте по ранг, за да пренаредите", + "Task actions": "Действия със задачата", + "All labels": "Всички етикети", + "Filter tasks by label": "Филтриране на задачите по етикет", + "View timeline": "Преглед на времевата линия" }, "plurals": null } diff --git a/l10n/bs.js b/l10n/bs.js index f40d86f0..c4b5d5bd 100644 --- a/l10n/bs.js +++ b/l10n/bs.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Posljednja kolona završenih ne može se ukloniti.", "WIP limit": "WIP ograničenje", "over limit": "iznad ograničenja", - "The status a card gets when it is moved into this column.": "Status koji kartica dobija kada se premjesti u ovu kolonu." + "The status a card gets when it is moved into this column.": "Status koji kartica dobija kada se premjesti u ovu kolonu.", + "All priorities": "Svi prioriteti", + "Could not create the task. Please try again.": "Zadatak nije moguće kreirati. Pokušajte ponovo.", + "Created": "Kreirano", + "Move to backlog": "U zaostatak", + "Move to board": "Na tablu", + "New task": "Novi zadatak", + "Nothing in the backlog": "Ništa u zaostatku", + "Rank": "Rang", + "Sort by": "Poredaj po", + "Sort by rank to reorder": "Poredajte po rangu da biste promijenili redoslijed", + "Task actions": "Radnje zadatka", + "All labels": "Sve oznake", + "Filter tasks by label": "Filtriraj zadatke po oznaci", + "View timeline": "Prikaži vremensku liniju" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/bs.json b/l10n/bs.json index 78721e4d..1daff23d 100644 --- a/l10n/bs.json +++ b/l10n/bs.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Posljednja kolona završenih ne može se ukloniti.", "WIP limit": "WIP ograničenje", "over limit": "iznad ograničenja", - "The status a card gets when it is moved into this column.": "Status koji kartica dobija kada se premjesti u ovu kolonu." + "The status a card gets when it is moved into this column.": "Status koji kartica dobija kada se premjesti u ovu kolonu.", + "All priorities": "Svi prioriteti", + "Could not create the task. Please try again.": "Zadatak nije moguće kreirati. Pokušajte ponovo.", + "Created": "Kreirano", + "Move to backlog": "U zaostatak", + "Move to board": "Na tablu", + "New task": "Novi zadatak", + "Nothing in the backlog": "Ništa u zaostatku", + "Rank": "Rang", + "Sort by": "Poredaj po", + "Sort by rank to reorder": "Poredajte po rangu da biste promijenili redoslijed", + "Task actions": "Radnje zadatka", + "All labels": "Sve oznake", + "Filter tasks by label": "Filtriraj zadatke po oznaci", + "View timeline": "Prikaži vremensku liniju" }, "plurals": null } diff --git a/l10n/ca.js b/l10n/ca.js index ee3e6443..c179d3dc 100644 --- a/l10n/ca.js +++ b/l10n/ca.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "L'última columna de fet no es pot eliminar.", "WIP limit": "Límit WIP", "over limit": "per sobre del límit", - "The status a card gets when it is moved into this column.": "L'estat que rep una targeta quan es mou a aquesta columna." + "The status a card gets when it is moved into this column.": "L'estat que rep una targeta quan es mou a aquesta columna.", + "All priorities": "Totes les prioritats", + "Could not create the task. Please try again.": "No s'ha pogut crear la tasca. Torneu-ho a provar.", + "Created": "Creada", + "Move to backlog": "Al backlog", + "Move to board": "Al tauler", + "New task": "Tasca nova", + "Nothing in the backlog": "No hi ha res al backlog", + "Rank": "Rang", + "Sort by": "Ordena per", + "Sort by rank to reorder": "Ordeneu per rang per reordenar", + "Task actions": "Accions de la tasca", + "All labels": "Totes les etiquetes", + "Filter tasks by label": "Filtra les tasques per etiqueta", + "View timeline": "Mostra la cronologia" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ca.json b/l10n/ca.json index 13fd665b..b7753d44 100644 --- a/l10n/ca.json +++ b/l10n/ca.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "L'última columna de fet no es pot eliminar.", "WIP limit": "Límit WIP", "over limit": "per sobre del límit", - "The status a card gets when it is moved into this column.": "L'estat que rep una targeta quan es mou a aquesta columna." + "The status a card gets when it is moved into this column.": "L'estat que rep una targeta quan es mou a aquesta columna.", + "All priorities": "Totes les prioritats", + "Could not create the task. Please try again.": "No s'ha pogut crear la tasca. Torneu-ho a provar.", + "Created": "Creada", + "Move to backlog": "Al backlog", + "Move to board": "Al tauler", + "New task": "Tasca nova", + "Nothing in the backlog": "No hi ha res al backlog", + "Rank": "Rang", + "Sort by": "Ordena per", + "Sort by rank to reorder": "Ordeneu per rang per reordenar", + "Task actions": "Accions de la tasca", + "All labels": "Totes les etiquetes", + "Filter tasks by label": "Filtra les tasques per etiqueta", + "View timeline": "Mostra la cronologia" }, "plurals": null } diff --git a/l10n/cs.js b/l10n/cs.js index 87acb1a5..73c992ea 100644 --- a/l10n/cs.js +++ b/l10n/cs.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Poslední sloupec hotovo nelze odebrat.", "WIP limit": "Limit WIP", "over limit": "nad limitem", - "The status a card gets when it is moved into this column.": "Stav, který karta získá po přesunutí do tohoto sloupce." + "The status a card gets when it is moved into this column.": "Stav, který karta získá po přesunutí do tohoto sloupce.", + "All priorities": "Všechny priority", + "Could not create the task. Please try again.": "Úkol se nepodařilo vytvořit. Zkuste to znovu.", + "Created": "Vytvořeno", + "Move to backlog": "Do backlogu", + "Move to board": "Na nástěnku", + "New task": "Nový úkol", + "Nothing in the backlog": "Backlog je prázdný", + "Rank": "Pořadí", + "Sort by": "Seřadit podle", + "Sort by rank to reorder": "Pro změnu pořadí seřaďte podle pořadí", + "Task actions": "Akce úkolu", + "All labels": "Všechny štítky", + "Filter tasks by label": "Filtrovat úkoly podle štítku", + "View timeline": "Zobrazit časovou osu" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/cs.json b/l10n/cs.json index eb4b6258..61d84e6d 100644 --- a/l10n/cs.json +++ b/l10n/cs.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Poslední sloupec hotovo nelze odebrat.", "WIP limit": "Limit WIP", "over limit": "nad limitem", - "The status a card gets when it is moved into this column.": "Stav, který karta získá po přesunutí do tohoto sloupce." + "The status a card gets when it is moved into this column.": "Stav, který karta získá po přesunutí do tohoto sloupce.", + "All priorities": "Všechny priority", + "Could not create the task. Please try again.": "Úkol se nepodařilo vytvořit. Zkuste to znovu.", + "Created": "Vytvořeno", + "Move to backlog": "Do backlogu", + "Move to board": "Na nástěnku", + "New task": "Nový úkol", + "Nothing in the backlog": "Backlog je prázdný", + "Rank": "Pořadí", + "Sort by": "Seřadit podle", + "Sort by rank to reorder": "Pro změnu pořadí seřaďte podle pořadí", + "Task actions": "Akce úkolu", + "All labels": "Všechny štítky", + "Filter tasks by label": "Filtrovat úkoly podle štítku", + "View timeline": "Zobrazit časovou osu" }, "plurals": null } diff --git a/l10n/da.js b/l10n/da.js index 9fb692c3..98f32104 100644 --- a/l10n/da.js +++ b/l10n/da.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Den sidste færdig-kolonne kan ikke fjernes.", "WIP limit": "WIP-grænse", "over limit": "over grænsen", - "The status a card gets when it is moved into this column.": "Den status et kort får, når det flyttes til denne kolonne." + "The status a card gets when it is moved into this column.": "Den status et kort får, når det flyttes til denne kolonne.", + "All priorities": "Alle prioriteter", + "Could not create the task. Please try again.": "Opgaven kunne ikke oprettes. Prøv igen.", + "Created": "Oprettet", + "Move to backlog": "Til backlog", + "Move to board": "Til tavlen", + "New task": "Ny opgave", + "Nothing in the backlog": "Intet i backloggen", + "Rank": "Rang", + "Sort by": "Sorter efter", + "Sort by rank to reorder": "Sorter efter rang for at ændre rækkefølgen", + "Task actions": "Opgavehandlinger", + "All labels": "Alle etiketter", + "Filter tasks by label": "Filtrer opgaver efter etiket", + "View timeline": "Vis tidslinje" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/da.json b/l10n/da.json index 67c5043f..e3eedd45 100644 --- a/l10n/da.json +++ b/l10n/da.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Den sidste færdig-kolonne kan ikke fjernes.", "WIP limit": "WIP-grænse", "over limit": "over grænsen", - "The status a card gets when it is moved into this column.": "Den status et kort får, når det flyttes til denne kolonne." + "The status a card gets when it is moved into this column.": "Den status et kort får, når det flyttes til denne kolonne.", + "All priorities": "Alle prioriteter", + "Could not create the task. Please try again.": "Opgaven kunne ikke oprettes. Prøv igen.", + "Created": "Oprettet", + "Move to backlog": "Til backlog", + "Move to board": "Til tavlen", + "New task": "Ny opgave", + "Nothing in the backlog": "Intet i backloggen", + "Rank": "Rang", + "Sort by": "Sorter efter", + "Sort by rank to reorder": "Sorter efter rang for at ændre rækkefølgen", + "Task actions": "Opgavehandlinger", + "All labels": "Alle etiketter", + "Filter tasks by label": "Filtrer opgaver efter etiket", + "View timeline": "Vis tidslinje" }, "plurals": null } diff --git a/l10n/de.js b/l10n/de.js index bb50a416..1d4cb102 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Die letzte Erledigt-Spalte kann nicht entfernt werden.", "WIP limit": "WIP-Limit", "over limit": "über dem Limit", - "The status a card gets when it is moved into this column.": "Der Status, den eine Karte erhält, wenn sie in diese Spalte verschoben wird." + "The status a card gets when it is moved into this column.": "Der Status, den eine Karte erhält, wenn sie in diese Spalte verschoben wird.", + "All priorities": "Alle Prioritäten", + "Could not create the task. Please try again.": "Die Aufgabe konnte nicht erstellt werden. Bitte versuchen Sie es erneut.", + "Created": "Erstellt", + "Move to backlog": "In den Backlog", + "Move to board": "Aufs Board", + "New task": "Neue Aufgabe", + "Nothing in the backlog": "Nichts im Backlog", + "Rank": "Rang", + "Sort by": "Sortieren nach", + "Sort by rank to reorder": "Nach Rang sortieren, um neu anzuordnen", + "Task actions": "Aufgabenaktionen", + "All labels": "Alle Labels", + "Filter tasks by label": "Aufgaben nach Label filtern", + "View timeline": "Zeitleiste anzeigen" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/de.json b/l10n/de.json index 2c2b8a87..1d5dd0cb 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Die letzte Erledigt-Spalte kann nicht entfernt werden.", "WIP limit": "WIP-Limit", "over limit": "über dem Limit", - "The status a card gets when it is moved into this column.": "Der Status, den eine Karte erhält, wenn sie in diese Spalte verschoben wird." + "The status a card gets when it is moved into this column.": "Der Status, den eine Karte erhält, wenn sie in diese Spalte verschoben wird.", + "All priorities": "Alle Prioritäten", + "Could not create the task. Please try again.": "Die Aufgabe konnte nicht erstellt werden. Bitte versuchen Sie es erneut.", + "Created": "Erstellt", + "Move to backlog": "In den Backlog", + "Move to board": "Aufs Board", + "New task": "Neue Aufgabe", + "Nothing in the backlog": "Nichts im Backlog", + "Rank": "Rang", + "Sort by": "Sortieren nach", + "Sort by rank to reorder": "Nach Rang sortieren, um neu anzuordnen", + "Task actions": "Aufgabenaktionen", + "All labels": "Alle Labels", + "Filter tasks by label": "Aufgaben nach Label filtern", + "View timeline": "Zeitleiste anzeigen" }, "plurals": null } diff --git a/l10n/el.js b/l10n/el.js index 35000299..0fa3f658 100644 --- a/l10n/el.js +++ b/l10n/el.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Η τελευταία στήλη ολοκληρωμένων δεν μπορεί να αφαιρεθεί.", "WIP limit": "Όριο WIP", "over limit": "πάνω από το όριο", - "The status a card gets when it is moved into this column.": "Η κατάσταση που παίρνει μια κάρτα όταν μετακινείται σε αυτή τη στήλη." + "The status a card gets when it is moved into this column.": "Η κατάσταση που παίρνει μια κάρτα όταν μετακινείται σε αυτή τη στήλη.", + "All priorities": "Όλες οι προτεραιότητες", + "Could not create the task. Please try again.": "Δεν ήταν δυνατή η δημιουργία της εργασίας. Δοκιμάστε ξανά.", + "Created": "Δημιουργήθηκε", + "Move to backlog": "Στο backlog", + "Move to board": "Στον πίνακα", + "New task": "Νέα εργασία", + "Nothing in the backlog": "Τίποτα στο backlog", + "Rank": "Κατάταξη", + "Sort by": "Ταξινόμηση κατά", + "Sort by rank to reorder": "Ταξινομήστε κατά κατάταξη για αναδιάταξη", + "Task actions": "Ενέργειες εργασίας", + "All labels": "Όλες οι ετικέτες", + "Filter tasks by label": "Φιλτράρισμα εργασιών κατά ετικέτα", + "View timeline": "Προβολή χρονολογίου" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/el.json b/l10n/el.json index 9226f031..d781a1d0 100644 --- a/l10n/el.json +++ b/l10n/el.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Η τελευταία στήλη ολοκληρωμένων δεν μπορεί να αφαιρεθεί.", "WIP limit": "Όριο WIP", "over limit": "πάνω από το όριο", - "The status a card gets when it is moved into this column.": "Η κατάσταση που παίρνει μια κάρτα όταν μετακινείται σε αυτή τη στήλη." + "The status a card gets when it is moved into this column.": "Η κατάσταση που παίρνει μια κάρτα όταν μετακινείται σε αυτή τη στήλη.", + "All priorities": "Όλες οι προτεραιότητες", + "Could not create the task. Please try again.": "Δεν ήταν δυνατή η δημιουργία της εργασίας. Δοκιμάστε ξανά.", + "Created": "Δημιουργήθηκε", + "Move to backlog": "Στο backlog", + "Move to board": "Στον πίνακα", + "New task": "Νέα εργασία", + "Nothing in the backlog": "Τίποτα στο backlog", + "Rank": "Κατάταξη", + "Sort by": "Ταξινόμηση κατά", + "Sort by rank to reorder": "Ταξινομήστε κατά κατάταξη για αναδιάταξη", + "Task actions": "Ενέργειες εργασίας", + "All labels": "Όλες οι ετικέτες", + "Filter tasks by label": "Φιλτράρισμα εργασιών κατά ετικέτα", + "View timeline": "Προβολή χρονολογίου" }, "plurals": null } diff --git a/l10n/en.js b/l10n/en.js index 459f3225..81127dac 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -265,7 +265,21 @@ OC.L10N.register( "The last done column cannot be removed.": "The last done column cannot be removed.", "WIP limit": "WIP limit", "over limit": "over limit", - "The status a card gets when it is moved into this column.": "The status a card gets when it is moved into this column." + "The status a card gets when it is moved into this column.": "The status a card gets when it is moved into this column.", + "All priorities": "All priorities", + "Could not create the task. Please try again.": "Could not create the task. Please try again.", + "Created": "Created", + "Move to backlog": "Move to backlog", + "Move to board": "Move to board", + "New task": "New task", + "Nothing in the backlog": "Nothing in the backlog", + "Rank": "Rank", + "Sort by": "Sort by", + "Sort by rank to reorder": "Sort by rank to reorder", + "Task actions": "Task actions", + "All labels": "All labels", + "Filter tasks by label": "Filter tasks by label", + "View timeline": "View timeline" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/en.json b/l10n/en.json index 66361b63..53992dae 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -264,7 +264,21 @@ "The last done column cannot be removed.": "The last done column cannot be removed.", "WIP limit": "WIP limit", "over limit": "over limit", - "The status a card gets when it is moved into this column.": "The status a card gets when it is moved into this column." + "The status a card gets when it is moved into this column.": "The status a card gets when it is moved into this column.", + "All priorities": "All priorities", + "Could not create the task. Please try again.": "Could not create the task. Please try again.", + "Created": "Created", + "Move to backlog": "Move to backlog", + "Move to board": "Move to board", + "New task": "New task", + "Nothing in the backlog": "Nothing in the backlog", + "Rank": "Rank", + "Sort by": "Sort by", + "Sort by rank to reorder": "Sort by rank to reorder", + "Task actions": "Task actions", + "All labels": "All labels", + "Filter tasks by label": "Filter tasks by label", + "View timeline": "View timeline" }, "plurals": {}, "pluralForm": "nplurals=2; plural=(n != 1);" diff --git a/l10n/es.js b/l10n/es.js index fb874a7a..ca057b1f 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "La última columna de terminado no se puede eliminar.", "WIP limit": "Límite WIP", "over limit": "por encima del límite", - "The status a card gets when it is moved into this column.": "El estado que recibe una tarjeta cuando se mueve a esta columna." + "The status a card gets when it is moved into this column.": "El estado que recibe una tarjeta cuando se mueve a esta columna.", + "All priorities": "Todas las prioridades", + "Could not create the task. Please try again.": "No se pudo crear la tarea. Inténtelo de nuevo.", + "Created": "Creada", + "Move to backlog": "Al backlog", + "Move to board": "Al tablero", + "New task": "Nueva tarea", + "Nothing in the backlog": "Nada en el backlog", + "Rank": "Rango", + "Sort by": "Ordenar por", + "Sort by rank to reorder": "Ordene por rango para reordenar", + "Task actions": "Acciones de la tarea", + "All labels": "Todas las etiquetas", + "Filter tasks by label": "Filtrar tareas por etiqueta", + "View timeline": "Ver cronología" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/es.json b/l10n/es.json index 40e4c5e5..abd3bb6e 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "La última columna de terminado no se puede eliminar.", "WIP limit": "Límite WIP", "over limit": "por encima del límite", - "The status a card gets when it is moved into this column.": "El estado que recibe una tarjeta cuando se mueve a esta columna." + "The status a card gets when it is moved into this column.": "El estado que recibe una tarjeta cuando se mueve a esta columna.", + "All priorities": "Todas las prioridades", + "Could not create the task. Please try again.": "No se pudo crear la tarea. Inténtelo de nuevo.", + "Created": "Creada", + "Move to backlog": "Al backlog", + "Move to board": "Al tablero", + "New task": "Nueva tarea", + "Nothing in the backlog": "Nada en el backlog", + "Rank": "Rango", + "Sort by": "Ordenar por", + "Sort by rank to reorder": "Ordene por rango para reordenar", + "Task actions": "Acciones de la tarea", + "All labels": "Todas las etiquetas", + "Filter tasks by label": "Filtrar tareas por etiqueta", + "View timeline": "Ver cronología" }, "plurals": null } diff --git a/l10n/et.js b/l10n/et.js index fc6a8392..c29c2461 100644 --- a/l10n/et.js +++ b/l10n/et.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Viimast valmis-veergu ei saa eemaldada.", "WIP limit": "WIP-piirang", "over limit": "üle piirangu", - "The status a card gets when it is moved into this column.": "Olek, mille kaart saab, kui see teisaldatakse sellesse veergu." + "The status a card gets when it is moved into this column.": "Olek, mille kaart saab, kui see teisaldatakse sellesse veergu.", + "All priorities": "Kõik prioriteedid", + "Could not create the task. Please try again.": "Ülesannet ei õnnestunud luua. Proovi uuesti.", + "Created": "Loodud", + "Move to backlog": "Ootenimekirja", + "Move to board": "Tahvlile", + "New task": "Uus ülesanne", + "Nothing in the backlog": "Ootenimekiri on tühi", + "Rank": "Järjekoht", + "Sort by": "Sordi", + "Sort by rank to reorder": "Järjestuse muutmiseks sordi järjekoha järgi", + "Task actions": "Ülesande toimingud", + "All labels": "Kõik sildid", + "Filter tasks by label": "Filtreeri ülesandeid sildi järgi", + "View timeline": "Vaata ajajoont" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/et.json b/l10n/et.json index 272e8f21..dd7152ee 100644 --- a/l10n/et.json +++ b/l10n/et.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Viimast valmis-veergu ei saa eemaldada.", "WIP limit": "WIP-piirang", "over limit": "üle piirangu", - "The status a card gets when it is moved into this column.": "Olek, mille kaart saab, kui see teisaldatakse sellesse veergu." + "The status a card gets when it is moved into this column.": "Olek, mille kaart saab, kui see teisaldatakse sellesse veergu.", + "All priorities": "Kõik prioriteedid", + "Could not create the task. Please try again.": "Ülesannet ei õnnestunud luua. Proovi uuesti.", + "Created": "Loodud", + "Move to backlog": "Ootenimekirja", + "Move to board": "Tahvlile", + "New task": "Uus ülesanne", + "Nothing in the backlog": "Ootenimekiri on tühi", + "Rank": "Järjekoht", + "Sort by": "Sordi", + "Sort by rank to reorder": "Järjestuse muutmiseks sordi järjekoha järgi", + "Task actions": "Ülesande toimingud", + "All labels": "Kõik sildid", + "Filter tasks by label": "Filtreeri ülesandeid sildi järgi", + "View timeline": "Vaata ajajoont" }, "plurals": null } diff --git a/l10n/fi.js b/l10n/fi.js index 4472478a..895b32c0 100644 --- a/l10n/fi.js +++ b/l10n/fi.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Viimeistä valmis-saraketta ei voi poistaa.", "WIP limit": "WIP-raja", "over limit": "yli rajan", - "The status a card gets when it is moved into this column.": "Tila, jonka kortti saa, kun se siirretään tähän sarakkeeseen." + "The status a card gets when it is moved into this column.": "Tila, jonka kortti saa, kun se siirretään tähän sarakkeeseen.", + "All priorities": "Kaikki prioriteetit", + "Could not create the task. Please try again.": "Tehtävää ei voitu luoda. Yritä uudelleen.", + "Created": "Luotu", + "Move to backlog": "Backlogiin", + "Move to board": "Taululle", + "New task": "Uusi tehtävä", + "Nothing in the backlog": "Backlog on tyhjä", + "Rank": "Järjestys", + "Sort by": "Lajitteluperuste", + "Sort by rank to reorder": "Lajittele järjestyksen mukaan muuttaaksesi järjestystä", + "Task actions": "Tehtävän toiminnot", + "All labels": "Kaikki tunnisteet", + "Filter tasks by label": "Suodata tehtäviä tunnisteen mukaan", + "View timeline": "Näytä aikajana" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/fi.json b/l10n/fi.json index ade66407..3b8647e0 100644 --- a/l10n/fi.json +++ b/l10n/fi.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Viimeistä valmis-saraketta ei voi poistaa.", "WIP limit": "WIP-raja", "over limit": "yli rajan", - "The status a card gets when it is moved into this column.": "Tila, jonka kortti saa, kun se siirretään tähän sarakkeeseen." + "The status a card gets when it is moved into this column.": "Tila, jonka kortti saa, kun se siirretään tähän sarakkeeseen.", + "All priorities": "Kaikki prioriteetit", + "Could not create the task. Please try again.": "Tehtävää ei voitu luoda. Yritä uudelleen.", + "Created": "Luotu", + "Move to backlog": "Backlogiin", + "Move to board": "Taululle", + "New task": "Uusi tehtävä", + "Nothing in the backlog": "Backlog on tyhjä", + "Rank": "Järjestys", + "Sort by": "Lajitteluperuste", + "Sort by rank to reorder": "Lajittele järjestyksen mukaan muuttaaksesi järjestystä", + "Task actions": "Tehtävän toiminnot", + "All labels": "Kaikki tunnisteet", + "Filter tasks by label": "Suodata tehtäviä tunnisteen mukaan", + "View timeline": "Näytä aikajana" }, "plurals": null } diff --git a/l10n/fr.js b/l10n/fr.js index 5417e6fd..a588f713 100644 --- a/l10n/fr.js +++ b/l10n/fr.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "La dernière colonne terminée ne peut pas être supprimée.", "WIP limit": "Limite WIP", "over limit": "au-dessus de la limite", - "The status a card gets when it is moved into this column.": "Le statut qu'une carte reçoit lorsqu'elle est déplacée dans cette colonne." + "The status a card gets when it is moved into this column.": "Le statut qu'une carte reçoit lorsqu'elle est déplacée dans cette colonne.", + "All priorities": "Toutes les priorités", + "Could not create the task. Please try again.": "Impossible de créer la tâche. Veuillez réessayer.", + "Created": "Créée", + "Move to backlog": "Vers le backlog", + "Move to board": "Vers le tableau", + "New task": "Nouvelle tâche", + "Nothing in the backlog": "Rien dans le backlog", + "Rank": "Rang", + "Sort by": "Trier par", + "Sort by rank to reorder": "Triez par rang pour réordonner", + "Task actions": "Actions de la tâche", + "All labels": "Toutes les étiquettes", + "Filter tasks by label": "Filtrer les tâches par étiquette", + "View timeline": "Voir la chronologie" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/fr.json b/l10n/fr.json index 6b390cff..2528849e 100644 --- a/l10n/fr.json +++ b/l10n/fr.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "La dernière colonne terminée ne peut pas être supprimée.", "WIP limit": "Limite WIP", "over limit": "au-dessus de la limite", - "The status a card gets when it is moved into this column.": "Le statut qu'une carte reçoit lorsqu'elle est déplacée dans cette colonne." + "The status a card gets when it is moved into this column.": "Le statut qu'une carte reçoit lorsqu'elle est déplacée dans cette colonne.", + "All priorities": "Toutes les priorités", + "Could not create the task. Please try again.": "Impossible de créer la tâche. Veuillez réessayer.", + "Created": "Créée", + "Move to backlog": "Vers le backlog", + "Move to board": "Vers le tableau", + "New task": "Nouvelle tâche", + "Nothing in the backlog": "Rien dans le backlog", + "Rank": "Rang", + "Sort by": "Trier par", + "Sort by rank to reorder": "Triez par rang pour réordonner", + "Task actions": "Actions de la tâche", + "All labels": "Toutes les étiquettes", + "Filter tasks by label": "Filtrer les tâches par étiquette", + "View timeline": "Voir la chronologie" }, "plurals": null } diff --git a/l10n/ga.js b/l10n/ga.js index 02197bc5..7fb17496 100644 --- a/l10n/ga.js +++ b/l10n/ga.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Ní féidir an colún críochnaithe deireanach a bhaint.", "WIP limit": "Teorainn WIP", "over limit": "os cionn na teorann", - "The status a card gets when it is moved into this column.": "An stádas a fhaigheann cárta nuair a bhogtar isteach sa cholún seo é." + "The status a card gets when it is moved into this column.": "An stádas a fhaigheann cárta nuair a bhogtar isteach sa cholún seo é.", + "All priorities": "Gach tosaíocht", + "Could not create the task. Please try again.": "Níorbh fhéidir an tasc a chruthú. Bain triail eile as.", + "Created": "Cruthaithe", + "Move to backlog": "Chuig an mbacliosta", + "Move to board": "Chuig an gclár", + "New task": "Tasc nua", + "Nothing in the backlog": "Níl aon rud sa bhacliosta", + "Rank": "Rangú", + "Sort by": "Sórtáil de réir", + "Sort by rank to reorder": "Sórtáil de réir rangaithe chun an t-ord a athrú", + "Task actions": "Gníomhartha taisc", + "All labels": "Gach lipéad", + "Filter tasks by label": "Scag tascanna de réir lipéid", + "View timeline": "Féach ar an amlíne" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ga.json b/l10n/ga.json index a923190d..799daf12 100644 --- a/l10n/ga.json +++ b/l10n/ga.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Ní féidir an colún críochnaithe deireanach a bhaint.", "WIP limit": "Teorainn WIP", "over limit": "os cionn na teorann", - "The status a card gets when it is moved into this column.": "An stádas a fhaigheann cárta nuair a bhogtar isteach sa cholún seo é." + "The status a card gets when it is moved into this column.": "An stádas a fhaigheann cárta nuair a bhogtar isteach sa cholún seo é.", + "All priorities": "Gach tosaíocht", + "Could not create the task. Please try again.": "Níorbh fhéidir an tasc a chruthú. Bain triail eile as.", + "Created": "Cruthaithe", + "Move to backlog": "Chuig an mbacliosta", + "Move to board": "Chuig an gclár", + "New task": "Tasc nua", + "Nothing in the backlog": "Níl aon rud sa bhacliosta", + "Rank": "Rangú", + "Sort by": "Sórtáil de réir", + "Sort by rank to reorder": "Sórtáil de réir rangaithe chun an t-ord a athrú", + "Task actions": "Gníomhartha taisc", + "All labels": "Gach lipéad", + "Filter tasks by label": "Scag tascanna de réir lipéid", + "View timeline": "Féach ar an amlíne" }, "plurals": null } diff --git a/l10n/hr.js b/l10n/hr.js index 57471ab0..8e243883 100644 --- a/l10n/hr.js +++ b/l10n/hr.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Posljednji stupac završenih ne može se ukloniti.", "WIP limit": "WIP ograničenje", "over limit": "iznad ograničenja", - "The status a card gets when it is moved into this column.": "Status koji kartica dobiva kada se premjesti u ovaj stupac." + "The status a card gets when it is moved into this column.": "Status koji kartica dobiva kada se premjesti u ovaj stupac.", + "All priorities": "Svi prioriteti", + "Could not create the task. Please try again.": "Zadatak nije moguće stvoriti. Pokušajte ponovno.", + "Created": "Stvoreno", + "Move to backlog": "U zaostatak", + "Move to board": "Na ploču", + "New task": "Novi zadatak", + "Nothing in the backlog": "Ništa u zaostatku", + "Rank": "Rang", + "Sort by": "Poredaj po", + "Sort by rank to reorder": "Poredajte po rangu za promjenu redoslijeda", + "Task actions": "Radnje zadatka", + "All labels": "Sve oznake", + "Filter tasks by label": "Filtriraj zadatke po oznaci", + "View timeline": "Prikaži vremensku crtu" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/hr.json b/l10n/hr.json index dce68c57..532ae802 100644 --- a/l10n/hr.json +++ b/l10n/hr.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Posljednji stupac završenih ne može se ukloniti.", "WIP limit": "WIP ograničenje", "over limit": "iznad ograničenja", - "The status a card gets when it is moved into this column.": "Status koji kartica dobiva kada se premjesti u ovaj stupac." + "The status a card gets when it is moved into this column.": "Status koji kartica dobiva kada se premjesti u ovaj stupac.", + "All priorities": "Svi prioriteti", + "Could not create the task. Please try again.": "Zadatak nije moguće stvoriti. Pokušajte ponovno.", + "Created": "Stvoreno", + "Move to backlog": "U zaostatak", + "Move to board": "Na ploču", + "New task": "Novi zadatak", + "Nothing in the backlog": "Ništa u zaostatku", + "Rank": "Rang", + "Sort by": "Poredaj po", + "Sort by rank to reorder": "Poredajte po rangu za promjenu redoslijeda", + "Task actions": "Radnje zadatka", + "All labels": "Sve oznake", + "Filter tasks by label": "Filtriraj zadatke po oznaci", + "View timeline": "Prikaži vremensku crtu" }, "plurals": null } diff --git a/l10n/hu.js b/l10n/hu.js index 09600c17..2bf33c87 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Az utolsó kész oszlop nem távolítható el.", "WIP limit": "WIP-korlát", "over limit": "korlát felett", - "The status a card gets when it is moved into this column.": "Az állapot, amelyet egy kártya kap, amikor ebbe az oszlopba kerül." + "The status a card gets when it is moved into this column.": "Az állapot, amelyet egy kártya kap, amikor ebbe az oszlopba kerül.", + "All priorities": "Minden prioritás", + "Could not create the task. Please try again.": "A feladatot nem sikerült létrehozni. Próbálja újra.", + "Created": "Létrehozva", + "Move to backlog": "A backlogba", + "Move to board": "A táblára", + "New task": "Új feladat", + "Nothing in the backlog": "A backlog üres", + "Rank": "Sorrend", + "Sort by": "Rendezés", + "Sort by rank to reorder": "Az átrendezéshez rendezzen sorrend szerint", + "Task actions": "Feladatműveletek", + "All labels": "Minden címke", + "Filter tasks by label": "Feladatok szűrése címke szerint", + "View timeline": "Idővonal megtekintése" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/hu.json b/l10n/hu.json index 031097d8..6b7f7a88 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Az utolsó kész oszlop nem távolítható el.", "WIP limit": "WIP-korlát", "over limit": "korlát felett", - "The status a card gets when it is moved into this column.": "Az állapot, amelyet egy kártya kap, amikor ebbe az oszlopba kerül." + "The status a card gets when it is moved into this column.": "Az állapot, amelyet egy kártya kap, amikor ebbe az oszlopba kerül.", + "All priorities": "Minden prioritás", + "Could not create the task. Please try again.": "A feladatot nem sikerült létrehozni. Próbálja újra.", + "Created": "Létrehozva", + "Move to backlog": "A backlogba", + "Move to board": "A táblára", + "New task": "Új feladat", + "Nothing in the backlog": "A backlog üres", + "Rank": "Sorrend", + "Sort by": "Rendezés", + "Sort by rank to reorder": "Az átrendezéshez rendezzen sorrend szerint", + "Task actions": "Feladatműveletek", + "All labels": "Minden címke", + "Filter tasks by label": "Feladatok szűrése címke szerint", + "View timeline": "Idővonal megtekintése" }, "plurals": null } diff --git a/l10n/is.js b/l10n/is.js index c4af823b..a54fd882 100644 --- a/l10n/is.js +++ b/l10n/is.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Ekki er hægt að fjarlægja síðasta lokið-dálkinn.", "WIP limit": "WIP-mörk", "over limit": "yfir mörkum", - "The status a card gets when it is moved into this column.": "Staðan sem spjald fær þegar það er fært í þennan dálk." + "The status a card gets when it is moved into this column.": "Staðan sem spjald fær þegar það er fært í þennan dálk.", + "All priorities": "Allar forgangsraðanir", + "Could not create the task. Please try again.": "Ekki tókst að búa til verkefnið. Reyndu aftur.", + "Created": "Búið til", + "Move to backlog": "Í biðlistann", + "Move to board": "Á töfluna", + "New task": "Nýtt verkefni", + "Nothing in the backlog": "Ekkert í biðlistanum", + "Rank": "Röð", + "Sort by": "Raða eftir", + "Sort by rank to reorder": "Raðaðu eftir röð til að endurraða", + "Task actions": "Aðgerðir verkefnis", + "All labels": "Öll merki", + "Filter tasks by label": "Sía verkefni eftir merki", + "View timeline": "Skoða tímalínu" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/is.json b/l10n/is.json index 7a8c7d4e..681da948 100644 --- a/l10n/is.json +++ b/l10n/is.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Ekki er hægt að fjarlægja síðasta lokið-dálkinn.", "WIP limit": "WIP-mörk", "over limit": "yfir mörkum", - "The status a card gets when it is moved into this column.": "Staðan sem spjald fær þegar það er fært í þennan dálk." + "The status a card gets when it is moved into this column.": "Staðan sem spjald fær þegar það er fært í þennan dálk.", + "All priorities": "Allar forgangsraðanir", + "Could not create the task. Please try again.": "Ekki tókst að búa til verkefnið. Reyndu aftur.", + "Created": "Búið til", + "Move to backlog": "Í biðlistann", + "Move to board": "Á töfluna", + "New task": "Nýtt verkefni", + "Nothing in the backlog": "Ekkert í biðlistanum", + "Rank": "Röð", + "Sort by": "Raða eftir", + "Sort by rank to reorder": "Raðaðu eftir röð til að endurraða", + "Task actions": "Aðgerðir verkefnis", + "All labels": "Öll merki", + "Filter tasks by label": "Sía verkefni eftir merki", + "View timeline": "Skoða tímalínu" }, "plurals": {} } diff --git a/l10n/it.js b/l10n/it.js index 3406bd0d..e2c9f22c 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "L'ultima colonna completati non può essere rimossa.", "WIP limit": "Limite WIP", "over limit": "oltre il limite", - "The status a card gets when it is moved into this column.": "Lo stato che una scheda riceve quando viene spostata in questa colonna." + "The status a card gets when it is moved into this column.": "Lo stato che una scheda riceve quando viene spostata in questa colonna.", + "All priorities": "Tutte le priorità", + "Could not create the task. Please try again.": "Impossibile creare l'attività. Riprova.", + "Created": "Creata", + "Move to backlog": "Nel backlog", + "Move to board": "Sulla bacheca", + "New task": "Nuova attività", + "Nothing in the backlog": "Niente nel backlog", + "Rank": "Rango", + "Sort by": "Ordina per", + "Sort by rank to reorder": "Ordina per rango per riordinare", + "Task actions": "Azioni dell'attività", + "All labels": "Tutte le etichette", + "Filter tasks by label": "Filtra le attività per etichetta", + "View timeline": "Visualizza cronologia" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/it.json b/l10n/it.json index e89d8a23..ee9d1a60 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "L'ultima colonna completati non può essere rimossa.", "WIP limit": "Limite WIP", "over limit": "oltre il limite", - "The status a card gets when it is moved into this column.": "Lo stato che una scheda riceve quando viene spostata in questa colonna." + "The status a card gets when it is moved into this column.": "Lo stato che una scheda riceve quando viene spostata in questa colonna.", + "All priorities": "Tutte le priorità", + "Could not create the task. Please try again.": "Impossibile creare l'attività. Riprova.", + "Created": "Creata", + "Move to backlog": "Nel backlog", + "Move to board": "Sulla bacheca", + "New task": "Nuova attività", + "Nothing in the backlog": "Niente nel backlog", + "Rank": "Rango", + "Sort by": "Ordina per", + "Sort by rank to reorder": "Ordina per rango per riordinare", + "Task actions": "Azioni dell'attività", + "All labels": "Tutte le etichette", + "Filter tasks by label": "Filtra le attività per etichetta", + "View timeline": "Visualizza cronologia" }, "plurals": null } diff --git a/l10n/lb.js b/l10n/lb.js index ffccbebb..0338d22d 100644 --- a/l10n/lb.js +++ b/l10n/lb.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Déi lescht Fäerdeg-Kolonn kann net ewechgeholl ginn.", "WIP limit": "WIP-Limit", "over limit": "iwwer der Limit", - "The status a card gets when it is moved into this column.": "De Status, deen eng Kaart kritt, wann se an dës Kolonn geréckelt gëtt." + "The status a card gets when it is moved into this column.": "De Status, deen eng Kaart kritt, wann se an dës Kolonn geréckelt gëtt.", + "All priorities": "All Prioritéiten", + "Could not create the task. Please try again.": "D'Aufgab konnt net erstallt ginn. Probéiert w.e.g. nach eng Kéier.", + "Created": "Erstallt", + "Move to backlog": "An de Backlog", + "Move to board": "Op de Board", + "New task": "Nei Aufgab", + "Nothing in the backlog": "Näischt am Backlog", + "Rank": "Rang", + "Sort by": "Sortéieren no", + "Sort by rank to reorder": "No Rang sortéieren fir nei unzeuerdnen", + "Task actions": "Aufgabenaktiounen", + "All labels": "All Etiketten", + "Filter tasks by label": "Aufgabe no Etikett filteren", + "View timeline": "Zäitlinn weisen" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lb.json b/l10n/lb.json index f7197a50..55e38045 100644 --- a/l10n/lb.json +++ b/l10n/lb.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Déi lescht Fäerdeg-Kolonn kann net ewechgeholl ginn.", "WIP limit": "WIP-Limit", "over limit": "iwwer der Limit", - "The status a card gets when it is moved into this column.": "De Status, deen eng Kaart kritt, wann se an dës Kolonn geréckelt gëtt." + "The status a card gets when it is moved into this column.": "De Status, deen eng Kaart kritt, wann se an dës Kolonn geréckelt gëtt.", + "All priorities": "All Prioritéiten", + "Could not create the task. Please try again.": "D'Aufgab konnt net erstallt ginn. Probéiert w.e.g. nach eng Kéier.", + "Created": "Erstallt", + "Move to backlog": "An de Backlog", + "Move to board": "Op de Board", + "New task": "Nei Aufgab", + "Nothing in the backlog": "Näischt am Backlog", + "Rank": "Rang", + "Sort by": "Sortéieren no", + "Sort by rank to reorder": "No Rang sortéieren fir nei unzeuerdnen", + "Task actions": "Aufgabenaktiounen", + "All labels": "All Etiketten", + "Filter tasks by label": "Aufgabe no Etikett filteren", + "View timeline": "Zäitlinn weisen" }, "plurals": null } diff --git a/l10n/lt.js b/l10n/lt.js index a731697a..f06f0521 100644 --- a/l10n/lt.js +++ b/l10n/lt.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Paskutinio atliktų stulpelio pašalinti negalima.", "WIP limit": "WIP riba", "over limit": "virš ribos", - "The status a card gets when it is moved into this column.": "Būsena, kurią kortelė gauna, kai perkeliama į šį stulpelį." + "The status a card gets when it is moved into this column.": "Būsena, kurią kortelė gauna, kai perkeliama į šį stulpelį.", + "All priorities": "Visi prioritetai", + "Could not create the task. Please try again.": "Nepavyko sukurti užduoties. Bandykite dar kartą.", + "Created": "Sukurta", + "Move to backlog": "Į atsargų sąrašą", + "Move to board": "Į lentą", + "New task": "Nauja užduotis", + "Nothing in the backlog": "Atsargų sąrašas tuščias", + "Rank": "Rangas", + "Sort by": "Rikiuoti pagal", + "Sort by rank to reorder": "Norėdami pertvarkyti, rikiuokite pagal rangą", + "Task actions": "Užduoties veiksmai", + "All labels": "Visos žymės", + "Filter tasks by label": "Filtruoti užduotis pagal žymę", + "View timeline": "Rodyti laiko juostą" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lt.json b/l10n/lt.json index 97006e44..e4bab6eb 100644 --- a/l10n/lt.json +++ b/l10n/lt.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Paskutinio atliktų stulpelio pašalinti negalima.", "WIP limit": "WIP riba", "over limit": "virš ribos", - "The status a card gets when it is moved into this column.": "Būsena, kurią kortelė gauna, kai perkeliama į šį stulpelį." + "The status a card gets when it is moved into this column.": "Būsena, kurią kortelė gauna, kai perkeliama į šį stulpelį.", + "All priorities": "Visi prioritetai", + "Could not create the task. Please try again.": "Nepavyko sukurti užduoties. Bandykite dar kartą.", + "Created": "Sukurta", + "Move to backlog": "Į atsargų sąrašą", + "Move to board": "Į lentą", + "New task": "Nauja užduotis", + "Nothing in the backlog": "Atsargų sąrašas tuščias", + "Rank": "Rangas", + "Sort by": "Rikiuoti pagal", + "Sort by rank to reorder": "Norėdami pertvarkyti, rikiuokite pagal rangą", + "Task actions": "Užduoties veiksmai", + "All labels": "Visos žymės", + "Filter tasks by label": "Filtruoti užduotis pagal žymę", + "View timeline": "Rodyti laiko juostą" }, "plurals": null } diff --git a/l10n/lv.js b/l10n/lv.js index 425af423..b3d425e7 100644 --- a/l10n/lv.js +++ b/l10n/lv.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Pēdējo pabeigto kolonnu nevar noņemt.", "WIP limit": "WIP ierobežojums", "over limit": "virs ierobežojuma", - "The status a card gets when it is moved into this column.": "Statuss, ko kartīte saņem, kad tā tiek pārvietota uz šo kolonnu." + "The status a card gets when it is moved into this column.": "Statuss, ko kartīte saņem, kad tā tiek pārvietota uz šo kolonnu.", + "All priorities": "Visas prioritātes", + "Could not create the task. Please try again.": "Uzdevumu neizdevās izveidot. Mēģiniet vēlreiz.", + "Created": "Izveidots", + "Move to backlog": "Uz uzkrājumu", + "Move to board": "Uz dēli", + "New task": "Jauns uzdevums", + "Nothing in the backlog": "Uzkrājumā nekā nav", + "Rank": "Rangs", + "Sort by": "Kārtot pēc", + "Sort by rank to reorder": "Kārtojiet pēc ranga, lai mainītu secību", + "Task actions": "Uzdevuma darbības", + "All labels": "Visas birkas", + "Filter tasks by label": "Filtrēt uzdevumus pēc birkas", + "View timeline": "Skatīt laika skalu" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lv.json b/l10n/lv.json index 12954c6b..6201f54a 100644 --- a/l10n/lv.json +++ b/l10n/lv.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Pēdējo pabeigto kolonnu nevar noņemt.", "WIP limit": "WIP ierobežojums", "over limit": "virs ierobežojuma", - "The status a card gets when it is moved into this column.": "Statuss, ko kartīte saņem, kad tā tiek pārvietota uz šo kolonnu." + "The status a card gets when it is moved into this column.": "Statuss, ko kartīte saņem, kad tā tiek pārvietota uz šo kolonnu.", + "All priorities": "Visas prioritātes", + "Could not create the task. Please try again.": "Uzdevumu neizdevās izveidot. Mēģiniet vēlreiz.", + "Created": "Izveidots", + "Move to backlog": "Uz uzkrājumu", + "Move to board": "Uz dēli", + "New task": "Jauns uzdevums", + "Nothing in the backlog": "Uzkrājumā nekā nav", + "Rank": "Rangs", + "Sort by": "Kārtot pēc", + "Sort by rank to reorder": "Kārtojiet pēc ranga, lai mainītu secību", + "Task actions": "Uzdevuma darbības", + "All labels": "Visas birkas", + "Filter tasks by label": "Filtrēt uzdevumus pēc birkas", + "View timeline": "Skatīt laika skalu" }, "plurals": null } diff --git a/l10n/mk.js b/l10n/mk.js index 5d9cf242..c5be859e 100644 --- a/l10n/mk.js +++ b/l10n/mk.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Последната колона за завршени не може да се отстрани.", "WIP limit": "WIP ограничување", "over limit": "над ограничувањето", - "The status a card gets when it is moved into this column.": "Статусот што го добива картичката кога ќе се премести во оваа колона." + "The status a card gets when it is moved into this column.": "Статусот што го добива картичката кога ќе се премести во оваа колона.", + "All priorities": "Сите приоритети", + "Could not create the task. Please try again.": "Задачата не може да се создаде. Обидете се повторно.", + "Created": "Создадено", + "Move to backlog": "Во заостатокот", + "Move to board": "На таблата", + "New task": "Нова задача", + "Nothing in the backlog": "Нема ништо во заостатокот", + "Rank": "Ранг", + "Sort by": "Подреди по", + "Sort by rank to reorder": "Подредете по ранг за да го промените редоследот", + "Task actions": "Дејства на задачата", + "All labels": "Сите ознаки", + "Filter tasks by label": "Филтрирај задачи по ознака", + "View timeline": "Прикажи временска линија" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/mk.json b/l10n/mk.json index 3c1c0467..b3387360 100644 --- a/l10n/mk.json +++ b/l10n/mk.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Последната колона за завршени не може да се отстрани.", "WIP limit": "WIP ограничување", "over limit": "над ограничувањето", - "The status a card gets when it is moved into this column.": "Статусот што го добива картичката кога ќе се премести во оваа колона." + "The status a card gets when it is moved into this column.": "Статусот што го добива картичката кога ќе се премести во оваа колона.", + "All priorities": "Сите приоритети", + "Could not create the task. Please try again.": "Задачата не може да се создаде. Обидете се повторно.", + "Created": "Создадено", + "Move to backlog": "Во заостатокот", + "Move to board": "На таблата", + "New task": "Нова задача", + "Nothing in the backlog": "Нема ништо во заостатокот", + "Rank": "Ранг", + "Sort by": "Подреди по", + "Sort by rank to reorder": "Подредете по ранг за да го промените редоследот", + "Task actions": "Дејства на задачата", + "All labels": "Сите ознаки", + "Filter tasks by label": "Филтрирај задачи по ознака", + "View timeline": "Прикажи временска линија" }, "plurals": null } diff --git a/l10n/mt.js b/l10n/mt.js index ed7fd8ca..ec18b3c9 100644 --- a/l10n/mt.js +++ b/l10n/mt.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "L-aħħar kolonna ta' lest ma tistax titneħħa.", "WIP limit": "Limitu WIP", "over limit": "'il fuq mil-limitu", - "The status a card gets when it is moved into this column.": "L-istatus li tieħu karta meta titmexxa f'din il-kolonna." + "The status a card gets when it is moved into this column.": "L-istatus li tieħu karta meta titmexxa f'din il-kolonna.", + "All priorities": "Il-prijoritajiet kollha", + "Could not create the task. Please try again.": "Il-kompitu ma setax jinħoloq. Erġa' pprova.", + "Created": "Maħluq", + "Move to backlog": "Għall-backlog", + "Move to board": "Għall-bord", + "New task": "Kompitu ġdid", + "Nothing in the backlog": "Xejn fil-backlog", + "Rank": "Rank", + "Sort by": "Issortja skont", + "Sort by rank to reorder": "Issortja skont ir-rank biex terġa' tordna", + "Task actions": "Azzjonijiet tal-kompitu", + "All labels": "It-tikketti kollha", + "Filter tasks by label": "Iffiltra l-kompiti skont it-tikketta", + "View timeline": "Ara l-kronoloġija" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/mt.json b/l10n/mt.json index cdf1d19f..88c39e29 100644 --- a/l10n/mt.json +++ b/l10n/mt.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "L-aħħar kolonna ta' lest ma tistax titneħħa.", "WIP limit": "Limitu WIP", "over limit": "'il fuq mil-limitu", - "The status a card gets when it is moved into this column.": "L-istatus li tieħu karta meta titmexxa f'din il-kolonna." + "The status a card gets when it is moved into this column.": "L-istatus li tieħu karta meta titmexxa f'din il-kolonna.", + "All priorities": "Il-prijoritajiet kollha", + "Could not create the task. Please try again.": "Il-kompitu ma setax jinħoloq. Erġa' pprova.", + "Created": "Maħluq", + "Move to backlog": "Għall-backlog", + "Move to board": "Għall-bord", + "New task": "Kompitu ġdid", + "Nothing in the backlog": "Xejn fil-backlog", + "Rank": "Rank", + "Sort by": "Issortja skont", + "Sort by rank to reorder": "Issortja skont ir-rank biex terġa' tordna", + "Task actions": "Azzjonijiet tal-kompitu", + "All labels": "It-tikketti kollha", + "Filter tasks by label": "Iffiltra l-kompiti skont it-tikketta", + "View timeline": "Ara l-kronoloġija" }, "plurals": {} } diff --git a/l10n/nb.js b/l10n/nb.js index 4df34ca7..bfd393c5 100644 --- a/l10n/nb.js +++ b/l10n/nb.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Den siste ferdig-kolonnen kan ikke fjernes.", "WIP limit": "WIP-grense", "over limit": "over grensen", - "The status a card gets when it is moved into this column.": "Statusen et kort får når det flyttes til denne kolonnen." + "The status a card gets when it is moved into this column.": "Statusen et kort får når det flyttes til denne kolonnen.", + "All priorities": "Alle prioriteter", + "Could not create the task. Please try again.": "Oppgaven kunne ikke opprettes. Prøv igjen.", + "Created": "Opprettet", + "Move to backlog": "Til backlogen", + "Move to board": "Til tavlen", + "New task": "Ny oppgave", + "Nothing in the backlog": "Ingenting i backlogen", + "Rank": "Rang", + "Sort by": "Sorter etter", + "Sort by rank to reorder": "Sorter etter rang for å endre rekkefølgen", + "Task actions": "Oppgavehandlinger", + "All labels": "Alle etiketter", + "Filter tasks by label": "Filtrer oppgaver etter etikett", + "View timeline": "Vis tidslinje" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nb.json b/l10n/nb.json index 2342cab9..f8545010 100644 --- a/l10n/nb.json +++ b/l10n/nb.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Den siste ferdig-kolonnen kan ikke fjernes.", "WIP limit": "WIP-grense", "over limit": "over grensen", - "The status a card gets when it is moved into this column.": "Statusen et kort får når det flyttes til denne kolonnen." + "The status a card gets when it is moved into this column.": "Statusen et kort får når det flyttes til denne kolonnen.", + "All priorities": "Alle prioriteter", + "Could not create the task. Please try again.": "Oppgaven kunne ikke opprettes. Prøv igjen.", + "Created": "Opprettet", + "Move to backlog": "Til backlogen", + "Move to board": "Til tavlen", + "New task": "Ny oppgave", + "Nothing in the backlog": "Ingenting i backlogen", + "Rank": "Rang", + "Sort by": "Sorter etter", + "Sort by rank to reorder": "Sorter etter rang for å endre rekkefølgen", + "Task actions": "Oppgavehandlinger", + "All labels": "Alle etiketter", + "Filter tasks by label": "Filtrer oppgaver etter etikett", + "View timeline": "Vis tidslinje" }, "plurals": {} } diff --git a/l10n/nl.js b/l10n/nl.js index 7b65568a..f1484427 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -312,7 +312,21 @@ OC.L10N.register( "The last done column cannot be removed.": "De laatste klaar-kolom kan niet worden verwijderd.", "WIP limit": "WIP-limiet", "over limit": "boven de limiet", - "The status a card gets when it is moved into this column.": "De status die een kaart krijgt als die naar deze kolom wordt verplaatst." + "The status a card gets when it is moved into this column.": "De status die een kaart krijgt als die naar deze kolom wordt verplaatst.", + "All priorities": "Alle prioriteiten", + "Could not create the task. Please try again.": "De taak kon niet worden aangemaakt. Probeer het opnieuw.", + "Created": "Aangemaakt", + "Move to backlog": "Naar de backlog", + "Move to board": "Naar het bord", + "New task": "Nieuwe taak", + "Nothing in the backlog": "Niets in de backlog", + "Rank": "Rangorde", + "Sort by": "Sorteren op", + "Sort by rank to reorder": "Sorteer op rangorde om te herschikken", + "Task actions": "Taakacties", + "All labels": "Alle labels", + "Filter tasks by label": "Taken filteren op label", + "View timeline": "Tijdlijn bekijken" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nl.json b/l10n/nl.json index 4093c6ca..b762e089 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -311,7 +311,21 @@ "The last done column cannot be removed.": "De laatste klaar-kolom kan niet worden verwijderd.", "WIP limit": "WIP-limiet", "over limit": "boven de limiet", - "The status a card gets when it is moved into this column.": "De status die een kaart krijgt als die naar deze kolom wordt verplaatst." + "The status a card gets when it is moved into this column.": "De status die een kaart krijgt als die naar deze kolom wordt verplaatst.", + "All priorities": "Alle prioriteiten", + "Could not create the task. Please try again.": "De taak kon niet worden aangemaakt. Probeer het opnieuw.", + "Created": "Aangemaakt", + "Move to backlog": "Naar de backlog", + "Move to board": "Naar het bord", + "New task": "Nieuwe taak", + "Nothing in the backlog": "Niets in de backlog", + "Rank": "Rangorde", + "Sort by": "Sorteren op", + "Sort by rank to reorder": "Sorteer op rangorde om te herschikken", + "Task actions": "Taakacties", + "All labels": "Alle labels", + "Filter tasks by label": "Taken filteren op label", + "View timeline": "Tijdlijn bekijken" }, "plurals": {}, "pluralForm": "nplurals=2; plural=(n != 1);" diff --git a/l10n/pl.js b/l10n/pl.js index a9660624..e419176d 100644 --- a/l10n/pl.js +++ b/l10n/pl.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Ostatniej kolumny ukończonych nie można usunąć.", "WIP limit": "Limit WIP", "over limit": "powyżej limitu", - "The status a card gets when it is moved into this column.": "Status, który karta otrzymuje po przeniesieniu do tej kolumny." + "The status a card gets when it is moved into this column.": "Status, który karta otrzymuje po przeniesieniu do tej kolumny.", + "All priorities": "Wszystkie priorytety", + "Could not create the task. Please try again.": "Nie udało się utworzyć zadania. Spróbuj ponownie.", + "Created": "Utworzono", + "Move to backlog": "Do backlogu", + "Move to board": "Na tablicę", + "New task": "Nowe zadanie", + "Nothing in the backlog": "Backlog jest pusty", + "Rank": "Pozycja", + "Sort by": "Sortuj według", + "Sort by rank to reorder": "Sortuj według pozycji, aby zmienić kolejność", + "Task actions": "Akcje zadania", + "All labels": "Wszystkie etykiety", + "Filter tasks by label": "Filtruj zadania według etykiety", + "View timeline": "Pokaż oś czasu" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/pl.json b/l10n/pl.json index e525c56b..2fa0a7ff 100644 --- a/l10n/pl.json +++ b/l10n/pl.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Ostatniej kolumny ukończonych nie można usunąć.", "WIP limit": "Limit WIP", "over limit": "powyżej limitu", - "The status a card gets when it is moved into this column.": "Status, który karta otrzymuje po przeniesieniu do tej kolumny." + "The status a card gets when it is moved into this column.": "Status, który karta otrzymuje po przeniesieniu do tej kolumny.", + "All priorities": "Wszystkie priorytety", + "Could not create the task. Please try again.": "Nie udało się utworzyć zadania. Spróbuj ponownie.", + "Created": "Utworzono", + "Move to backlog": "Do backlogu", + "Move to board": "Na tablicę", + "New task": "Nowe zadanie", + "Nothing in the backlog": "Backlog jest pusty", + "Rank": "Pozycja", + "Sort by": "Sortuj według", + "Sort by rank to reorder": "Sortuj według pozycji, aby zmienić kolejność", + "Task actions": "Akcje zadania", + "All labels": "Wszystkie etykiety", + "Filter tasks by label": "Filtruj zadania według etykiety", + "View timeline": "Pokaż oś czasu" }, "plurals": {} } diff --git a/l10n/pt.js b/l10n/pt.js index f3d1134f..0c44e01b 100644 --- a/l10n/pt.js +++ b/l10n/pt.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "A última coluna de concluído não pode ser removida.", "WIP limit": "Limite WIP", "over limit": "acima do limite", - "The status a card gets when it is moved into this column.": "O estado que um cartão recebe quando é movido para esta coluna." + "The status a card gets when it is moved into this column.": "O estado que um cartão recebe quando é movido para esta coluna.", + "All priorities": "Todas as prioridades", + "Could not create the task. Please try again.": "Não foi possível criar a tarefa. Tente novamente.", + "Created": "Criada", + "Move to backlog": "Para o backlog", + "Move to board": "Para o quadro", + "New task": "Nova tarefa", + "Nothing in the backlog": "Nada no backlog", + "Rank": "Posição", + "Sort by": "Ordenar por", + "Sort by rank to reorder": "Ordene por posição para reordenar", + "Task actions": "Ações da tarefa", + "All labels": "Todas as etiquetas", + "Filter tasks by label": "Filtrar tarefas por etiqueta", + "View timeline": "Ver cronologia" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/pt.json b/l10n/pt.json index ba2c878d..7dbc08a6 100644 --- a/l10n/pt.json +++ b/l10n/pt.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "A última coluna de concluído não pode ser removida.", "WIP limit": "Limite WIP", "over limit": "acima do limite", - "The status a card gets when it is moved into this column.": "O estado que um cartão recebe quando é movido para esta coluna." + "The status a card gets when it is moved into this column.": "O estado que um cartão recebe quando é movido para esta coluna.", + "All priorities": "Todas as prioridades", + "Could not create the task. Please try again.": "Não foi possível criar a tarefa. Tente novamente.", + "Created": "Criada", + "Move to backlog": "Para o backlog", + "Move to board": "Para o quadro", + "New task": "Nova tarefa", + "Nothing in the backlog": "Nada no backlog", + "Rank": "Posição", + "Sort by": "Ordenar por", + "Sort by rank to reorder": "Ordene por posição para reordenar", + "Task actions": "Ações da tarefa", + "All labels": "Todas as etiquetas", + "Filter tasks by label": "Filtrar tarefas por etiqueta", + "View timeline": "Ver cronologia" }, "plurals": {} } diff --git a/l10n/rm.js b/l10n/rm.js index 9391f67d..27170937 100644 --- a/l10n/rm.js +++ b/l10n/rm.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "L'ultima colonna terminà na po betg vegnir allontanada.", "WIP limit": "Limita WIP", "over limit": "sur la limita", - "The status a card gets when it is moved into this column.": "Il status che ina carta survegn cura ch'ella vegn spustada en questa colonna." + "The status a card gets when it is moved into this column.": "Il status che ina carta survegn cura ch'ella vegn spustada en questa colonna.", + "All priorities": "Tut las prioritads", + "Could not create the task. Please try again.": "L'incumbensa n'ha betg pudì vegnir creada. Empruvai anc ina giada.", + "Created": "Creà", + "Move to backlog": "En il backlog", + "Move to board": "Sin la tavla", + "New task": "Nova incumbensa", + "Nothing in the backlog": "Nagut en il backlog", + "Rank": "Rang", + "Sort by": "Zavrar tenor", + "Sort by rank to reorder": "Zavrai tenor rang per midar la successiun", + "Task actions": "Acziuns da l'incumbensa", + "All labels": "Tut las etichettas", + "Filter tasks by label": "Filtrar las incumbensas tenor etichetta", + "View timeline": "Mussar la cronologia" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/rm.json b/l10n/rm.json index 7b812414..f1d90d44 100644 --- a/l10n/rm.json +++ b/l10n/rm.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "L'ultima colonna terminà na po betg vegnir allontanada.", "WIP limit": "Limita WIP", "over limit": "sur la limita", - "The status a card gets when it is moved into this column.": "Il status che ina carta survegn cura ch'ella vegn spustada en questa colonna." + "The status a card gets when it is moved into this column.": "Il status che ina carta survegn cura ch'ella vegn spustada en questa colonna.", + "All priorities": "Tut las prioritads", + "Could not create the task. Please try again.": "L'incumbensa n'ha betg pudì vegnir creada. Empruvai anc ina giada.", + "Created": "Creà", + "Move to backlog": "En il backlog", + "Move to board": "Sin la tavla", + "New task": "Nova incumbensa", + "Nothing in the backlog": "Nagut en il backlog", + "Rank": "Rang", + "Sort by": "Zavrar tenor", + "Sort by rank to reorder": "Zavrai tenor rang per midar la successiun", + "Task actions": "Acziuns da l'incumbensa", + "All labels": "Tut las etichettas", + "Filter tasks by label": "Filtrar las incumbensas tenor etichetta", + "View timeline": "Mussar la cronologia" }, "plurals": null } diff --git a/l10n/ro.js b/l10n/ro.js index 6fa3d4df..07e78aa4 100644 --- a/l10n/ro.js +++ b/l10n/ro.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Ultima coloană finalizate nu poate fi eliminată.", "WIP limit": "Limită WIP", "over limit": "peste limită", - "The status a card gets when it is moved into this column.": "Starea pe care o primește un card când este mutat în această coloană." + "The status a card gets when it is moved into this column.": "Starea pe care o primește un card când este mutat în această coloană.", + "All priorities": "Toate prioritățile", + "Could not create the task. Please try again.": "Sarcina nu a putut fi creată. Încercați din nou.", + "Created": "Creată", + "Move to backlog": "În backlog", + "Move to board": "Pe tablă", + "New task": "Sarcină nouă", + "Nothing in the backlog": "Nimic în backlog", + "Rank": "Rang", + "Sort by": "Sortează după", + "Sort by rank to reorder": "Sortați după rang pentru a reordona", + "Task actions": "Acțiuni sarcină", + "All labels": "Toate etichetele", + "Filter tasks by label": "Filtrează sarcinile după etichetă", + "View timeline": "Vezi cronologia" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ro.json b/l10n/ro.json index 87c0f50a..c5be6878 100644 --- a/l10n/ro.json +++ b/l10n/ro.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Ultima coloană finalizate nu poate fi eliminată.", "WIP limit": "Limită WIP", "over limit": "peste limită", - "The status a card gets when it is moved into this column.": "Starea pe care o primește un card când este mutat în această coloană." + "The status a card gets when it is moved into this column.": "Starea pe care o primește un card când este mutat în această coloană.", + "All priorities": "Toate prioritățile", + "Could not create the task. Please try again.": "Sarcina nu a putut fi creată. Încercați din nou.", + "Created": "Creată", + "Move to backlog": "În backlog", + "Move to board": "Pe tablă", + "New task": "Sarcină nouă", + "Nothing in the backlog": "Nimic în backlog", + "Rank": "Rang", + "Sort by": "Sortează după", + "Sort by rank to reorder": "Sortați după rang pentru a reordona", + "Task actions": "Acțiuni sarcină", + "All labels": "Toate etichetele", + "Filter tasks by label": "Filtrează sarcinile după etichetă", + "View timeline": "Vezi cronologia" }, "plurals": {} } diff --git a/l10n/ru.js b/l10n/ru.js index 8ebc63ed..279ca7f2 100644 --- a/l10n/ru.js +++ b/l10n/ru.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Последний столбец выполненных нельзя удалить.", "WIP limit": "Лимит WIP", "over limit": "выше лимита", - "The status a card gets when it is moved into this column.": "Статус, который получает карточка при перемещении в этот столбец." + "The status a card gets when it is moved into this column.": "Статус, который получает карточка при перемещении в этот столбец.", + "All priorities": "Все приоритеты", + "Could not create the task. Please try again.": "Не удалось создать задачу. Попробуйте ещё раз.", + "Created": "Создано", + "Move to backlog": "В бэклог", + "Move to board": "На доску", + "New task": "Новая задача", + "Nothing in the backlog": "В бэклоге ничего нет", + "Rank": "Ранг", + "Sort by": "Сортировать по", + "Sort by rank to reorder": "Сортируйте по рангу, чтобы изменить порядок", + "Task actions": "Действия с задачей", + "All labels": "Все метки", + "Filter tasks by label": "Фильтровать задачи по метке", + "View timeline": "Показать временную шкалу" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ru.json b/l10n/ru.json index 19deea5e..fdb91a43 100644 --- a/l10n/ru.json +++ b/l10n/ru.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Последний столбец выполненных нельзя удалить.", "WIP limit": "Лимит WIP", "over limit": "выше лимита", - "The status a card gets when it is moved into this column.": "Статус, который получает карточка при перемещении в этот столбец." + "The status a card gets when it is moved into this column.": "Статус, который получает карточка при перемещении в этот столбец.", + "All priorities": "Все приоритеты", + "Could not create the task. Please try again.": "Не удалось создать задачу. Попробуйте ещё раз.", + "Created": "Создано", + "Move to backlog": "В бэклог", + "Move to board": "На доску", + "New task": "Новая задача", + "Nothing in the backlog": "В бэклоге ничего нет", + "Rank": "Ранг", + "Sort by": "Сортировать по", + "Sort by rank to reorder": "Сортируйте по рангу, чтобы изменить порядок", + "Task actions": "Действия с задачей", + "All labels": "Все метки", + "Filter tasks by label": "Фильтровать задачи по метке", + "View timeline": "Показать временную шкалу" }, "plurals": {} } diff --git a/l10n/sk.js b/l10n/sk.js index 9749662a..ed173700 100644 --- a/l10n/sk.js +++ b/l10n/sk.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Posledný stĺpec hotovo nemožno odstrániť.", "WIP limit": "Limit WIP", "over limit": "nad limitom", - "The status a card gets when it is moved into this column.": "Stav, ktorý karta získa po presunutí do tohto stĺpca." + "The status a card gets when it is moved into this column.": "Stav, ktorý karta získa po presunutí do tohto stĺpca.", + "All priorities": "Všetky priority", + "Could not create the task. Please try again.": "Úlohu sa nepodarilo vytvoriť. Skúste to znova.", + "Created": "Vytvorené", + "Move to backlog": "Do backlogu", + "Move to board": "Na nástenku", + "New task": "Nová úloha", + "Nothing in the backlog": "Backlog je prázdny", + "Rank": "Poradie", + "Sort by": "Zoradiť podľa", + "Sort by rank to reorder": "Na zmenu poradia zoraďte podľa poradia", + "Task actions": "Akcie úlohy", + "All labels": "Všetky štítky", + "Filter tasks by label": "Filtrovať úlohy podľa štítka", + "View timeline": "Zobraziť časovú os" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sk.json b/l10n/sk.json index 58ab95b3..44dd8842 100644 --- a/l10n/sk.json +++ b/l10n/sk.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Posledný stĺpec hotovo nemožno odstrániť.", "WIP limit": "Limit WIP", "over limit": "nad limitom", - "The status a card gets when it is moved into this column.": "Stav, ktorý karta získa po presunutí do tohto stĺpca." + "The status a card gets when it is moved into this column.": "Stav, ktorý karta získa po presunutí do tohto stĺpca.", + "All priorities": "Všetky priority", + "Could not create the task. Please try again.": "Úlohu sa nepodarilo vytvoriť. Skúste to znova.", + "Created": "Vytvorené", + "Move to backlog": "Do backlogu", + "Move to board": "Na nástenku", + "New task": "Nová úloha", + "Nothing in the backlog": "Backlog je prázdny", + "Rank": "Poradie", + "Sort by": "Zoradiť podľa", + "Sort by rank to reorder": "Na zmenu poradia zoraďte podľa poradia", + "Task actions": "Akcie úlohy", + "All labels": "Všetky štítky", + "Filter tasks by label": "Filtrovať úlohy podľa štítka", + "View timeline": "Zobraziť časovú os" }, "plurals": null } diff --git a/l10n/sl.js b/l10n/sl.js index 038e711f..682b2601 100644 --- a/l10n/sl.js +++ b/l10n/sl.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Zadnjega stolpca opravljenih ni mogoče odstraniti.", "WIP limit": "Omejitev WIP", "over limit": "nad omejitvijo", - "The status a card gets when it is moved into this column.": "Stanje, ki ga kartica dobi, ko je premaknjena v ta stolpec." + "The status a card gets when it is moved into this column.": "Stanje, ki ga kartica dobi, ko je premaknjena v ta stolpec.", + "All priorities": "Vse prioritete", + "Could not create the task. Please try again.": "Opravila ni bilo mogoče ustvariti. Poskusite znova.", + "Created": "Ustvarjeno", + "Move to backlog": "V zaostanek", + "Move to board": "Na tablo", + "New task": "Novo opravilo", + "Nothing in the backlog": "V zaostanku ni ničesar", + "Rank": "Rang", + "Sort by": "Razvrsti po", + "Sort by rank to reorder": "Za spremembo vrstnega reda razvrstite po rangu", + "Task actions": "Dejanja opravila", + "All labels": "Vse oznake", + "Filter tasks by label": "Filtriraj opravila po oznaki", + "View timeline": "Prikaži časovnico" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sl.json b/l10n/sl.json index 20c9d423..03179dcd 100644 --- a/l10n/sl.json +++ b/l10n/sl.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Zadnjega stolpca opravljenih ni mogoče odstraniti.", "WIP limit": "Omejitev WIP", "over limit": "nad omejitvijo", - "The status a card gets when it is moved into this column.": "Stanje, ki ga kartica dobi, ko je premaknjena v ta stolpec." + "The status a card gets when it is moved into this column.": "Stanje, ki ga kartica dobi, ko je premaknjena v ta stolpec.", + "All priorities": "Vse prioritete", + "Could not create the task. Please try again.": "Opravila ni bilo mogoče ustvariti. Poskusite znova.", + "Created": "Ustvarjeno", + "Move to backlog": "V zaostanek", + "Move to board": "Na tablo", + "New task": "Novo opravilo", + "Nothing in the backlog": "V zaostanku ni ničesar", + "Rank": "Rang", + "Sort by": "Razvrsti po", + "Sort by rank to reorder": "Za spremembo vrstnega reda razvrstite po rangu", + "Task actions": "Dejanja opravila", + "All labels": "Vse oznake", + "Filter tasks by label": "Filtriraj opravila po oznaki", + "View timeline": "Prikaži časovnico" }, "plurals": null } diff --git a/l10n/sq.js b/l10n/sq.js index d77d7f07..67b2d689 100644 --- a/l10n/sq.js +++ b/l10n/sq.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Kolona e fundit e përfunduarave nuk mund të hiqet.", "WIP limit": "Kufiri WIP", "over limit": "mbi kufirin", - "The status a card gets when it is moved into this column.": "Gjendja që merr një kartë kur zhvendoset në këtë kolonë." + "The status a card gets when it is moved into this column.": "Gjendja që merr një kartë kur zhvendoset në këtë kolonë.", + "All priorities": "Të gjitha prioritetet", + "Could not create the task. Please try again.": "Detyra nuk u krijua dot. Provoni përsëri.", + "Created": "Krijuar", + "Move to backlog": "Te lista e pritjes", + "Move to board": "Te tabela", + "New task": "Detyrë e re", + "Nothing in the backlog": "Asgjë në listën e pritjes", + "Rank": "Renditja", + "Sort by": "Rendit sipas", + "Sort by rank to reorder": "Rendit sipas renditjes për ta ndryshuar radhën", + "Task actions": "Veprimet e detyrës", + "All labels": "Të gjitha etiketat", + "Filter tasks by label": "Filtro detyrat sipas etiketës", + "View timeline": "Shiko kronologjinë" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sq.json b/l10n/sq.json index 53f49a8a..e523359e 100644 --- a/l10n/sq.json +++ b/l10n/sq.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Kolona e fundit e përfunduarave nuk mund të hiqet.", "WIP limit": "Kufiri WIP", "over limit": "mbi kufirin", - "The status a card gets when it is moved into this column.": "Gjendja që merr një kartë kur zhvendoset në këtë kolonë." + "The status a card gets when it is moved into this column.": "Gjendja që merr një kartë kur zhvendoset në këtë kolonë.", + "All priorities": "Të gjitha prioritetet", + "Could not create the task. Please try again.": "Detyra nuk u krijua dot. Provoni përsëri.", + "Created": "Krijuar", + "Move to backlog": "Te lista e pritjes", + "Move to board": "Te tabela", + "New task": "Detyrë e re", + "Nothing in the backlog": "Asgjë në listën e pritjes", + "Rank": "Renditja", + "Sort by": "Rendit sipas", + "Sort by rank to reorder": "Rendit sipas renditjes për ta ndryshuar radhën", + "Task actions": "Veprimet e detyrës", + "All labels": "Të gjitha etiketat", + "Filter tasks by label": "Filtro detyrat sipas etiketës", + "View timeline": "Shiko kronologjinë" }, "plurals": {} } diff --git a/l10n/sr.js b/l10n/sr.js index a90b21f6..1e1f528d 100644 --- a/l10n/sr.js +++ b/l10n/sr.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Последња колона завршених не може да се уклони.", "WIP limit": "WIP ограничење", "over limit": "изнад ограничења", - "The status a card gets when it is moved into this column.": "Статус који картица добија када се премести у ову колону." + "The status a card gets when it is moved into this column.": "Статус који картица добија када се премести у ову колону.", + "All priorities": "Сви приоритети", + "Could not create the task. Please try again.": "Задатак није могуће направити. Покушајте поново.", + "Created": "Направљено", + "Move to backlog": "У заостатак", + "Move to board": "На таблу", + "New task": "Нови задатак", + "Nothing in the backlog": "Нема ничега у заостатку", + "Rank": "Ранг", + "Sort by": "Поређај по", + "Sort by rank to reorder": "Поређајте по рангу да бисте променили редослед", + "Task actions": "Радње задатка", + "All labels": "Све ознаке", + "Filter tasks by label": "Филтрирај задатке по ознаци", + "View timeline": "Прикажи временску линију" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sr.json b/l10n/sr.json index f435957a..c94fb86f 100644 --- a/l10n/sr.json +++ b/l10n/sr.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Последња колона завршених не може да се уклони.", "WIP limit": "WIP ограничење", "over limit": "изнад ограничења", - "The status a card gets when it is moved into this column.": "Статус који картица добија када се премести у ову колону." + "The status a card gets when it is moved into this column.": "Статус који картица добија када се премести у ову колону.", + "All priorities": "Сви приоритети", + "Could not create the task. Please try again.": "Задатак није могуће направити. Покушајте поново.", + "Created": "Направљено", + "Move to backlog": "У заостатак", + "Move to board": "На таблу", + "New task": "Нови задатак", + "Nothing in the backlog": "Нема ничега у заостатку", + "Rank": "Ранг", + "Sort by": "Поређај по", + "Sort by rank to reorder": "Поређајте по рангу да бисте променили редослед", + "Task actions": "Радње задатка", + "All labels": "Све ознаке", + "Filter tasks by label": "Филтрирај задатке по ознаци", + "View timeline": "Прикажи временску линију" }, "plurals": null } diff --git a/l10n/sv.js b/l10n/sv.js index 05cdbe0b..967b6f70 100644 --- a/l10n/sv.js +++ b/l10n/sv.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Den sista klar-kolumnen kan inte tas bort.", "WIP limit": "WIP-gräns", "over limit": "över gränsen", - "The status a card gets when it is moved into this column.": "Den status ett kort får när det flyttas till den här kolumnen." + "The status a card gets when it is moved into this column.": "Den status ett kort får när det flyttas till den här kolumnen.", + "All priorities": "Alla prioriteter", + "Could not create the task. Please try again.": "Uppgiften kunde inte skapas. Försök igen.", + "Created": "Skapad", + "Move to backlog": "Till backloggen", + "Move to board": "Till tavlan", + "New task": "Ny uppgift", + "Nothing in the backlog": "Inget i backloggen", + "Rank": "Rang", + "Sort by": "Sortera efter", + "Sort by rank to reorder": "Sortera efter rang för att ändra ordningen", + "Task actions": "Uppgiftsåtgärder", + "All labels": "Alla etiketter", + "Filter tasks by label": "Filtrera uppgifter efter etikett", + "View timeline": "Visa tidslinje" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sv.json b/l10n/sv.json index d1d85f84..ffa2b0eb 100644 --- a/l10n/sv.json +++ b/l10n/sv.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Den sista klar-kolumnen kan inte tas bort.", "WIP limit": "WIP-gräns", "over limit": "över gränsen", - "The status a card gets when it is moved into this column.": "Den status ett kort får när det flyttas till den här kolumnen." + "The status a card gets when it is moved into this column.": "Den status ett kort får när det flyttas till den här kolumnen.", + "All priorities": "Alla prioriteter", + "Could not create the task. Please try again.": "Uppgiften kunde inte skapas. Försök igen.", + "Created": "Skapad", + "Move to backlog": "Till backloggen", + "Move to board": "Till tavlan", + "New task": "Ny uppgift", + "Nothing in the backlog": "Inget i backloggen", + "Rank": "Rang", + "Sort by": "Sortera efter", + "Sort by rank to reorder": "Sortera efter rang för att ändra ordningen", + "Task actions": "Uppgiftsåtgärder", + "All labels": "Alla etiketter", + "Filter tasks by label": "Filtrera uppgifter efter etikett", + "View timeline": "Visa tidslinje" }, "plurals": null } diff --git a/l10n/tr.js b/l10n/tr.js index eceb7058..d50f0232 100644 --- a/l10n/tr.js +++ b/l10n/tr.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Son tamamlandı sütunu kaldırılamaz.", "WIP limit": "WIP sınırı", "over limit": "sınırın üzerinde", - "The status a card gets when it is moved into this column.": "Bir kart bu sütuna taşındığında aldığı durum." + "The status a card gets when it is moved into this column.": "Bir kart bu sütuna taşındığında aldığı durum.", + "All priorities": "Tüm öncelikler", + "Could not create the task. Please try again.": "Görev oluşturulamadı. Lütfen tekrar deneyin.", + "Created": "Oluşturulma", + "Move to backlog": "Birikime taşı", + "Move to board": "Panoya taşı", + "New task": "Yeni görev", + "Nothing in the backlog": "Birikimde hiçbir şey yok", + "Rank": "Sıra", + "Sort by": "Sırala", + "Sort by rank to reorder": "Yeniden sıralamak için sıraya göre sıralayın", + "Task actions": "Görev işlemleri", + "All labels": "Tüm etiketler", + "Filter tasks by label": "Görevleri etikete göre filtrele", + "View timeline": "Zaman çizelgesini görüntüle" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/tr.json b/l10n/tr.json index fc10322b..5d6a6591 100644 --- a/l10n/tr.json +++ b/l10n/tr.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Son tamamlandı sütunu kaldırılamaz.", "WIP limit": "WIP sınırı", "over limit": "sınırın üzerinde", - "The status a card gets when it is moved into this column.": "Bir kart bu sütuna taşındığında aldığı durum." + "The status a card gets when it is moved into this column.": "Bir kart bu sütuna taşındığında aldığı durum.", + "All priorities": "Tüm öncelikler", + "Could not create the task. Please try again.": "Görev oluşturulamadı. Lütfen tekrar deneyin.", + "Created": "Oluşturulma", + "Move to backlog": "Birikime taşı", + "Move to board": "Panoya taşı", + "New task": "Yeni görev", + "Nothing in the backlog": "Birikimde hiçbir şey yok", + "Rank": "Sıra", + "Sort by": "Sırala", + "Sort by rank to reorder": "Yeniden sıralamak için sıraya göre sıralayın", + "Task actions": "Görev işlemleri", + "All labels": "Tüm etiketler", + "Filter tasks by label": "Görevleri etikete göre filtrele", + "View timeline": "Zaman çizelgesini görüntüle" }, "plurals": {} } diff --git a/l10n/uk.js b/l10n/uk.js index 03854f32..ae8959cf 100644 --- a/l10n/uk.js +++ b/l10n/uk.js @@ -291,7 +291,21 @@ OC.L10N.register( "The last done column cannot be removed.": "Останній стовпець виконаних не можна видалити.", "WIP limit": "Ліміт WIP", "over limit": "понад ліміт", - "The status a card gets when it is moved into this column.": "Статус, який отримує картка, коли її переміщують у цей стовпець." + "The status a card gets when it is moved into this column.": "Статус, який отримує картка, коли її переміщують у цей стовпець.", + "All priorities": "Усі пріоритети", + "Could not create the task. Please try again.": "Не вдалося створити завдання. Спробуйте ще раз.", + "Created": "Створено", + "Move to backlog": "До беклогу", + "Move to board": "На дошку", + "New task": "Нове завдання", + "Nothing in the backlog": "У беклозі нічого немає", + "Rank": "Ранг", + "Sort by": "Сортувати за", + "Sort by rank to reorder": "Сортуйте за рангом, щоб змінити порядок", + "Task actions": "Дії із завданням", + "All labels": "Усі мітки", + "Filter tasks by label": "Фільтрувати завдання за міткою", + "View timeline": "Переглянути часову шкалу" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/uk.json b/l10n/uk.json index 6392301b..5f259dcc 100644 --- a/l10n/uk.json +++ b/l10n/uk.json @@ -290,7 +290,21 @@ "The last done column cannot be removed.": "Останній стовпець виконаних не можна видалити.", "WIP limit": "Ліміт WIP", "over limit": "понад ліміт", - "The status a card gets when it is moved into this column.": "Статус, який отримує картка, коли її переміщують у цей стовпець." + "The status a card gets when it is moved into this column.": "Статус, який отримує картка, коли її переміщують у цей стовпець.", + "All priorities": "Усі пріоритети", + "Could not create the task. Please try again.": "Не вдалося створити завдання. Спробуйте ще раз.", + "Created": "Створено", + "Move to backlog": "До беклогу", + "Move to board": "На дошку", + "New task": "Нове завдання", + "Nothing in the backlog": "У беклозі нічого немає", + "Rank": "Ранг", + "Sort by": "Сортувати за", + "Sort by rank to reorder": "Сортуйте за рангом, щоб змінити порядок", + "Task actions": "Дії із завданням", + "All labels": "Усі мітки", + "Filter tasks by label": "Фільтрувати завдання за міткою", + "View timeline": "Переглянути часову шкалу" }, "plurals": {} } diff --git a/openspec/changes/backlog-list/design.md b/openspec/changes/archive/2026-09-28-backlog-list/design.md similarity index 75% rename from openspec/changes/backlog-list/design.md rename to openspec/changes/archive/2026-09-28-backlog-list/design.md index 685b4395..6a725f5a 100644 --- a/openspec/changes/backlog-list/design.md +++ b/openspec/changes/archive/2026-09-28-backlog-list/design.md @@ -51,6 +51,22 @@ a hint "Sort by rank to reorder". The sort choice is in the query string, like t "New task" on the backlog opens `TaskFormDialog` without a column. The main spec's "Create a task" scenario (placed in the backlog by default) is met here. +Amended at build (28 Sep): `TaskFormDialog` does not exist at HEAD (it belongs to +`tasks-create-edit-delete`, not built yet). The backlog has an inline "New task" field and an Add +button that create the task with status open, priority normal, no column and the bottom rank. + +### Decision 6: filters without the board's filter bar + +Amended at build (28 Sep): `boards-filters` is not built yet, so the backlog carries its own two +filters: a priority choice and a "Cancelled" switch, both in the query string with the sort. When +`boards-filters` lands, its bar replaces the priority choice here. + +### Decision 7: a card taken off the board is open again + +"Move to backlog" clears `column`, puts the card last in rank and sets status `open`, so a card +taken off a done lane shows in the backlog (done tasks are hidden there) and the server clears +its `completedAt`. + ## Risks / trade-offs - [Done tasks without a column stay hidden] -> they are finished work; the Cancelled filter and the diff --git a/openspec/changes/backlog-list/proposal.md b/openspec/changes/archive/2026-09-28-backlog-list/proposal.md similarity index 100% rename from openspec/changes/backlog-list/proposal.md rename to openspec/changes/archive/2026-09-28-backlog-list/proposal.md diff --git a/openspec/changes/backlog-list/specs/backlog/spec.md b/openspec/changes/archive/2026-09-28-backlog-list/specs/backlog/spec.md similarity index 94% rename from openspec/changes/backlog-list/specs/backlog/spec.md rename to openspec/changes/archive/2026-09-28-backlog-list/specs/backlog/spec.md index 1ef30737..358fa822 100644 --- a/openspec/changes/backlog-list/specs/backlog/spec.md +++ b/openspec/changes/archive/2026-09-28-backlog-list/specs/backlog/spec.md @@ -42,6 +42,8 @@ filter. Tier: MVP. #### Scenario: Sort by due date +@e2e exclude the backlog has no due-date field to set from the UI yet; the comparator is covered by tests/vitest/backlog.spec.js (sortBacklog) + - **GIVEN** backlog tasks due on 3, 1 and 2 October - **WHEN** a member sorts by due date - **THEN** they are listed 1, 2, 3 October diff --git a/openspec/changes/archive/2026-09-28-backlog-list/tasks.md b/openspec/changes/archive/2026-09-28-backlog-list/tasks.md new file mode 100644 index 00000000..b4e26c0d --- /dev/null +++ b/openspec/changes/archive/2026-09-28-backlog-list/tasks.md @@ -0,0 +1,22 @@ +# Tasks: backlog-list + +## 1. List + +- [x] 1.1 Replace the placeholder in `src/views/ProjectBacklog.vue` with a list of the project's column-less, not-done tasks in rank order, with an empty state "Nothing in the backlog". Verify: vitest spec on a `backlogTasks(tasks)` helper; Playwright e2e opens the backlog of a seeded project. Built: `src/views/ProjectBacklog.vue`, `src/utils/backlogHelpers.js` backlogTasks(). Tests: `tests/vitest/backlog.spec.js`; e2e `tests/e2e/backlog.spec.ts`. +- [x] 1.2 "New task" on the backlog creating a task without a column at the bottom. Verify: Playwright e2e. Built as an inline "New task" field on the page (there is no TaskFormDialog at HEAD); store action `createTask`. Tests: `backlog.spec.js` (newBacklogTask payload validated against the real task schema); e2e. + +## 2. Rank, sort and filter + +- [x] 2.1 Drag handle and "Move up" and "Move down" writing sparse `columnOrder`. Verify: vitest spec on the rank helper; Playwright e2e reorders and reloads. Built: drag a row onto another (rank sort only) and Move up / Move down, through `orderFor` and `orderPatchesForStep` in `src/utils/columnHelpers.js`. Tests: `backlog.spec.js` (ranking); e2e. +- [x] 2.2 Sort by rank, priority, due date or created, kept in the query string; dragging disabled when not sorted by rank. Verify: vitest spec on the comparators. Test: `backlog.spec.js` (sortBacklog). +- [x] 2.3 The board filter bar from `boards-filters` on the backlog, plus a "Cancelled" filter. Verify: Playwright e2e filters by priority. Built without `boards-filters` (not built yet): a priority filter and a Cancelled switch on the backlog, in the query string. Test: `backlog.spec.js` (filterBacklog, backlogTasks cancelled). + +## 3. Moving + +- [x] 3.1 "Move to board" with a column menu on a backlog row, and "Move to backlog" in the board card menu. Verify: Playwright e2e moves a task both ways. Built: backlog row menu lists the board columns; board card menu gains "Move to backlog" (`moveToBacklogPatch`). Tests: `backlog.spec.js`; e2e. + +## 4. Spec hygiene, copy and verification + +- [x] 4.1 At archive, retire the "Placeholder until task management is implemented" scenario of `openspec/specs/projects.md`. Verify: the archived spec no longer mentions the placeholder. Done in this change: the scenario is replaced in `openspec/specs/projects.md`. +- [x] 4.2 New strings in `l10n/en.json` and `l10n/nl.json`. Verify: `npm run check:l10n` exits 0. Built in all 37 catalogues; also the three inherited board strings. +- [x] 4.3 `openspec validate backlog-list --type change --strict` passes. Run with openspec 1.12.0. diff --git a/openspec/changes/backlog-list/tasks.md b/openspec/changes/backlog-list/tasks.md deleted file mode 100644 index 1137cfce..00000000 --- a/openspec/changes/backlog-list/tasks.md +++ /dev/null @@ -1,22 +0,0 @@ -# Tasks: backlog-list - -## 1. List - -- [ ] 1.1 Replace the placeholder in `src/views/ProjectBacklog.vue` with a list of the project's column-less, not-done tasks in rank order, with an empty state "Nothing in the backlog". Verify: vitest spec on a `backlogTasks(tasks)` helper; Playwright e2e opens the backlog of a seeded project. -- [ ] 1.2 "New task" on the backlog creating a task without a column at the bottom. Verify: Playwright e2e. - -## 2. Rank, sort and filter - -- [ ] 2.1 Drag handle and "Move up" and "Move down" writing sparse `columnOrder`. Verify: vitest spec on the rank helper; Playwright e2e reorders and reloads. -- [ ] 2.2 Sort by rank, priority, due date or created, kept in the query string; dragging disabled when not sorted by rank. Verify: vitest spec on the comparators. -- [ ] 2.3 The board filter bar from `boards-filters` on the backlog, plus a "Cancelled" filter. Verify: Playwright e2e filters by priority. - -## 3. Moving - -- [ ] 3.1 "Move to board" with a column menu on a backlog row, and "Move to backlog" in the board card menu. Verify: Playwright e2e moves a task both ways. - -## 4. Spec hygiene, copy and verification - -- [ ] 4.1 At archive, retire the "Placeholder until task management is implemented" scenario of `openspec/specs/projects.md`. Verify: the archived spec no longer mentions the placeholder. -- [ ] 4.2 New strings in `l10n/en.json` and `l10n/nl.json`. Verify: `npm run check:l10n` exits 0. -- [ ] 4.3 `openspec validate backlog-list --type change --strict` passes. diff --git a/openspec/parity/capabilities.json b/openspec/parity/capabilities.json index 96615011..a5e780c2 100644 --- a/openspec/parity/capabilities.json +++ b/openspec/parity/capabilities.json @@ -1725,15 +1725,15 @@ }, "built": { "state": "building", - "evidence": "src/views/ProjectBacklog.vue:24-30 renders NcEmptyContent 'Backlog view coming soon' / 'Task management will be available in a future update.', an explicit placeholder, not a working list view.", - "reachedOn": "ProjectBacklog page /projects/:id/backlog (placeholder only)", + "evidence": "src/views/ProjectBacklog.vue lists only the tasks in no board column (backlog); no view lists the board's own cards as a list", + "reachedOn": "ProjectBacklog page /projects/:id/backlog (backlog only)", "owner": "ConductionNL/planninq" }, "provider": "planninq", "providerHow": "read-from-code", "feature": "kanban-board", "featureConfidence": "high", - "note": "the Backlog button on the board (src/views/ProjectBoard.vue:43-48) navigates to a page that is explicitly a stub; there is no working list rendering of the board's tasks anywhere." + "note": "the Backlog button on the board (src/views/ProjectBoard.vue:43-48) navigates to a page that is explicitly a stub; there is no working list rendering of the board's tasks anywhere. Read again on 2026-09-28: the backlog page is now a working list (2026-09-28-backlog-list) of the tasks in no column, not the same tasks as the board, so this row stays building until a board and list toggle exists." }, { "id": "brd-card-badges", @@ -2848,7 +2848,7 @@ "area": "backlog", "name": "Keep tasks that are not yet planned in a backlog apart from the board.", "origin": "own-code", - "planninq": "no", + "planninq": "yes", "nextcloud-deck": "no", "openproject": "yes", "plane": "partial", @@ -2866,23 +2866,25 @@ "plane": "corpus: plane/round4/code-census.md §2 state groups 'backlog, unstarted, started, completed, cancelled, triage'; the backlog is a state group shown as board columns unless filtered out, not a separate list apart from the board. Intake (off by default) is a triage queue, not a backlog (menu-tree.md) ; source read at v1.4.2: apps/api/plane/db/models/state.py:15 BACKLOG is a state group; apps/api/plane/utils/issue_filters.py:298-300 display type 'backlog' narrows to that group, offered in packages/constants/src/issue/filter.ts:121 type active or backlog. The backlog is a filter over the same layouts, not a separate list apart from the board" }, "built": { - "state": "specified", - "evidence": "src/views/ProjectBacklog.vue:20-26 is a literal placeholder: NcEmptyContent with name 'Backlog view coming soon' and description 'Task management will be available in a future update.' Component-level comment confirms: 'Stays a placeholder until tasks#REQ-Task-CRUD lands.'", - "reachedOn": "route /projects/:id/backlog exists and is reachable (ProjectBoard.vue:46-48 'Backlog' button) but renders only the placeholder", + "state": "built", + "evidence": "src/views/ProjectBacklog.vue:266 rows() lists the tasks in no column, not done, in rank order (src/utils/backlogHelpers.js backlogTasks()); :376 createTask() adds a task with no column last in rank", + "reachedOn": "ProjectBacklog page /projects/:id/backlog", "owner": "ConductionNL/planninq", - "change": "openspec/changes/backlog-list" + "change": "openspec/changes/archive/2026-09-28-backlog-list", + "readOn": "2026-09-28" }, "provider": "planninq", "providerHow": "read-from-code", "feature": "backlog", - "featureConfidence": "high" + "featureConfidence": "high", + "note": "Built on 2026-09-28 by openspec/changes/archive/2026-09-28-backlog-list." }, { "id": "bkl-to-board", "area": "backlog", "name": "Move a task from the backlog onto the board.", "origin": "own-code", - "planninq": "no", + "planninq": "yes", "nextcloud-deck": "no", "openproject": "yes", "plane": "yes", @@ -2900,23 +2902,25 @@ "plane": "corpus: plane/round4/code-census.md §2 State group; moving a work item from a backlog-group state to an unstarted or started state is an ordinary state change (drag or state menu) ; source read at v1.4.2: apps/web/core/components/issues/issue-layouts/kanban/kanban-group.tsx:136,171 drop a card from a backlog-group column into another state column; apps/web/core/components/issues/issue-modal/components/default-properties.tsx:90 state_id dropdown; apps/api/plane/app/views/issue/base.py:628 partial_update; apps/api/plane/db/models/state.py:15 BACKLOG group" }, "built": { - "state": "specified", - "evidence": "Same placeholder page, src/views/ProjectBacklog.vue:20-26; no task list, no move-to-board action exists at all.", - "reachedOn": "nothing reaches it", + "state": "built", + "evidence": "src/views/ProjectBacklog.vue:457 moveToBoard() puts a backlog task at the bottom of the chosen lane with its status (columnHelpers buildMovePatch()); src/views/ProjectBoard.vue:730 moveToBacklog() takes a card back", + "reachedOn": "ProjectBacklog page /projects/:id/backlog; ProjectBoard page, card menu", "owner": "ConductionNL/planninq", - "change": "openspec/changes/backlog-list" + "change": "openspec/changes/archive/2026-09-28-backlog-list", + "readOn": "2026-09-28" }, "provider": "planninq", "providerHow": "read-from-code", "feature": "backlog", - "featureConfidence": "high" + "featureConfidence": "high", + "note": "Built on 2026-09-28 by openspec/changes/archive/2026-09-28-backlog-list." }, { "id": "bkl-sort-filter", "area": "backlog", "name": "Sort and filter the backlog.", "origin": "own-code", - "planninq": "no", + "planninq": "yes", "nextcloud-deck": "no", "openproject": "partial", "plane": "yes", @@ -2934,23 +2938,25 @@ "plane": "corpus: plane/round4/code-census.md §9 IssueView filters and display_filters ; usability.md 'Layouts, filtering ... are all good' ; source read at v1.4.2: apps/web/core/components/issues/issue-layouts/filters/header/display-filters/display-filters-selection.tsx:112-118 order_by selector; apps/web/core/components/work-item-filters/filters-hoc/project-level.tsx:206 filters; apps/api/plane/utils/issue_filters.py:298-300 backlog type; apps/api/plane/utils/order_queryset.py:12 order_by allowlist" }, "built": { - "state": "specified", - "evidence": "Same placeholder page, src/views/ProjectBacklog.vue:20-26; no sort or filter controls exist.", - "reachedOn": "nothing reaches it", + "state": "built", + "evidence": "src/views/ProjectBacklog.vue:266 rows() applies filterBacklog() (priority, Cancelled switch) and sortBacklog() (rank, priority, due date, created), both kept in the query string", + "reachedOn": "ProjectBacklog page /projects/:id/backlog, toolbar", "owner": "ConductionNL/planninq", - "change": "openspec/changes/backlog-list" + "change": "openspec/changes/archive/2026-09-28-backlog-list", + "readOn": "2026-09-28" }, "provider": "planninq", "providerHow": "read-from-code", "feature": "backlog", - "featureConfidence": "high" + "featureConfidence": "high", + "note": "Built on 2026-09-28 by openspec/changes/archive/2026-09-28-backlog-list." }, { "id": "bkl-rank", "area": "backlog", "name": "Rank the backlog by dragging items up and down.", "origin": "competitor-derived", - "planninq": "no", + "planninq": "yes", "nextcloud-deck": "no", "openproject": "yes", "plane": "yes", @@ -2968,16 +2974,18 @@ "plane": "corpus: plane/round4/code-census.md §2 Issue 'sort_order float (:158)'; list and board support manual drag ordering (kanban/block.tsx:206 draggable) ; source read at v1.4.2: apps/web/core/components/issues/issue-layouts/list/block.tsx:117 draggable rows in the list layout and apps/web/core/components/issues/issue-layouts/kanban/block.tsx:206 on the board; apps/web/core/components/issues/issue-layouts/utils.tsx:491-518 new sort_order between neighbours; apps/api/plane/db/models/issue.py:158 sort_order" }, "built": { - "state": "specified", - "evidence": "No backlog list exists to rank (src/views/ProjectBacklog.vue:20-26 placeholder) and no ordering field/drag UI for a backlog exists anywhere.", - "reachedOn": "nothing reaches it", + "state": "built", + "evidence": "src/views/ProjectBacklog.vue:425 step() (Move up, Move down) and :437 onDrop() (drag a row in front of another) write columnOrder through columnHelpers orderPatchesForStep()/orderFor()", + "reachedOn": "ProjectBacklog page /projects/:id/backlog, row menu and drag", "owner": "ConductionNL/planninq", - "change": "openspec/changes/backlog-list" + "change": "openspec/changes/archive/2026-09-28-backlog-list", + "readOn": "2026-09-28" }, "provider": "planninq", "providerHow": "read-from-code", "feature": "backlog", - "featureConfidence": "high" + "featureConfidence": "high", + "note": "Built on 2026-09-28 by openspec/changes/archive/2026-09-28-backlog-list." }, { "id": "bkl-sprints", diff --git a/openspec/specs/backlog/spec.md b/openspec/specs/backlog/spec.md new file mode 100644 index 00000000..01e68e1f --- /dev/null +++ b/openspec/specs/backlog/spec.md @@ -0,0 +1,67 @@ +# backlog Specification + +## Purpose +A project's backlog: the tasks in no board column, which members create into, rank, sort, filter and move to and from the board. Built by the archived change 2026-09-28-backlog-list. + +## Requirements + +### Requirement: The backlog lists the tasks that are not on the board + +The system MUST show on /projects/:id/backlog every task of the project that has no column and is +not done, in rank order, and MUST let a project member create a task directly into it. +Tier: MVP (docs/FEATURES.md). + +#### Scenario: Open the backlog + +- **GIVEN** project "Vergunningen" has two tasks without a column and five on the board +- **WHEN** a member opens its backlog +- **THEN** the two column-less tasks are listed and the five board tasks are not + +#### Scenario: Create into the backlog + +- **GIVEN** a member is on the backlog +- **WHEN** they choose "New task", enter "Check the zoning plan" and save +- **THEN** the task is listed last in the backlog and does not show on the board + +### Requirement: A member can rank the backlog + +The system MUST let a project member put backlog tasks in their own order by dragging or with +"Move up" and "Move down", and MUST keep that order after a reload. Tier: V1. + +#### Scenario: Rank with the keyboard + +- **GIVEN** the backlog lists A, B, C +- **WHEN** a member chooses "Move up" on C twice +- **THEN** the backlog lists C, A, B, also after a reload + +### Requirement: A member can sort and filter the backlog + +The system MUST let a project member sort the backlog by rank, priority, due date or creation date +and filter it with the board's filters, and MUST show cancelled tasks only under the "Cancelled" +filter. Tier: MVP. + +#### Scenario: Sort by due date + +@e2e exclude the backlog has no due-date field to set from the UI yet; the comparator is covered by tests/vitest/backlog.spec.js (sortBacklog) + +- **GIVEN** backlog tasks due on 3, 1 and 2 October +- **WHEN** a member sorts by due date +- **THEN** they are listed 1, 2, 3 October +- **AND** dragging is disabled with the hint "Sort by rank to reorder" + +### Requirement: A member can move tasks between the backlog and the board + +The system MUST let a project member move a backlog task to a board column of their choice, and +move a board card back to the backlog. Tier: MVP. + +#### Scenario: Plan a task + +- **GIVEN** "Check the zoning plan" is in the backlog +- **WHEN** a member chooses "Move to board" and then "In progress" +- **THEN** the task leaves the backlog and shows at the bottom of the In progress lane with that lane's status + +#### Scenario: Take a card off the board + +- **GIVEN** a card in the To do lane +- **WHEN** a member chooses "Move to backlog" in its card menu +- **THEN** the card leaves the board and is listed last in the backlog diff --git a/openspec/specs/projects.md b/openspec/specs/projects.md index c0c7b97e..cdcc38c6 100644 --- a/openspec/specs/projects.md +++ b/openspec/specs/projects.md @@ -301,7 +301,7 @@ The OpenRegister gate check MUST occur in the App root component (`App.vue`) bef > Added by the `retrofit-2026-05-24-reverse-spec-projects-backlog` change — retroactively specifies the existing `/projects/:id/backlog` placeholder route. -The system MUST provide a navigable Backlog route per project that renders inside the project context (breadcrumb back to project board) and shows a placeholder until task management ships. +The system MUST provide a navigable Backlog route per project that renders inside the project context (breadcrumb back to project board) and lists the project's backlog (see `openspec/specs/backlog/spec.md`). #### Scenario: Navigate to backlog route - GIVEN the user is authenticated and a project exists with id `:id` @@ -315,14 +315,14 @@ The system MUST provide a navigable Backlog route per project that renders insid - WHEN the `ProjectBacklog` view mounts - THEN the view MUST call `projectsStore.fetchProject(:id)` so the breadcrumb title resolves to the project title rather than the raw UUID -#### Scenario: Placeholder until task management is implemented +#### Scenario: The backlog lists the project's tasks - GIVEN the `ProjectBacklog` view has rendered -- WHEN there is no task-management implementation yet -- THEN the view MUST show an `NcEmptyContent` with name "Backlog view coming soon" and description "Task management will be available in a future update." -- AND the placeholder MUST use the `FormatListBulleted` MDI icon so the feature intent is recognisable +- WHEN the project has tasks without a board column +- THEN the view MUST list them as the working backlog described in `openspec/specs/backlog/spec.md` +- AND the former "Backlog view coming soon" placeholder MUST NOT show #### Notes -- The placeholder copy is deliberate. It MUST stay aligned with the unimplemented `tasks#REQ-Task-CRUD` and `kanban-board#REQ-Kanban-Board-View` REQs (currently Bucket 3b — planned, never started). When task management lands, this REQ should either be retired in favour of a real backlog REQ or rewritten to describe the populated list view. +- The placeholder was retired on 2026-09-28 by the `backlog-list` change; the backlog's behaviour lives in `openspec/specs/backlog/spec.md`. - The view does not own loading or error states beyond what the projects store provides — those are covered by `Requirement: Loading and Error States` above. --- diff --git a/src/store/projects.js b/src/store/projects.js index de3c00be..252f6840 100644 --- a/src/store/projects.js +++ b/src/store/projects.js @@ -869,6 +869,36 @@ export const useProjectsStore = defineStore('projects', { } }, + // ── 2.14b createTask ────────────────────────────────────────────── + + /** + * Create a task through OpenRegister's object API (ADR-022). The + * server stamps the members list and refuses a caller who is not a + * member of the task's project (ProjectMemberAccessListener). + * + * @param {object} data The task fields; `title`, `status` and `project` at least + * @return {Promise} The created task, or null on failure + * + * @spec openspec/changes/backlog-list/tasks.md#task-1.2 + */ + async createTask(data) { + try { + const url = generateUrl('/apps/openregister/api/objects/planninq/task') + const response = await fetch(url, { + method: 'POST', + headers: buildHeaders(), + body: JSON.stringify(data), + }) + if (!response.ok) { + return null + } + return await response.json() + } catch (err) { + console.error('createTask error:', err) + return null + } + }, + // ── 2.15 updateTask ──────────────────────────────────────────────── /** diff --git a/src/utils/backlogHelpers.js b/src/utils/backlogHelpers.js new file mode 100644 index 00000000..4c640b66 --- /dev/null +++ b/src/utils/backlogHelpers.js @@ -0,0 +1,122 @@ +/** + * Backlog helpers (backlog-list): the backlog is the set of a project's tasks + * that sit in no board column. Rank is data (`columnOrder`, the same field a + * board lane uses), sort is a view. + * + * @spec openspec/changes/backlog-list/tasks.md#task-1.1 + */ +import { orderFor } from './columnHelpers.js' + +/** The sort choices, the first one the default. */ +export const BACKLOG_SORTS = ['rank', 'priority', 'due', 'created'] + +/** Priority weight, most urgent first. */ +const PRIORITY_RANK = { urgent: 0, high: 1, normal: 2, low: 3 } + +/** + * @param {object} task A task. + * @return {number} Its rank, 0 when unset. + */ +function rank(task) { + return Number(task?.columnOrder) || 0 +} + +/** + * The backlog: tasks without a column, in rank order. Done tasks are finished + * work and never listed; cancelled ones only when `cancelled` is asked for. + * + * @param {Array} tasks The project's tasks. + * @param {{cancelled?: boolean}} [options] Show the cancelled tasks instead. + * @return {Array} + * + * @spec openspec/changes/backlog-list/tasks.md#task-1.1 + */ +export function backlogTasks(tasks = [], { cancelled = false } = {}) { + return (tasks || []) + .filter((task) => task && !task.column && task.status !== 'done') + .filter((task) => (task.status === 'cancelled') === cancelled) + .sort((a, b) => rank(a) - rank(b)) +} + +/** + * The backlog in the chosen order. An unknown choice keeps rank order. + * + * @param {Array} tasks The backlog tasks. + * @param {string} sort One of BACKLOG_SORTS. + * @return {Array} + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.2 + */ +export function sortBacklog(tasks = [], sort = 'rank') { + const list = [...(tasks || [])] + const byRank = (a, b) => rank(a) - rank(b) + const comparators = { + priority: (a, b) => (PRIORITY_RANK[a.priority ?? 'normal'] ?? 2) - (PRIORITY_RANK[b.priority ?? 'normal'] ?? 2) || byRank(a, b), + due: (a, b) => { + if (!a.dueDate || !b.dueDate) { + return (a.dueDate ? -1 : 0) + (b.dueDate ? 1 : 0) || byRank(a, b) + } + return String(a.dueDate).localeCompare(String(b.dueDate)) || byRank(a, b) + }, + created: (a, b) => { + const ca = a?.['@self']?.created ?? '' + const cb = b?.['@self']?.created ?? '' + if (!ca || !cb) { + return (ca ? -1 : 0) + (cb ? 1 : 0) || byRank(a, b) + } + return new Date(ca) - new Date(cb) || byRank(a, b) + }, + } + return list.sort(comparators[sort] || byRank) +} + +/** + * The backlog narrowed to one priority; an empty choice keeps every task. + * A task without a priority counts as normal, the schema default. + * + * @param {Array} tasks The backlog tasks. + * @param {{priority?: string}} filters The filter choices. + * @return {Array} + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.3 + */ +export function filterBacklog(tasks = [], { priority = '' } = {}) { + if (!priority) { + return [...(tasks || [])] + } + return (tasks || []).filter((task) => (task.priority || 'normal') === priority) +} + +/** + * The PATCH that takes a card off the board: no column, last in the backlog, + * back to open. + * + * @param {Array} backlog The current backlog. + * @return {{column: null, columnOrder: number, status: string}} + * + * @spec openspec/changes/backlog-list/tasks.md#task-3.1 + */ +export function moveToBacklogPatch(backlog = []) { + return { column: null, columnOrder: orderFor(backlog), status: 'open' } +} + +/** + * A new task for the backlog: no column, last in rank. + * + * @param {string} title The title as typed. + * @param {string} projectId The project UUID. + * @param {Array} backlog The current backlog. + * @return {object} + * + * @spec openspec/changes/backlog-list/tasks.md#task-1.2 + */ +export function newBacklogTask(title, projectId, backlog = []) { + return { + title: String(title ?? '').trim(), + status: 'open', + priority: 'normal', + project: projectId, + column: null, + columnOrder: orderFor(backlog), + } +} diff --git a/src/utils/columnHelpers.js b/src/utils/columnHelpers.js index 07e80bf6..6963b1dc 100644 --- a/src/utils/columnHelpers.js +++ b/src/utils/columnHelpers.js @@ -94,6 +94,27 @@ export function mappedStatus(column) { return column?.type === 'done' ? 'done' : undefined } +/** + * The `columnOrder` for a card put at the bottom of a list, or just before `beforeTask`. + * + * @param {Array} laneTasks The list's cards, without the moved card. + * @param {object|null} [beforeTask] The card to land in front of. + * @return {number} + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.1 + */ +export function orderFor(laneTasks = [], beforeTask = null) { + const lane = sortCards(laneTasks || []) + const index = beforeTask ? lane.findIndex((task) => idOf(task) === idOf(beforeTask)) : -1 + if (index === -1) { + const last = lane.length ? Number(lane[lane.length - 1].columnOrder) || 0 : 0 + return last + ORDER_STEP + } + const after = Number(lane[index].columnOrder) || 0 + const before = index > 0 ? Number(lane[index - 1].columnOrder) || 0 : after - ORDER_STEP + return Math.floor((before + after) / 2) +} + /** * The PATCH that moves a card into a lane: at the bottom, or just before `beforeTask`. * @@ -105,18 +126,7 @@ export function mappedStatus(column) { * @spec openspec/changes/boards-configurable-columns/tasks.md#task-3.2 */ export function buildMovePatch(column, laneTasks = [], beforeTask = null) { - const lane = sortCards(laneTasks || []) - let columnOrder - const index = beforeTask ? lane.findIndex((task) => idOf(task) === idOf(beforeTask)) : -1 - if (index === -1) { - const last = lane.length ? Number(lane[lane.length - 1].columnOrder) || 0 : 0 - columnOrder = last + ORDER_STEP - } else { - const after = Number(lane[index].columnOrder) || 0 - const before = index > 0 ? Number(lane[index - 1].columnOrder) || 0 : after - ORDER_STEP - columnOrder = Math.floor((before + after) / 2) - } - const patch = { column: idOf(column), columnOrder } + const patch = { column: idOf(column), columnOrder: orderFor(laneTasks, beforeTask) } const status = mappedStatus(column) if (status) { patch.status = status diff --git a/src/views/ProjectBacklog.vue b/src/views/ProjectBacklog.vue index c529cbbe..ded1f706 100644 --- a/src/views/ProjectBacklog.vue +++ b/src/views/ProjectBacklog.vue @@ -15,19 +15,120 @@ {{ t('planninq', 'Backlog') }} - +

{{ t('planninq', 'Backlog') }}

+
+ + + {{ t('planninq', 'Add') }} + + +
+ + +
+ + + + {{ t('planninq', 'Cancelled') }} + +
+

+ {{ t('planninq', 'Sort by rank to reorder') }} +

+ +
+
- + v-else-if="rows.length === 0" + :name="t('planninq', 'Nothing in the backlog')"> + +
    +
  1. +
  2. +
@@ -35,27 +136,85 @@ /** * ProjectBacklog view. * - * Renders the `/projects/:id/backlog` route as a navigable shell — breadcrumb - * back to the project board plus a placeholder NcEmptyContent. Hydrates the - * projects store on direct deep link so the breadcrumb resolves the project - * title rather than echoing the raw UUID. Stays a placeholder until - * tasks#REQ-Task-CRUD lands. + * The project's backlog: every task in no board column, in rank order. A + * member adds a task straight into it, ranks it by dragging a row or with + * "Move up" and "Move down", sorts it by rank, priority, due date or creation + * date, filters it by priority or to the cancelled tasks, and moves a task + * onto the board with a column choice. Sort and filters live in the query + * string. Hydrates the project on a deep link so the breadcrumb shows its + * title. * - * @spec openspec/changes/retrofit-2026-05-24-reverse-spec-projects-backlog/tasks.md#task-1 + * @spec openspec/changes/backlog-list/tasks.md#task-1.1 */ -import { NcButton, NcEmptyContent } from '@nextcloud/vue' +import { showError } from '@nextcloud/dialogs' +import { + NcActionButton, + NcActionCaption, + NcActions, + NcButton, + NcCheckboxRadioSwitch, + NcEmptyContent, + NcLoadingIcon, + NcSelect, + NcTextField, +} from '@nextcloud/vue' +import ArrowDownIcon from 'vue-material-design-icons/ArrowDown.vue' +import ArrowRightIcon from 'vue-material-design-icons/ArrowRight.vue' +import ArrowUpIcon from 'vue-material-design-icons/ArrowUp.vue' +import DragIcon from 'vue-material-design-icons/Drag.vue' import FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue' import { useProjectsStore } from '../store/projects.js' +import { + BACKLOG_SORTS, + backlogTasks, + filterBacklog, + newBacklogTask, + sortBacklog, +} from '../utils/backlogHelpers.js' +import { + buildMovePatch, + groupTasksByColumn, + orderFor, + orderPatchesForStep, + sortColumns, +} from '../utils/columnHelpers.js' export default { name: 'ProjectBacklog', components: { + NcActionButton, + NcActionCaption, + NcActions, NcButton, + NcCheckboxRadioSwitch, NcEmptyContent, + NcLoadingIcon, + NcSelect, + NcTextField, + ArrowDownIcon, + ArrowRightIcon, + ArrowUpIcon, + DragIcon, FormatListBulleted, }, + data() { + return { + /** @type {Array} Every task of the project. */ + tasks: [], + /** @type {Array} The project's board columns. */ + boardColumns: [], + loading: false, + newTitle: '', + creating: false, + /** @type {object|null} The row being dragged. */ + draggingTask: null, + /** @type {string|null} Id of the row hovered during a drag. */ + dropTargetId: null, + } + }, + computed: { /** * @spec exclude Store passthrough — returns the projects Pinia store. @@ -70,18 +229,235 @@ export default { projectTitle() { return this.projectsStore.activeProject?.title || this.$route.params.id }, + + /** + * @spec exclude Display helper — the board columns in lane order. + */ + columns() { + return sortColumns(this.boardColumns) + }, + + /** + * The sort from the query string, rank when absent or unknown. + * + * @return {string} + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.2 + */ + sort() { + const sort = this.$route.query.sort + return BACKLOG_SORTS.includes(sort) ? sort : 'rank' + }, + + /** + * @spec exclude Display helper — whether the Cancelled filter is on. + */ + showCancelled() { + return this.$route.query.cancelled === '1' + }, + + /** + * The backlog as shown: filtered, then in the chosen order. + * + * @return {Array} + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.3 + */ + rows() { + const backlog = backlogTasks(this.tasks, { cancelled: this.showCancelled }) + return sortBacklog(filterBacklog(backlog, { priority: this.$route.query.priority || '' }), this.sort) + }, + + /** + * @spec exclude Display helper — the sort choices. + */ + sortOptions() { + return [ + { id: 'rank', label: this.t('planninq', 'Rank') }, + { id: 'priority', label: this.t('planninq', 'Priority') }, + { id: 'due', label: this.t('planninq', 'Due date') }, + { id: 'created', label: this.t('planninq', 'Created') }, + ] + }, + + /** + * @spec exclude Display helper — the selected sort choice. + */ + sortOption() { + return this.sortOptions.find((option) => option.id === this.sort) + }, + + /** + * @spec exclude Display helper — the priority filter choices. + */ + priorityOptions() { + return [ + { id: '', label: this.t('planninq', 'All priorities') }, + { id: 'urgent', label: this.t('planninq', 'Urgent') }, + { id: 'high', label: this.t('planninq', 'High') }, + { id: 'normal', label: this.t('planninq', 'Normal') }, + { id: 'low', label: this.t('planninq', 'Low') }, + ] + }, + + /** + * @spec exclude Display helper — the selected priority filter. + */ + priorityOption() { + return this.priorityOptions.find((option) => option.id === (this.$route.query.priority || '')) || this.priorityOptions[0] + }, }, /** - * Hydrate the projects store on direct deep link so the breadcrumb - * title resolves to the project title rather than the raw UUID. + * Hydrate the project on a deep link, then load the tasks and columns. * - * @spec openspec/changes/retrofit-2026-05-24-reverse-spec-projects-backlog/tasks.md#task-1 + * @spec openspec/changes/backlog-list/tasks.md#task-1.1 */ async mounted() { - if (!this.projectsStore.activeProject || this.projectsStore.activeProject.id !== this.$route.params.id) { - await this.projectsStore.fetchProject(this.$route.params.id) + const id = this.$route.params.id + if (!this.projectsStore.activeProject || this.projectsStore.activeProject.id !== id) { + await this.projectsStore.fetchProject(id) } + await this.load() + }, + + methods: { + /** + * Load the project's tasks and board columns. + * + * @return {Promise} + * + * @spec openspec/changes/backlog-list/tasks.md#task-1.1 + */ + async load() { + const id = this.$route.params.id + this.loading = true + try { + this.boardColumns = await this.projectsStore.fetchColumns(id) + this.tasks = await this.projectsStore.fetchTasks(id) + } finally { + this.loading = false + } + }, + + /** + * Change the sort or a filter, keeping the rest of the query. + * + * @param {object} change Query keys to set; undefined removes a key. + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.2 + */ + setQuery(change) { + const query = { ...this.$route.query, ...change } + for (const key of Object.keys(query)) { + if (query[key] === undefined) { + delete query[key] + } + } + this.$router.replace({ query }) + }, + + /** + * @param {string} priority A task priority. + * @return {string} Its label. + * @spec exclude Display helper — the label of a priority value. + */ + priorityLabel(priority) { + return (this.priorityOptions.find((option) => option.id === (priority || 'normal')) || {}).label + }, + + /** + * Create a task straight into the backlog, last in rank. + * + * @return {Promise} + * + * @spec openspec/changes/backlog-list/tasks.md#task-1.2 + */ + async createTask() { + if (this.newTitle.trim() === '' || this.creating) { + return + } + this.creating = true + const payload = newBacklogTask(this.newTitle, this.$route.params.id, backlogTasks(this.tasks)) + const created = await this.projectsStore.createTask(payload) + this.creating = false + if (!created) { + showError(this.t('planninq', 'Could not create the task. Please try again.')) + return + } + this.newTitle = '' + this.tasks = [...this.tasks, created] + }, + + /** + * Apply task patches optimistically and persist them; revert on failure. + * + * @param {Array} patches Each `{ id, ...fields }`. + * @return {Promise} + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.1 + */ + async patchTasks(patches) { + if (!patches.length) { + return + } + const previous = this.tasks + const byId = new Map(patches.map((patch) => [patch.id, patch])) + this.tasks = this.tasks.map((task) => byId.has(task.id) ? { ...task, ...byId.get(task.id) } : task) + for (const { id, ...fields } of patches) { + if (!(await this.projectsStore.updateTask(id, fields))) { + this.tasks = previous + showError(this.t('planninq', 'Could not move the task. Please try again.')) + return + } + } + }, + + /** + * Move a row one step up (-1) or down (+1) in rank. + * + * @param {object} task The task. + * @param {number} direction -1 or +1. + * @return {Promise} + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.1 + */ + async step(task, direction) { + await this.patchTasks(orderPatchesForStep(this.rows, task, direction)) + }, + + /** + * Drop the dragged row in front of another; only when sorted by rank. + * + * @param {object} beforeTask The row it was dropped on. + * @return {Promise} + * + * @spec openspec/changes/backlog-list/tasks.md#task-2.1 + */ + async onDrop(beforeTask) { + const task = this.draggingTask + this.draggingTask = null + this.dropTargetId = null + if (!task || this.sort !== 'rank' || task.id === beforeTask.id) { + return + } + const list = this.rows.filter((row) => row.id !== task.id) + await this.patchTasks([{ id: task.id, columnOrder: orderFor(list, beforeTask) }]) + }, + + /** + * Plan a task: put it at the bottom of a board lane with that lane's status. + * + * @param {object} task The backlog task. + * @param {object} column The chosen column. + * @return {Promise} + * + * @spec openspec/changes/backlog-list/tasks.md#task-3.1 + */ + async moveToBoard(task, column) { + const lane = groupTasksByColumn(this.tasks, this.columns)[column.id] || [] + await this.patchTasks([{ id: task.id, ...buildMovePatch(column, lane) }]) + }, }, } @@ -102,6 +478,11 @@ export default { } .project-backlog__header { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + justify-content: space-between; + gap: 12px; margin-bottom: 16px; } @@ -110,4 +491,60 @@ export default { font-size: 22px; font-weight: 600; } + +.project-backlog__create, +.project-backlog__toolbar { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + gap: 8px; +} + +.project-backlog__toolbar { + margin-bottom: 8px; +} + +.project-backlog__hint { + margin: 0 0 8px; + color: var(--color-text-maxcontrast); +} + +.project-backlog__list { + margin: 0; + padding: 0; + list-style: none; +} + +.project-backlog__row { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 8px; + border-bottom: 1px solid var(--color-border); +} + +.project-backlog__row--drop-target { + box-shadow: inset 0 2px 0 var(--color-primary-element); +} + +.project-backlog__handle { + cursor: grab; + color: var(--color-text-maxcontrast); +} + +.project-backlog__title { + flex: 1; + justify-content: flex-start; +} + +.project-backlog__meta { + font-size: 12px; + color: var(--color-text-maxcontrast); +} + +.project-backlog__loading { + display: flex; + justify-content: center; + padding: 24px; +} diff --git a/src/views/ProjectBoard.vue b/src/views/ProjectBoard.vue index 1a01558e..fe5dd1c7 100644 --- a/src/views/ProjectBoard.vue +++ b/src/views/ProjectBoard.vue @@ -185,6 +185,15 @@ {{ t('planninq', 'Move down') }} + + + {{ t('planninq', 'Move to backlog') }} + } + * + * @spec openspec/changes/backlog-list/tasks.md#task-3.1 + */ + async moveToBacklog(task) { + await this.patchTasks([{ id: task.id, ...moveToBacklogPatch(backlogTasks(this.tasks)) }]) + }, + /** * Move a card one step up (-1) or down (+1) in its lane. * diff --git a/tests/e2e/backlog.spec.ts b/tests/e2e/backlog.spec.ts new file mode 100644 index 00000000..38a07a41 --- /dev/null +++ b/tests/e2e/backlog.spec.ts @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * E2E coverage for the backlog capability (backlog-list). + * + * @e2e backlog::open-the-backlog + * @e2e backlog::create-into-the-backlog + * @e2e backlog::rank-with-the-keyboard + * @e2e backlog::plan-a-task + * @e2e backlog::take-a-card-off-the-board + * + * "Sort by due date" carries an `@e2e exclude` in the spec: the backlog has + * no due-date field to set from the UI yet, and tests/vitest/backlog.spec.js + * covers the comparator. + * + * Titles carry a run id so repeated nightly runs do not collide. + */ + +import { expect, test } from '@playwright/test' +import { openFixtureProjectBoard } from './nav.ts' + +async function openBacklog(page) { + const projectId = await openFixtureProjectBoard(page) + await page.getByRole('button', { name: 'View backlog' }).click() + await expect(page).toHaveURL(new RegExp(`/projects/${projectId}/backlog`)) + return projectId +} + +async function addTask(page, title: string) { + await page.getByTestId('backlog-new-task').locator('input').fill(title) + await page.getByTestId('backlog-add').click() + await expect(page.locator(`[data-testid="backlog-row"][data-title="${title}"]`)).toHaveCount(1) +} + +async function rowTitles(page) { + return page.getByTestId('backlog-row').evaluateAll((rows) => rows.map((row) => row.getAttribute('data-title'))) +} + +test.describe('Backlog', () => { + // @e2e backlog::open-the-backlog + // @e2e backlog::create-into-the-backlog + test('a new task lands last in the backlog and not on the board', async ({ page }) => { + const title = `Check the zoning plan ${Date.now()}` + await openBacklog(page) + await addTask(page, title) + expect((await rowTitles(page)).at(-1)).toBe(title) + + await page.goBack() + await expect(page.locator('[data-cy="kanban-board"]')).toHaveCount(1) + await expect(page.locator(`[data-testid="task-card"][aria-label="${title}"]`)).toHaveCount(0) + }) + + // @e2e backlog::rank-with-the-keyboard + test('Move up twice on C gives C, A, B, also after a reload', async ({ page }) => { + const run = Date.now() + const [a, b, c] = ['A', 'B', 'C'].map((name) => `${name} ${run}`) + await openBacklog(page) + for (const title of [a, b, c]) { + await addTask(page, title) + } + for (let i = 0; i < 2; i++) { + await page.locator(`[data-testid="backlog-row"][data-title="${c}"]`).getByRole('button', { name: 'Task actions' }).click() + await page.getByRole('menuitem', { name: 'Move up' }).click() + } + const ours = (titles) => titles.filter((title) => [a, b, c].includes(title)) + expect(ours(await rowTitles(page))).toEqual([c, a, b]) + + await page.reload() + await expect(page.getByTestId('backlog-list')).toBeVisible() + expect(ours(await rowTitles(page))).toEqual([c, a, b]) + }) + + // @e2e backlog::plan-a-task + // @e2e backlog::take-a-card-off-the-board + test('a task moves to the board and back to the backlog', async ({ page }) => { + const title = `Plan me ${Date.now()}` + await openBacklog(page) + await addTask(page, title) + await page.locator(`[data-testid="backlog-row"][data-title="${title}"]`).getByRole('button', { name: 'Task actions' }).click() + await page.getByRole('menuitem', { name: 'In progress' }).click() + await expect(page.locator(`[data-testid="backlog-row"][data-title="${title}"]`)).toHaveCount(0) + + await page.goBack() + const card = page.locator('section.kanban-column[data-column="In progress"]').locator(`[data-testid="task-card"][aria-label="${title}"]`) + await expect(card).toHaveCount(1) + + await card.getByRole('button', { name: 'Move task to another column' }).click() + await page.getByRole('menuitem', { name: 'Move to backlog' }).click() + await expect(page.locator(`[data-testid="task-card"][aria-label="${title}"]`)).toHaveCount(0) + + await page.getByRole('button', { name: 'View backlog' }).click() + expect((await rowTitles(page)).at(-1)).toBe(title) + }) +}) diff --git a/tests/vitest/backlog.spec.js b/tests/vitest/backlog.spec.js new file mode 100644 index 00000000..0b0e13d6 --- /dev/null +++ b/tests/vitest/backlog.spec.js @@ -0,0 +1,126 @@ +/** + * Vitest unit tests for the backlog helpers (backlog-list). + * + * @spec openspec/changes/backlog-list/tasks.md#task-1.1 + */ +import Ajv from 'ajv' +import addFormats from 'ajv-formats' +import { readFileSync } from 'node:fs' +import { describe, expect, it } from 'vitest' +import { + backlogTasks, + filterBacklog, + moveToBacklogPatch, + newBacklogTask, + sortBacklog, +} from '../../src/utils/backlogHelpers.js' +import { buildMovePatch, orderPatchesForStep } from '../../src/utils/columnHelpers.js' + +const project = '11111111-1111-4111-8111-111111111111' + +describe('backlogTasks', () => { + const tasks = [ + { id: 'b1', title: 'B1', status: 'open', column: null, columnOrder: 2000 }, + { id: 'b2', title: 'B2', status: 'in_progress', columnOrder: 1000 }, + { id: 'c1', title: 'On board', status: 'open', column: 'col-1', columnOrder: 0 }, + { id: 'd1', title: 'Finished', status: 'done', column: null }, + { id: 'x1', title: 'Dropped', status: 'cancelled', column: null }, + ] + + it('lists the column-less, not done tasks in rank order (scenario: open the backlog)', () => { + expect(backlogTasks(tasks).map((t) => t.id)).toEqual(['b2', 'b1']) + }) + + it('shows cancelled tasks only under the Cancelled filter', () => { + expect(backlogTasks(tasks, { cancelled: true }).map((t) => t.id)).toEqual(['x1']) + }) +}) + +describe('sortBacklog', () => { + const tasks = [ + { id: 'a', columnOrder: 1000, priority: 'low', dueDate: '2026-10-03', '@self': { created: '2026-09-03T10:00:00+00:00' } }, + { id: 'b', columnOrder: 2000, priority: 'urgent', dueDate: '2026-10-01', '@self': { created: '2026-09-01T10:00:00+00:00' } }, + { id: 'c', columnOrder: 3000, priority: 'normal', dueDate: '2026-10-02', '@self': { created: '2026-09-02T10:00:00+00:00' } }, + { id: 'd', columnOrder: 4000, priority: 'high' }, + ] + + it('sorts by due date, 1 2 3 October, undated last (scenario: sort by due date)', () => { + expect(sortBacklog(tasks, 'due').map((t) => t.id)).toEqual(['b', 'c', 'a', 'd']) + }) + + it('sorts by priority, urgent first', () => { + expect(sortBacklog(tasks, 'priority').map((t) => t.id)).toEqual(['b', 'd', 'c', 'a']) + }) + + it('sorts by creation date and by rank', () => { + expect(sortBacklog(tasks, 'created').map((t) => t.id)).toEqual(['b', 'c', 'a', 'd']) + expect(sortBacklog(tasks, 'rank').map((t) => t.id)).toEqual(['a', 'b', 'c', 'd']) + expect(sortBacklog(tasks, 'nonsense').map((t) => t.id)).toEqual(['a', 'b', 'c', 'd']) + }) +}) + +describe('filterBacklog', () => { + it('keeps only the chosen priority', () => { + const tasks = [{ id: 'a', priority: 'high' }, { id: 'b', priority: 'low' }, { id: 'c' }] + expect(filterBacklog(tasks, { priority: 'high' }).map((t) => t.id)).toEqual(['a']) + expect(filterBacklog(tasks, { priority: '' }).map((t) => t.id)).toEqual(['a', 'b', 'c']) + expect(filterBacklog(tasks, { priority: 'normal' }).map((t) => t.id)).toEqual(['c']) + }) +}) + +describe('ranking', () => { + it('Move up on C twice gives C, A, B (scenario: rank with the keyboard)', () => { + let list = [{ id: 'A', columnOrder: 1000 }, { id: 'B', columnOrder: 2000 }, { id: 'C', columnOrder: 3000 }] + for (let i = 0; i < 2; i++) { + const patches = orderPatchesForStep(list, list.find((t) => t.id === 'C'), -1) + list = list.map((t) => ({ ...t, ...(patches.find((p) => p.id === t.id) || {}) })) + } + expect(sortBacklog(list, 'rank').map((t) => t.id)).toEqual(['C', 'A', 'B']) + }) +}) + +describe('moving between backlog and board', () => { + it('plans a task at the bottom of the chosen lane with its status (scenario: plan a task)', () => { + const lane = [{ id: 'x', columnOrder: 5000 }] + expect(buildMovePatch({ id: 'doing', status: 'in_progress' }, lane)).toEqual({ column: 'doing', columnOrder: 6000, status: 'in_progress' }) + }) + + it('takes a card off the board to the bottom of the backlog (scenario: take a card off the board)', () => { + const backlog = [{ id: 'b', columnOrder: 3000 }] + expect(moveToBacklogPatch(backlog)).toEqual({ column: null, columnOrder: 4000, status: 'open' }) + }) +}) + +describe('newBacklogTask', () => { + const register = JSON.parse(readFileSync(new URL('../../lib/Settings/planninq_register.json', import.meta.url), 'utf8')) + const schema = register.components.schemas.task + const properties = {} + for (const [name, property] of Object.entries(schema.properties)) { + const { $ref, visible, nullable, ...rest } = property + for (const key of Object.keys(rest)) { + if (key.startsWith('x-')) { + delete rest[key] + } + } + if (nullable) { + rest.type = [rest.type, 'null'] + if (rest.enum) { + rest.enum = [...rest.enum, null] + } + } + properties[name] = rest + } + const ajv = new Ajv({ strict: false }) + addFormats(ajv) + const validate = ajv.compile({ type: 'object', required: schema.required, properties }) + + it('creates a valid task last in the backlog, with no column (scenario: create into the backlog)', () => { + const payload = newBacklogTask(' Check the zoning plan ', project, [{ id: 'a', columnOrder: 1000 }]) + expect(payload).toEqual({ title: 'Check the zoning plan', status: 'open', priority: 'normal', project, column: null, columnOrder: 2000 }) + expect(validate(payload), JSON.stringify(validate.errors)).toBe(true) + }) + + it('control: the validator refuses a task without a title', () => { + expect(validate({ status: 'open', project })).toBe(false) + }) +}) From 86840fa2a6ead6f2f597a0788b73999a489a9a84 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 28 Sep 2026 22:45:30 +0200 Subject: [PATCH 156/289] feat(board): members switch the board page between the board and a list of the same cards (#705) * feat(board): switch the board page between the board and a list of the same cards boards-list-toggle, written and built for brd-list-toggle, the one planninq row left at building with no change: a Board and List switch kept in ?view=list, and a list of the board's cards (after the label filter) in lane then card order with column, priority and due date. * chore(openspec): archive boards-list-toggle, brd-list-toggle built --- l10n/be.js | 6 +- l10n/be.json | 6 +- l10n/bg.js | 6 +- l10n/bg.json | 6 +- l10n/bs.js | 6 +- l10n/bs.json | 6 +- l10n/ca.js | 6 +- l10n/ca.json | 6 +- l10n/cs.js | 6 +- l10n/cs.json | 6 +- l10n/da.js | 6 +- l10n/da.json | 6 +- l10n/de.js | 6 +- l10n/de.json | 6 +- l10n/el.js | 6 +- l10n/el.json | 6 +- l10n/en.js | 6 +- l10n/en.json | 6 +- l10n/es.js | 6 +- l10n/es.json | 6 +- l10n/et.js | 6 +- l10n/et.json | 6 +- l10n/fi.js | 6 +- l10n/fi.json | 6 +- l10n/fr.js | 6 +- l10n/fr.json | 6 +- l10n/ga.js | 6 +- l10n/ga.json | 6 +- l10n/hr.js | 6 +- l10n/hr.json | 6 +- l10n/hu.js | 6 +- l10n/hu.json | 6 +- l10n/is.js | 6 +- l10n/is.json | 6 +- l10n/it.js | 6 +- l10n/it.json | 6 +- l10n/lb.js | 6 +- l10n/lb.json | 6 +- l10n/lt.js | 6 +- l10n/lt.json | 6 +- l10n/lv.js | 6 +- l10n/lv.json | 6 +- l10n/mk.js | 6 +- l10n/mk.json | 6 +- l10n/mt.js | 6 +- l10n/mt.json | 6 +- l10n/nb.js | 6 +- l10n/nb.json | 6 +- l10n/nl.js | 6 +- l10n/nl.json | 6 +- l10n/pl.js | 6 +- l10n/pl.json | 6 +- l10n/pt.js | 6 +- l10n/pt.json | 6 +- l10n/rm.js | 6 +- l10n/rm.json | 6 +- l10n/ro.js | 6 +- l10n/ro.json | 6 +- l10n/ru.js | 6 +- l10n/ru.json | 6 +- l10n/sk.js | 6 +- l10n/sk.json | 6 +- l10n/sl.js | 6 +- l10n/sl.json | 6 +- l10n/sq.js | 6 +- l10n/sq.json | 6 +- l10n/sr.js | 6 +- l10n/sr.json | 6 +- l10n/sv.js | 6 +- l10n/sv.json | 6 +- l10n/tr.js | 6 +- l10n/tr.json | 6 +- l10n/uk.js | 6 +- l10n/uk.json | 6 +- .../2026-09-28-boards-list-toggle/design.md | 37 +++++ .../2026-09-28-boards-list-toggle/proposal.md | 40 ++++++ .../specs/board-list/spec.md | 23 +++ .../2026-09-28-boards-list-toggle/tasks.md | 11 ++ openspec/parity/capabilities.json | 14 +- openspec/parity/gap-decisions.json | 8 ++ openspec/specs/board-list/spec.md | 26 ++++ src/utils/columnHelpers.js | 17 +++ src/views/ProjectBoard.vue | 133 ++++++++++++++++++ tests/e2e/board-list.spec.ts | 40 ++++++ tests/vitest/boardColumns.spec.js | 13 ++ 85 files changed, 726 insertions(+), 80 deletions(-) create mode 100644 openspec/changes/archive/2026-09-28-boards-list-toggle/design.md create mode 100644 openspec/changes/archive/2026-09-28-boards-list-toggle/proposal.md create mode 100644 openspec/changes/archive/2026-09-28-boards-list-toggle/specs/board-list/spec.md create mode 100644 openspec/changes/archive/2026-09-28-boards-list-toggle/tasks.md create mode 100644 openspec/specs/board-list/spec.md create mode 100644 tests/e2e/board-list.spec.ts diff --git a/l10n/be.js b/l10n/be.js index 6f6758de..67667378 100644 --- a/l10n/be.js +++ b/l10n/be.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Дзеянні з задачай", "All labels": "Усе меткі", "Filter tasks by label": "Фільтраваць задачы па метцы", - "View timeline": "Праглядзець часовую шкалу" + "View timeline": "Праглядзець часовую шкалу", + "Board": "Дошка", + "List": "Спіс", + "Column": "Слупок", + "Board or list": "Дошка або спіс" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/be.json b/l10n/be.json index 31cdb272..c2cdeedd 100644 --- a/l10n/be.json +++ b/l10n/be.json @@ -304,7 +304,11 @@ "Task actions": "Дзеянні з задачай", "All labels": "Усе меткі", "Filter tasks by label": "Фільтраваць задачы па метцы", - "View timeline": "Праглядзець часовую шкалу" + "View timeline": "Праглядзець часовую шкалу", + "Board": "Дошка", + "List": "Спіс", + "Column": "Слупок", + "Board or list": "Дошка або спіс" }, "plurals": {} } diff --git a/l10n/bg.js b/l10n/bg.js index 7cd57f15..80cc0281 100644 --- a/l10n/bg.js +++ b/l10n/bg.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Действия със задачата", "All labels": "Всички етикети", "Filter tasks by label": "Филтриране на задачите по етикет", - "View timeline": "Преглед на времевата линия" + "View timeline": "Преглед на времевата линия", + "Board": "Табло", + "List": "Списък", + "Column": "Колона", + "Board or list": "Табло или списък" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/bg.json b/l10n/bg.json index 04731566..77d81a02 100644 --- a/l10n/bg.json +++ b/l10n/bg.json @@ -304,7 +304,11 @@ "Task actions": "Действия със задачата", "All labels": "Всички етикети", "Filter tasks by label": "Филтриране на задачите по етикет", - "View timeline": "Преглед на времевата линия" + "View timeline": "Преглед на времевата линия", + "Board": "Табло", + "List": "Списък", + "Column": "Колона", + "Board or list": "Табло или списък" }, "plurals": null } diff --git a/l10n/bs.js b/l10n/bs.js index c4b5d5bd..c3b09c46 100644 --- a/l10n/bs.js +++ b/l10n/bs.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Radnje zadatka", "All labels": "Sve oznake", "Filter tasks by label": "Filtriraj zadatke po oznaci", - "View timeline": "Prikaži vremensku liniju" + "View timeline": "Prikaži vremensku liniju", + "Board": "Tabla", + "List": "Lista", + "Column": "Kolona", + "Board or list": "Tabla ili lista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/bs.json b/l10n/bs.json index 1daff23d..9dfaad95 100644 --- a/l10n/bs.json +++ b/l10n/bs.json @@ -304,7 +304,11 @@ "Task actions": "Radnje zadatka", "All labels": "Sve oznake", "Filter tasks by label": "Filtriraj zadatke po oznaci", - "View timeline": "Prikaži vremensku liniju" + "View timeline": "Prikaži vremensku liniju", + "Board": "Tabla", + "List": "Lista", + "Column": "Kolona", + "Board or list": "Tabla ili lista" }, "plurals": null } diff --git a/l10n/ca.js b/l10n/ca.js index c179d3dc..cf809476 100644 --- a/l10n/ca.js +++ b/l10n/ca.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Accions de la tasca", "All labels": "Totes les etiquetes", "Filter tasks by label": "Filtra les tasques per etiqueta", - "View timeline": "Mostra la cronologia" + "View timeline": "Mostra la cronologia", + "Board": "Tauler", + "List": "Llista", + "Column": "Columna", + "Board or list": "Tauler o llista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ca.json b/l10n/ca.json index b7753d44..3f74c7aa 100644 --- a/l10n/ca.json +++ b/l10n/ca.json @@ -304,7 +304,11 @@ "Task actions": "Accions de la tasca", "All labels": "Totes les etiquetes", "Filter tasks by label": "Filtra les tasques per etiqueta", - "View timeline": "Mostra la cronologia" + "View timeline": "Mostra la cronologia", + "Board": "Tauler", + "List": "Llista", + "Column": "Columna", + "Board or list": "Tauler o llista" }, "plurals": null } diff --git a/l10n/cs.js b/l10n/cs.js index 73c992ea..35f7dbec 100644 --- a/l10n/cs.js +++ b/l10n/cs.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Akce úkolu", "All labels": "Všechny štítky", "Filter tasks by label": "Filtrovat úkoly podle štítku", - "View timeline": "Zobrazit časovou osu" + "View timeline": "Zobrazit časovou osu", + "Board": "Nástěnka", + "List": "Seznam", + "Column": "Sloupec", + "Board or list": "Nástěnka nebo seznam" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/cs.json b/l10n/cs.json index 61d84e6d..8cc07f6f 100644 --- a/l10n/cs.json +++ b/l10n/cs.json @@ -304,7 +304,11 @@ "Task actions": "Akce úkolu", "All labels": "Všechny štítky", "Filter tasks by label": "Filtrovat úkoly podle štítku", - "View timeline": "Zobrazit časovou osu" + "View timeline": "Zobrazit časovou osu", + "Board": "Nástěnka", + "List": "Seznam", + "Column": "Sloupec", + "Board or list": "Nástěnka nebo seznam" }, "plurals": null } diff --git a/l10n/da.js b/l10n/da.js index 98f32104..3296ba99 100644 --- a/l10n/da.js +++ b/l10n/da.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Opgavehandlinger", "All labels": "Alle etiketter", "Filter tasks by label": "Filtrer opgaver efter etiket", - "View timeline": "Vis tidslinje" + "View timeline": "Vis tidslinje", + "Board": "Tavle", + "List": "Liste", + "Column": "Kolonne", + "Board or list": "Tavle eller liste" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/da.json b/l10n/da.json index e3eedd45..17e7e2ce 100644 --- a/l10n/da.json +++ b/l10n/da.json @@ -304,7 +304,11 @@ "Task actions": "Opgavehandlinger", "All labels": "Alle etiketter", "Filter tasks by label": "Filtrer opgaver efter etiket", - "View timeline": "Vis tidslinje" + "View timeline": "Vis tidslinje", + "Board": "Tavle", + "List": "Liste", + "Column": "Kolonne", + "Board or list": "Tavle eller liste" }, "plurals": null } diff --git a/l10n/de.js b/l10n/de.js index 1d4cb102..483c2838 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Aufgabenaktionen", "All labels": "Alle Labels", "Filter tasks by label": "Aufgaben nach Label filtern", - "View timeline": "Zeitleiste anzeigen" + "View timeline": "Zeitleiste anzeigen", + "Board": "Board", + "List": "Liste", + "Column": "Spalte", + "Board or list": "Board oder Liste" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/de.json b/l10n/de.json index 1d5dd0cb..c814879c 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -304,7 +304,11 @@ "Task actions": "Aufgabenaktionen", "All labels": "Alle Labels", "Filter tasks by label": "Aufgaben nach Label filtern", - "View timeline": "Zeitleiste anzeigen" + "View timeline": "Zeitleiste anzeigen", + "Board": "Board", + "List": "Liste", + "Column": "Spalte", + "Board or list": "Board oder Liste" }, "plurals": null } diff --git a/l10n/el.js b/l10n/el.js index 0fa3f658..e9ec65ca 100644 --- a/l10n/el.js +++ b/l10n/el.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Ενέργειες εργασίας", "All labels": "Όλες οι ετικέτες", "Filter tasks by label": "Φιλτράρισμα εργασιών κατά ετικέτα", - "View timeline": "Προβολή χρονολογίου" + "View timeline": "Προβολή χρονολογίου", + "Board": "Πίνακας", + "List": "Λίστα", + "Column": "Στήλη", + "Board or list": "Πίνακας ή λίστα" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/el.json b/l10n/el.json index d781a1d0..e1d3c2eb 100644 --- a/l10n/el.json +++ b/l10n/el.json @@ -304,7 +304,11 @@ "Task actions": "Ενέργειες εργασίας", "All labels": "Όλες οι ετικέτες", "Filter tasks by label": "Φιλτράρισμα εργασιών κατά ετικέτα", - "View timeline": "Προβολή χρονολογίου" + "View timeline": "Προβολή χρονολογίου", + "Board": "Πίνακας", + "List": "Λίστα", + "Column": "Στήλη", + "Board or list": "Πίνακας ή λίστα" }, "plurals": null } diff --git a/l10n/en.js b/l10n/en.js index 81127dac..9b92fa0b 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -279,7 +279,11 @@ OC.L10N.register( "Task actions": "Task actions", "All labels": "All labels", "Filter tasks by label": "Filter tasks by label", - "View timeline": "View timeline" + "View timeline": "View timeline", + "Board": "Board", + "List": "List", + "Column": "Column", + "Board or list": "Board or list" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/en.json b/l10n/en.json index 53992dae..8288dea4 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -278,7 +278,11 @@ "Task actions": "Task actions", "All labels": "All labels", "Filter tasks by label": "Filter tasks by label", - "View timeline": "View timeline" + "View timeline": "View timeline", + "Board": "Board", + "List": "List", + "Column": "Column", + "Board or list": "Board or list" }, "plurals": {}, "pluralForm": "nplurals=2; plural=(n != 1);" diff --git a/l10n/es.js b/l10n/es.js index ca057b1f..6275bb93 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Acciones de la tarea", "All labels": "Todas las etiquetas", "Filter tasks by label": "Filtrar tareas por etiqueta", - "View timeline": "Ver cronología" + "View timeline": "Ver cronología", + "Board": "Tablero", + "List": "Lista", + "Column": "Columna", + "Board or list": "Tablero o lista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/es.json b/l10n/es.json index abd3bb6e..d57eb0e1 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -304,7 +304,11 @@ "Task actions": "Acciones de la tarea", "All labels": "Todas las etiquetas", "Filter tasks by label": "Filtrar tareas por etiqueta", - "View timeline": "Ver cronología" + "View timeline": "Ver cronología", + "Board": "Tablero", + "List": "Lista", + "Column": "Columna", + "Board or list": "Tablero o lista" }, "plurals": null } diff --git a/l10n/et.js b/l10n/et.js index c29c2461..f9885437 100644 --- a/l10n/et.js +++ b/l10n/et.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Ülesande toimingud", "All labels": "Kõik sildid", "Filter tasks by label": "Filtreeri ülesandeid sildi järgi", - "View timeline": "Vaata ajajoont" + "View timeline": "Vaata ajajoont", + "Board": "Tahvel", + "List": "Loend", + "Column": "Veerg", + "Board or list": "Tahvel või loend" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/et.json b/l10n/et.json index dd7152ee..779bdce7 100644 --- a/l10n/et.json +++ b/l10n/et.json @@ -304,7 +304,11 @@ "Task actions": "Ülesande toimingud", "All labels": "Kõik sildid", "Filter tasks by label": "Filtreeri ülesandeid sildi järgi", - "View timeline": "Vaata ajajoont" + "View timeline": "Vaata ajajoont", + "Board": "Tahvel", + "List": "Loend", + "Column": "Veerg", + "Board or list": "Tahvel või loend" }, "plurals": null } diff --git a/l10n/fi.js b/l10n/fi.js index 895b32c0..72c07c84 100644 --- a/l10n/fi.js +++ b/l10n/fi.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Tehtävän toiminnot", "All labels": "Kaikki tunnisteet", "Filter tasks by label": "Suodata tehtäviä tunnisteen mukaan", - "View timeline": "Näytä aikajana" + "View timeline": "Näytä aikajana", + "Board": "Taulu", + "List": "Luettelo", + "Column": "Sarake", + "Board or list": "Taulu tai luettelo" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/fi.json b/l10n/fi.json index 3b8647e0..8aa6185a 100644 --- a/l10n/fi.json +++ b/l10n/fi.json @@ -304,7 +304,11 @@ "Task actions": "Tehtävän toiminnot", "All labels": "Kaikki tunnisteet", "Filter tasks by label": "Suodata tehtäviä tunnisteen mukaan", - "View timeline": "Näytä aikajana" + "View timeline": "Näytä aikajana", + "Board": "Taulu", + "List": "Luettelo", + "Column": "Sarake", + "Board or list": "Taulu tai luettelo" }, "plurals": null } diff --git a/l10n/fr.js b/l10n/fr.js index a588f713..b94d18f9 100644 --- a/l10n/fr.js +++ b/l10n/fr.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Actions de la tâche", "All labels": "Toutes les étiquettes", "Filter tasks by label": "Filtrer les tâches par étiquette", - "View timeline": "Voir la chronologie" + "View timeline": "Voir la chronologie", + "Board": "Tableau", + "List": "Liste", + "Column": "Colonne", + "Board or list": "Tableau ou liste" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/fr.json b/l10n/fr.json index 2528849e..4c162a4e 100644 --- a/l10n/fr.json +++ b/l10n/fr.json @@ -304,7 +304,11 @@ "Task actions": "Actions de la tâche", "All labels": "Toutes les étiquettes", "Filter tasks by label": "Filtrer les tâches par étiquette", - "View timeline": "Voir la chronologie" + "View timeline": "Voir la chronologie", + "Board": "Tableau", + "List": "Liste", + "Column": "Colonne", + "Board or list": "Tableau ou liste" }, "plurals": null } diff --git a/l10n/ga.js b/l10n/ga.js index 7fb17496..e7f72cf2 100644 --- a/l10n/ga.js +++ b/l10n/ga.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Gníomhartha taisc", "All labels": "Gach lipéad", "Filter tasks by label": "Scag tascanna de réir lipéid", - "View timeline": "Féach ar an amlíne" + "View timeline": "Féach ar an amlíne", + "Board": "Clár", + "List": "Liosta", + "Column": "Colún", + "Board or list": "Clár nó liosta" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ga.json b/l10n/ga.json index 799daf12..1c9f5e5c 100644 --- a/l10n/ga.json +++ b/l10n/ga.json @@ -304,7 +304,11 @@ "Task actions": "Gníomhartha taisc", "All labels": "Gach lipéad", "Filter tasks by label": "Scag tascanna de réir lipéid", - "View timeline": "Féach ar an amlíne" + "View timeline": "Féach ar an amlíne", + "Board": "Clár", + "List": "Liosta", + "Column": "Colún", + "Board or list": "Clár nó liosta" }, "plurals": null } diff --git a/l10n/hr.js b/l10n/hr.js index 8e243883..329b05ef 100644 --- a/l10n/hr.js +++ b/l10n/hr.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Radnje zadatka", "All labels": "Sve oznake", "Filter tasks by label": "Filtriraj zadatke po oznaci", - "View timeline": "Prikaži vremensku crtu" + "View timeline": "Prikaži vremensku crtu", + "Board": "Ploča", + "List": "Popis", + "Column": "Stupac", + "Board or list": "Ploča ili popis" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/hr.json b/l10n/hr.json index 532ae802..22c02bca 100644 --- a/l10n/hr.json +++ b/l10n/hr.json @@ -304,7 +304,11 @@ "Task actions": "Radnje zadatka", "All labels": "Sve oznake", "Filter tasks by label": "Filtriraj zadatke po oznaci", - "View timeline": "Prikaži vremensku crtu" + "View timeline": "Prikaži vremensku crtu", + "Board": "Ploča", + "List": "Popis", + "Column": "Stupac", + "Board or list": "Ploča ili popis" }, "plurals": null } diff --git a/l10n/hu.js b/l10n/hu.js index 2bf33c87..26a25ff7 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Feladatműveletek", "All labels": "Minden címke", "Filter tasks by label": "Feladatok szűrése címke szerint", - "View timeline": "Idővonal megtekintése" + "View timeline": "Idővonal megtekintése", + "Board": "Tábla", + "List": "Lista", + "Column": "Oszlop", + "Board or list": "Tábla vagy lista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/hu.json b/l10n/hu.json index 6b7f7a88..20f2a2c2 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -304,7 +304,11 @@ "Task actions": "Feladatműveletek", "All labels": "Minden címke", "Filter tasks by label": "Feladatok szűrése címke szerint", - "View timeline": "Idővonal megtekintése" + "View timeline": "Idővonal megtekintése", + "Board": "Tábla", + "List": "Lista", + "Column": "Oszlop", + "Board or list": "Tábla vagy lista" }, "plurals": null } diff --git a/l10n/is.js b/l10n/is.js index a54fd882..e7ebca86 100644 --- a/l10n/is.js +++ b/l10n/is.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Aðgerðir verkefnis", "All labels": "Öll merki", "Filter tasks by label": "Sía verkefni eftir merki", - "View timeline": "Skoða tímalínu" + "View timeline": "Skoða tímalínu", + "Board": "Tafla", + "List": "Listi", + "Column": "Dálkur", + "Board or list": "Tafla eða listi" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/is.json b/l10n/is.json index 681da948..110777e8 100644 --- a/l10n/is.json +++ b/l10n/is.json @@ -304,7 +304,11 @@ "Task actions": "Aðgerðir verkefnis", "All labels": "Öll merki", "Filter tasks by label": "Sía verkefni eftir merki", - "View timeline": "Skoða tímalínu" + "View timeline": "Skoða tímalínu", + "Board": "Tafla", + "List": "Listi", + "Column": "Dálkur", + "Board or list": "Tafla eða listi" }, "plurals": {} } diff --git a/l10n/it.js b/l10n/it.js index e2c9f22c..a89a0e39 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Azioni dell'attività", "All labels": "Tutte le etichette", "Filter tasks by label": "Filtra le attività per etichetta", - "View timeline": "Visualizza cronologia" + "View timeline": "Visualizza cronologia", + "Board": "Bacheca", + "List": "Elenco", + "Column": "Colonna", + "Board or list": "Bacheca o elenco" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/it.json b/l10n/it.json index ee9d1a60..d901ebda 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -304,7 +304,11 @@ "Task actions": "Azioni dell'attività", "All labels": "Tutte le etichette", "Filter tasks by label": "Filtra le attività per etichetta", - "View timeline": "Visualizza cronologia" + "View timeline": "Visualizza cronologia", + "Board": "Bacheca", + "List": "Elenco", + "Column": "Colonna", + "Board or list": "Bacheca o elenco" }, "plurals": null } diff --git a/l10n/lb.js b/l10n/lb.js index 0338d22d..7e624e59 100644 --- a/l10n/lb.js +++ b/l10n/lb.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Aufgabenaktiounen", "All labels": "All Etiketten", "Filter tasks by label": "Aufgabe no Etikett filteren", - "View timeline": "Zäitlinn weisen" + "View timeline": "Zäitlinn weisen", + "Board": "Board", + "List": "Lëscht", + "Column": "Kolonn", + "Board or list": "Board oder Lëscht" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lb.json b/l10n/lb.json index 55e38045..f4308da8 100644 --- a/l10n/lb.json +++ b/l10n/lb.json @@ -304,7 +304,11 @@ "Task actions": "Aufgabenaktiounen", "All labels": "All Etiketten", "Filter tasks by label": "Aufgabe no Etikett filteren", - "View timeline": "Zäitlinn weisen" + "View timeline": "Zäitlinn weisen", + "Board": "Board", + "List": "Lëscht", + "Column": "Kolonn", + "Board or list": "Board oder Lëscht" }, "plurals": null } diff --git a/l10n/lt.js b/l10n/lt.js index f06f0521..5f9e958b 100644 --- a/l10n/lt.js +++ b/l10n/lt.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Užduoties veiksmai", "All labels": "Visos žymės", "Filter tasks by label": "Filtruoti užduotis pagal žymę", - "View timeline": "Rodyti laiko juostą" + "View timeline": "Rodyti laiko juostą", + "Board": "Lenta", + "List": "Sąrašas", + "Column": "Stulpelis", + "Board or list": "Lenta arba sąrašas" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lt.json b/l10n/lt.json index e4bab6eb..cf0b2d2c 100644 --- a/l10n/lt.json +++ b/l10n/lt.json @@ -304,7 +304,11 @@ "Task actions": "Užduoties veiksmai", "All labels": "Visos žymės", "Filter tasks by label": "Filtruoti užduotis pagal žymę", - "View timeline": "Rodyti laiko juostą" + "View timeline": "Rodyti laiko juostą", + "Board": "Lenta", + "List": "Sąrašas", + "Column": "Stulpelis", + "Board or list": "Lenta arba sąrašas" }, "plurals": null } diff --git a/l10n/lv.js b/l10n/lv.js index b3d425e7..5e961d44 100644 --- a/l10n/lv.js +++ b/l10n/lv.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Uzdevuma darbības", "All labels": "Visas birkas", "Filter tasks by label": "Filtrēt uzdevumus pēc birkas", - "View timeline": "Skatīt laika skalu" + "View timeline": "Skatīt laika skalu", + "Board": "Dēlis", + "List": "Saraksts", + "Column": "Kolonna", + "Board or list": "Dēlis vai saraksts" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lv.json b/l10n/lv.json index 6201f54a..daa30d40 100644 --- a/l10n/lv.json +++ b/l10n/lv.json @@ -304,7 +304,11 @@ "Task actions": "Uzdevuma darbības", "All labels": "Visas birkas", "Filter tasks by label": "Filtrēt uzdevumus pēc birkas", - "View timeline": "Skatīt laika skalu" + "View timeline": "Skatīt laika skalu", + "Board": "Dēlis", + "List": "Saraksts", + "Column": "Kolonna", + "Board or list": "Dēlis vai saraksts" }, "plurals": null } diff --git a/l10n/mk.js b/l10n/mk.js index c5be859e..2e9e5217 100644 --- a/l10n/mk.js +++ b/l10n/mk.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Дејства на задачата", "All labels": "Сите ознаки", "Filter tasks by label": "Филтрирај задачи по ознака", - "View timeline": "Прикажи временска линија" + "View timeline": "Прикажи временска линија", + "Board": "Табла", + "List": "Листа", + "Column": "Колона", + "Board or list": "Табла или листа" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/mk.json b/l10n/mk.json index b3387360..9d0f71a8 100644 --- a/l10n/mk.json +++ b/l10n/mk.json @@ -304,7 +304,11 @@ "Task actions": "Дејства на задачата", "All labels": "Сите ознаки", "Filter tasks by label": "Филтрирај задачи по ознака", - "View timeline": "Прикажи временска линија" + "View timeline": "Прикажи временска линија", + "Board": "Табла", + "List": "Листа", + "Column": "Колона", + "Board or list": "Табла или листа" }, "plurals": null } diff --git a/l10n/mt.js b/l10n/mt.js index ec18b3c9..ddb2f30a 100644 --- a/l10n/mt.js +++ b/l10n/mt.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Azzjonijiet tal-kompitu", "All labels": "It-tikketti kollha", "Filter tasks by label": "Iffiltra l-kompiti skont it-tikketta", - "View timeline": "Ara l-kronoloġija" + "View timeline": "Ara l-kronoloġija", + "Board": "Bord", + "List": "Lista", + "Column": "Kolonna", + "Board or list": "Bord jew lista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/mt.json b/l10n/mt.json index 88c39e29..13a001bd 100644 --- a/l10n/mt.json +++ b/l10n/mt.json @@ -304,7 +304,11 @@ "Task actions": "Azzjonijiet tal-kompitu", "All labels": "It-tikketti kollha", "Filter tasks by label": "Iffiltra l-kompiti skont it-tikketta", - "View timeline": "Ara l-kronoloġija" + "View timeline": "Ara l-kronoloġija", + "Board": "Bord", + "List": "Lista", + "Column": "Kolonna", + "Board or list": "Bord jew lista" }, "plurals": {} } diff --git a/l10n/nb.js b/l10n/nb.js index bfd393c5..b377e419 100644 --- a/l10n/nb.js +++ b/l10n/nb.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Oppgavehandlinger", "All labels": "Alle etiketter", "Filter tasks by label": "Filtrer oppgaver etter etikett", - "View timeline": "Vis tidslinje" + "View timeline": "Vis tidslinje", + "Board": "Tavle", + "List": "Liste", + "Column": "Kolonne", + "Board or list": "Tavle eller liste" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nb.json b/l10n/nb.json index f8545010..dda9f668 100644 --- a/l10n/nb.json +++ b/l10n/nb.json @@ -304,7 +304,11 @@ "Task actions": "Oppgavehandlinger", "All labels": "Alle etiketter", "Filter tasks by label": "Filtrer oppgaver etter etikett", - "View timeline": "Vis tidslinje" + "View timeline": "Vis tidslinje", + "Board": "Tavle", + "List": "Liste", + "Column": "Kolonne", + "Board or list": "Tavle eller liste" }, "plurals": {} } diff --git a/l10n/nl.js b/l10n/nl.js index f1484427..5a1c1ee2 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -326,7 +326,11 @@ OC.L10N.register( "Task actions": "Taakacties", "All labels": "Alle labels", "Filter tasks by label": "Taken filteren op label", - "View timeline": "Tijdlijn bekijken" + "View timeline": "Tijdlijn bekijken", + "Board": "Bord", + "List": "Lijst", + "Column": "Kolom", + "Board or list": "Bord of lijst" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nl.json b/l10n/nl.json index b762e089..80ad24aa 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -325,7 +325,11 @@ "Task actions": "Taakacties", "All labels": "Alle labels", "Filter tasks by label": "Taken filteren op label", - "View timeline": "Tijdlijn bekijken" + "View timeline": "Tijdlijn bekijken", + "Board": "Bord", + "List": "Lijst", + "Column": "Kolom", + "Board or list": "Bord of lijst" }, "plurals": {}, "pluralForm": "nplurals=2; plural=(n != 1);" diff --git a/l10n/pl.js b/l10n/pl.js index e419176d..d4be0f2f 100644 --- a/l10n/pl.js +++ b/l10n/pl.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Akcje zadania", "All labels": "Wszystkie etykiety", "Filter tasks by label": "Filtruj zadania według etykiety", - "View timeline": "Pokaż oś czasu" + "View timeline": "Pokaż oś czasu", + "Board": "Tablica", + "List": "Lista", + "Column": "Kolumna", + "Board or list": "Tablica lub lista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/pl.json b/l10n/pl.json index 2fa0a7ff..cd096eb5 100644 --- a/l10n/pl.json +++ b/l10n/pl.json @@ -304,7 +304,11 @@ "Task actions": "Akcje zadania", "All labels": "Wszystkie etykiety", "Filter tasks by label": "Filtruj zadania według etykiety", - "View timeline": "Pokaż oś czasu" + "View timeline": "Pokaż oś czasu", + "Board": "Tablica", + "List": "Lista", + "Column": "Kolumna", + "Board or list": "Tablica lub lista" }, "plurals": {} } diff --git a/l10n/pt.js b/l10n/pt.js index 0c44e01b..8615ca86 100644 --- a/l10n/pt.js +++ b/l10n/pt.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Ações da tarefa", "All labels": "Todas as etiquetas", "Filter tasks by label": "Filtrar tarefas por etiqueta", - "View timeline": "Ver cronologia" + "View timeline": "Ver cronologia", + "Board": "Quadro", + "List": "Lista", + "Column": "Coluna", + "Board or list": "Quadro ou lista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/pt.json b/l10n/pt.json index 7dbc08a6..f9a4468e 100644 --- a/l10n/pt.json +++ b/l10n/pt.json @@ -304,7 +304,11 @@ "Task actions": "Ações da tarefa", "All labels": "Todas as etiquetas", "Filter tasks by label": "Filtrar tarefas por etiqueta", - "View timeline": "Ver cronologia" + "View timeline": "Ver cronologia", + "Board": "Quadro", + "List": "Lista", + "Column": "Coluna", + "Board or list": "Quadro ou lista" }, "plurals": {} } diff --git a/l10n/rm.js b/l10n/rm.js index 27170937..db64974c 100644 --- a/l10n/rm.js +++ b/l10n/rm.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Acziuns da l'incumbensa", "All labels": "Tut las etichettas", "Filter tasks by label": "Filtrar las incumbensas tenor etichetta", - "View timeline": "Mussar la cronologia" + "View timeline": "Mussar la cronologia", + "Board": "Tavla", + "List": "Glista", + "Column": "Colonna", + "Board or list": "Tavla u glista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/rm.json b/l10n/rm.json index f1d90d44..6a57f259 100644 --- a/l10n/rm.json +++ b/l10n/rm.json @@ -304,7 +304,11 @@ "Task actions": "Acziuns da l'incumbensa", "All labels": "Tut las etichettas", "Filter tasks by label": "Filtrar las incumbensas tenor etichetta", - "View timeline": "Mussar la cronologia" + "View timeline": "Mussar la cronologia", + "Board": "Tavla", + "List": "Glista", + "Column": "Colonna", + "Board or list": "Tavla u glista" }, "plurals": null } diff --git a/l10n/ro.js b/l10n/ro.js index 07e78aa4..1dca3c01 100644 --- a/l10n/ro.js +++ b/l10n/ro.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Acțiuni sarcină", "All labels": "Toate etichetele", "Filter tasks by label": "Filtrează sarcinile după etichetă", - "View timeline": "Vezi cronologia" + "View timeline": "Vezi cronologia", + "Board": "Tablă", + "List": "Listă", + "Column": "Coloană", + "Board or list": "Tablă sau listă" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ro.json b/l10n/ro.json index c5be6878..a29472b2 100644 --- a/l10n/ro.json +++ b/l10n/ro.json @@ -304,7 +304,11 @@ "Task actions": "Acțiuni sarcină", "All labels": "Toate etichetele", "Filter tasks by label": "Filtrează sarcinile după etichetă", - "View timeline": "Vezi cronologia" + "View timeline": "Vezi cronologia", + "Board": "Tablă", + "List": "Listă", + "Column": "Coloană", + "Board or list": "Tablă sau listă" }, "plurals": {} } diff --git a/l10n/ru.js b/l10n/ru.js index 279ca7f2..928809ef 100644 --- a/l10n/ru.js +++ b/l10n/ru.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Действия с задачей", "All labels": "Все метки", "Filter tasks by label": "Фильтровать задачи по метке", - "View timeline": "Показать временную шкалу" + "View timeline": "Показать временную шкалу", + "Board": "Доска", + "List": "Список", + "Column": "Столбец", + "Board or list": "Доска или список" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ru.json b/l10n/ru.json index fdb91a43..f115af33 100644 --- a/l10n/ru.json +++ b/l10n/ru.json @@ -304,7 +304,11 @@ "Task actions": "Действия с задачей", "All labels": "Все метки", "Filter tasks by label": "Фильтровать задачи по метке", - "View timeline": "Показать временную шкалу" + "View timeline": "Показать временную шкалу", + "Board": "Доска", + "List": "Список", + "Column": "Столбец", + "Board or list": "Доска или список" }, "plurals": {} } diff --git a/l10n/sk.js b/l10n/sk.js index ed173700..e93b537a 100644 --- a/l10n/sk.js +++ b/l10n/sk.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Akcie úlohy", "All labels": "Všetky štítky", "Filter tasks by label": "Filtrovať úlohy podľa štítka", - "View timeline": "Zobraziť časovú os" + "View timeline": "Zobraziť časovú os", + "Board": "Nástenka", + "List": "Zoznam", + "Column": "Stĺpec", + "Board or list": "Nástenka alebo zoznam" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sk.json b/l10n/sk.json index 44dd8842..04b41fe7 100644 --- a/l10n/sk.json +++ b/l10n/sk.json @@ -304,7 +304,11 @@ "Task actions": "Akcie úlohy", "All labels": "Všetky štítky", "Filter tasks by label": "Filtrovať úlohy podľa štítka", - "View timeline": "Zobraziť časovú os" + "View timeline": "Zobraziť časovú os", + "Board": "Nástenka", + "List": "Zoznam", + "Column": "Stĺpec", + "Board or list": "Nástenka alebo zoznam" }, "plurals": null } diff --git a/l10n/sl.js b/l10n/sl.js index 682b2601..a063ed52 100644 --- a/l10n/sl.js +++ b/l10n/sl.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Dejanja opravila", "All labels": "Vse oznake", "Filter tasks by label": "Filtriraj opravila po oznaki", - "View timeline": "Prikaži časovnico" + "View timeline": "Prikaži časovnico", + "Board": "Tabla", + "List": "Seznam", + "Column": "Stolpec", + "Board or list": "Tabla ali seznam" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sl.json b/l10n/sl.json index 03179dcd..5b9d8ebf 100644 --- a/l10n/sl.json +++ b/l10n/sl.json @@ -304,7 +304,11 @@ "Task actions": "Dejanja opravila", "All labels": "Vse oznake", "Filter tasks by label": "Filtriraj opravila po oznaki", - "View timeline": "Prikaži časovnico" + "View timeline": "Prikaži časovnico", + "Board": "Tabla", + "List": "Seznam", + "Column": "Stolpec", + "Board or list": "Tabla ali seznam" }, "plurals": null } diff --git a/l10n/sq.js b/l10n/sq.js index 67b2d689..ea7008fd 100644 --- a/l10n/sq.js +++ b/l10n/sq.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Veprimet e detyrës", "All labels": "Të gjitha etiketat", "Filter tasks by label": "Filtro detyrat sipas etiketës", - "View timeline": "Shiko kronologjinë" + "View timeline": "Shiko kronologjinë", + "Board": "Tabela", + "List": "Lista", + "Column": "Kolona", + "Board or list": "Tabela ose lista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sq.json b/l10n/sq.json index e523359e..0d6736e6 100644 --- a/l10n/sq.json +++ b/l10n/sq.json @@ -304,7 +304,11 @@ "Task actions": "Veprimet e detyrës", "All labels": "Të gjitha etiketat", "Filter tasks by label": "Filtro detyrat sipas etiketës", - "View timeline": "Shiko kronologjinë" + "View timeline": "Shiko kronologjinë", + "Board": "Tabela", + "List": "Lista", + "Column": "Kolona", + "Board or list": "Tabela ose lista" }, "plurals": {} } diff --git a/l10n/sr.js b/l10n/sr.js index 1e1f528d..88e970a4 100644 --- a/l10n/sr.js +++ b/l10n/sr.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Радње задатка", "All labels": "Све ознаке", "Filter tasks by label": "Филтрирај задатке по ознаци", - "View timeline": "Прикажи временску линију" + "View timeline": "Прикажи временску линију", + "Board": "Табла", + "List": "Листа", + "Column": "Колона", + "Board or list": "Табла или листа" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sr.json b/l10n/sr.json index c94fb86f..0ea5f66f 100644 --- a/l10n/sr.json +++ b/l10n/sr.json @@ -304,7 +304,11 @@ "Task actions": "Радње задатка", "All labels": "Све ознаке", "Filter tasks by label": "Филтрирај задатке по ознаци", - "View timeline": "Прикажи временску линију" + "View timeline": "Прикажи временску линију", + "Board": "Табла", + "List": "Листа", + "Column": "Колона", + "Board or list": "Табла или листа" }, "plurals": null } diff --git a/l10n/sv.js b/l10n/sv.js index 967b6f70..31162d9d 100644 --- a/l10n/sv.js +++ b/l10n/sv.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Uppgiftsåtgärder", "All labels": "Alla etiketter", "Filter tasks by label": "Filtrera uppgifter efter etikett", - "View timeline": "Visa tidslinje" + "View timeline": "Visa tidslinje", + "Board": "Tavla", + "List": "Lista", + "Column": "Kolumn", + "Board or list": "Tavla eller lista" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sv.json b/l10n/sv.json index ffa2b0eb..05a3ce40 100644 --- a/l10n/sv.json +++ b/l10n/sv.json @@ -304,7 +304,11 @@ "Task actions": "Uppgiftsåtgärder", "All labels": "Alla etiketter", "Filter tasks by label": "Filtrera uppgifter efter etikett", - "View timeline": "Visa tidslinje" + "View timeline": "Visa tidslinje", + "Board": "Tavla", + "List": "Lista", + "Column": "Kolumn", + "Board or list": "Tavla eller lista" }, "plurals": null } diff --git a/l10n/tr.js b/l10n/tr.js index d50f0232..5571d398 100644 --- a/l10n/tr.js +++ b/l10n/tr.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Görev işlemleri", "All labels": "Tüm etiketler", "Filter tasks by label": "Görevleri etikete göre filtrele", - "View timeline": "Zaman çizelgesini görüntüle" + "View timeline": "Zaman çizelgesini görüntüle", + "Board": "Pano", + "List": "Liste", + "Column": "Sütun", + "Board or list": "Pano veya liste" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/tr.json b/l10n/tr.json index 5d6a6591..a85be3e8 100644 --- a/l10n/tr.json +++ b/l10n/tr.json @@ -304,7 +304,11 @@ "Task actions": "Görev işlemleri", "All labels": "Tüm etiketler", "Filter tasks by label": "Görevleri etikete göre filtrele", - "View timeline": "Zaman çizelgesini görüntüle" + "View timeline": "Zaman çizelgesini görüntüle", + "Board": "Pano", + "List": "Liste", + "Column": "Sütun", + "Board or list": "Pano veya liste" }, "plurals": {} } diff --git a/l10n/uk.js b/l10n/uk.js index ae8959cf..16081062 100644 --- a/l10n/uk.js +++ b/l10n/uk.js @@ -305,7 +305,11 @@ OC.L10N.register( "Task actions": "Дії із завданням", "All labels": "Усі мітки", "Filter tasks by label": "Фільтрувати завдання за міткою", - "View timeline": "Переглянути часову шкалу" + "View timeline": "Переглянути часову шкалу", + "Board": "Дошка", + "List": "Список", + "Column": "Стовпець", + "Board or list": "Дошка або список" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/uk.json b/l10n/uk.json index 5f259dcc..86cd7219 100644 --- a/l10n/uk.json +++ b/l10n/uk.json @@ -304,7 +304,11 @@ "Task actions": "Дії із завданням", "All labels": "Усі мітки", "Filter tasks by label": "Фільтрувати завдання за міткою", - "View timeline": "Переглянути часову шкалу" + "View timeline": "Переглянути часову шкалу", + "Board": "Дошка", + "List": "Список", + "Column": "Стовпець", + "Board or list": "Дошка або список" }, "plurals": {} } diff --git a/openspec/changes/archive/2026-09-28-boards-list-toggle/design.md b/openspec/changes/archive/2026-09-28-boards-list-toggle/design.md new file mode 100644 index 00000000..75a031de --- /dev/null +++ b/openspec/changes/archive/2026-09-28-boards-list-toggle/design.md @@ -0,0 +1,37 @@ +# Design: a list view of the board's own cards + +## Context + +Read at development `4806ed3`: + +- `src/views/ProjectBoard.vue` renders lanes from the project's column objects and cards from + `tasksByColumn`, a grouping of `visibleTasks` (the tasks after the label filter) by `column` + (`src/utils/columnHelpers.js` `groupTasksByColumn`). A task with no column is in the backlog and + not on the board. +- The page already keeps no view state in the URL; the backlog keeps its sort and filters in the + query string (`src/views/ProjectBacklog.vue` `setQuery`). + +## Decisions + +### Decision 1: the list is the board flattened + +`boardListRows(tasks, columns)` walks `groupTasksByColumn` in lane order and returns one row per +card with its column. The list therefore shows exactly the cards the board shows, in the order the +board shows them, and follows the label filter because it reads `visibleTasks`. Alternative: a +separate query. Rejected: two sources can disagree. + +### Decision 2: the view is in the query string + +`?view=list` opens the list; no parameter is the board. A switch of two buttons with +`aria-pressed` sits in the page header. Alternative: a user preference. Rejected: a link should +open what the sender saw, and the backlog already keeps its view state in the URL. + +### Decision 3: the list is read-only in this change + +A row opens the task. Moving and ranking stay on the board, where they already have a keyboard +path (the card menu). Non-goal: inline editing, column sorting of the list. + +## Risks + +- [A long board makes a long list] -> the list has no paging, like the board; the board fetches + every task already. diff --git a/openspec/changes/archive/2026-09-28-boards-list-toggle/proposal.md b/openspec/changes/archive/2026-09-28-boards-list-toggle/proposal.md new file mode 100644 index 00000000..f3124771 --- /dev/null +++ b/openspec/changes/archive/2026-09-28-boards-list-toggle/proposal.md @@ -0,0 +1,40 @@ +--- +kind: code +--- + +# Switch between the board and a list of the same tasks + +## Why + +The only planninq-owned parity row still marked `building` is `brd-list-toggle`, "Switch between +the board and a list of the same tasks", and it named no change. Its built evidence pointed at the +backlog placeholder, which `backlog-list` (archived 2026-09-28) turned into a list of the tasks in +no column: that is the backlog, not the board's own cards. So the row is still not built, and a +`building` row with no change is a claim nobody backs. + +Decision: build. The row is in the boards area (a core area of the product) and four competitors +rate it yes. + +Matrix: `openspec/parity/capabilities.json` in ConductionNL/planninq. Row: `brd-list-toggle` +(origin own-code). +- OpenProject yes: "Work packages list, plus Gantt, board and calendar views" over the same work + packages (corpus openproject/round4/menu-tree.md). +- Plane yes: "spreadsheet, board, calendar, gantt and list layouts" + (apps/web/core/components/issues/issue-layouts/filters/header/layout-selection.tsx:24-36 at v1.4.2). +- Kanboard yes: "Overview · Board · List" with the same search (app/Template/project_header/views.php:12 + and :18 at v1.2.54). +- Jira yes: a board is based on a saved filter, and the same filter opens as a list in the issue + navigator (https://confluence.atlassian.com/jirasoftwareserver/creating-a-board-938845220.html). +- Nextcloud Deck partial: Kanban and Gantt view modes only (src/components/Controls.vue). + +## What changes + +- The board page gets a Board and List switch. The list shows the same cards the board shows, + with the same label filter, one row per card with its column, priority and due date, in lane + order and then card order. +- The choice lives in the query string, so a link opens the same view. + +## Impact + +- `src/views/ProjectBoard.vue`, `src/utils/columnHelpers.js`, catalogues. +- No schema or server change. diff --git a/openspec/changes/archive/2026-09-28-boards-list-toggle/specs/board-list/spec.md b/openspec/changes/archive/2026-09-28-boards-list-toggle/specs/board-list/spec.md new file mode 100644 index 00000000..25636df8 --- /dev/null +++ b/openspec/changes/archive/2026-09-28-boards-list-toggle/specs/board-list/spec.md @@ -0,0 +1,23 @@ +# board-list delta for boards-list-toggle + +## ADDED Requirements + +### Requirement: A member can see the board as a list + +The system MUST let a project member switch the board page between the board and a list of the +same cards, MUST show in the list every card the board shows (after the label filter) with its +column, priority and due date, in lane order and then card order, and MUST keep the choice in the +page address. Tier: V1. + +#### Scenario: Switch to the list + +- **GIVEN** project "Vergunningen" has two cards in To do and one in Done +- **WHEN** a member opens its board and chooses "List" +- **THEN** a list shows the three cards, the two To do cards first, each with its column +- **AND** after a reload the page still shows the list + +#### Scenario: The list follows the label filter + +- **GIVEN** one of the three cards carries the label "Urgent client" +- **WHEN** the member picks that label in the filter and chooses "List" +- **THEN** the list shows that one card diff --git a/openspec/changes/archive/2026-09-28-boards-list-toggle/tasks.md b/openspec/changes/archive/2026-09-28-boards-list-toggle/tasks.md new file mode 100644 index 00000000..19fa930d --- /dev/null +++ b/openspec/changes/archive/2026-09-28-boards-list-toggle/tasks.md @@ -0,0 +1,11 @@ +# Tasks: boards-list-toggle + +## 1. List view + +- [x] 1.1 `boardListRows(tasks, columns)` in `src/utils/columnHelpers.js`: one row per card on the board, in lane order then card order, with its column. Verify: vitest spec in `tests/vitest/boardColumns.spec.js`. Red on 'boardListRows is not a function', then green. +- [x] 1.2 Board and List switch in `src/views/ProjectBoard.vue`, the view kept in `?view=list`, and a list table (title, column, priority, due date) whose rows open the task. Verify: Playwright e2e switches to the list, finds the board's cards, reloads and stays on the list. Spec file `tests/e2e/board-list.spec.ts`. + +## 2. Copy and verification + +- [x] 2.1 New strings in every catalogue the repo ships. Verify: `npm run check:l10n` exits 0. Four strings in all 37 catalogues. +- [x] 2.2 `openspec validate boards-list-toggle --type change --strict` passes. diff --git a/openspec/parity/capabilities.json b/openspec/parity/capabilities.json index a5e780c2..17008c0b 100644 --- a/openspec/parity/capabilities.json +++ b/openspec/parity/capabilities.json @@ -1706,7 +1706,7 @@ "area": "boards", "name": "Switch between the board and a list of the same tasks.", "origin": "own-code", - "planninq": "no", + "planninq": "yes", "nextcloud-deck": "partial", "openproject": "yes", "plane": "yes", @@ -1724,16 +1724,18 @@ "plane": "corpus: plane/round4/menu-tree.md 'spreadsheet, board, calendar, gantt and list layouts' ; source read at v1.4.2: apps/web/core/components/issues/issue-layouts/filters/header/layout-selection.tsx:24-36 LayoutSelection renders ISSUE_LAYOUTS; packages/constants/src/issue/layout.ts:9 'list' | 'kanban' | 'calendar' | 'spreadsheet' | 'gantt'; layout is a per-user display filter, the same items are served by apps/api/plane/app/views/issue/base.py:628 viewset" }, "built": { - "state": "building", - "evidence": "src/views/ProjectBacklog.vue lists only the tasks in no board column (backlog); no view lists the board's own cards as a list", - "reachedOn": "ProjectBacklog page /projects/:id/backlog (backlog only)", - "owner": "ConductionNL/planninq" + "state": "built", + "evidence": "src/views/ProjectBoard.vue:57 List button in the Board or list switch sets ?view=list -> :123 table of listRows (src/utils/columnHelpers.js boardListRows(): the board's cards after the label filter, lane then card order, with column, priority and due date)", + "reachedOn": "ProjectBoard page, Board or list switch in the header", + "owner": "ConductionNL/planninq", + "change": "openspec/changes/archive/2026-09-28-boards-list-toggle", + "readOn": "2026-09-28" }, "provider": "planninq", "providerHow": "read-from-code", "feature": "kanban-board", "featureConfidence": "high", - "note": "the Backlog button on the board (src/views/ProjectBoard.vue:43-48) navigates to a page that is explicitly a stub; there is no working list rendering of the board's tasks anywhere. Read again on 2026-09-28: the backlog page is now a working list (2026-09-28-backlog-list) of the tasks in no column, not the same tasks as the board, so this row stays building until a board and list toggle exists." + "note": "the Backlog button on the board (src/views/ProjectBoard.vue:43-48) navigates to a page that is explicitly a stub; there is no working list rendering of the board's tasks anywhere. Read again on 2026-09-28: the backlog page is now a working list (2026-09-28-backlog-list) of the tasks in no column, not the same tasks as the board, so this row stays building until a board and list toggle exists. Built on 2026-09-28 by openspec/changes/archive/2026-09-28-boards-list-toggle." }, { "id": "brd-card-badges", diff --git a/openspec/parity/gap-decisions.json b/openspec/parity/gap-decisions.json index a57f3daa..9c3e0e62 100644 --- a/openspec/parity/gap-decisions.json +++ b/openspec/parity/gap-decisions.json @@ -1574,5 +1574,13 @@ "reason": "Single competitor or none, no demand that meets the build rule, outside the core areas. One competitor rates it yes (TimeEdit), the only demand row is a competitor changelog, which is evidence of a competitor shipping it, not demand, and it sits outside the core areas (projects, boards) in platform. The row note records that it does not apply: every planninq date is a calendar date. The 27 Sep pass deferred it; defer is retired on 28 Sep, so this is a recorded no that can be reversed when demand arrives.", "change": null, "decidedOn": "2026-09-28" + }, + { + "row": "brd-list-toggle", + "matrix": "planninq", + "decision": "build", + "reason": "The only planninq-owned row marked building, and it named no change. The backlog page it pointed at is now a list of the tasks in no column (2026-09-28-backlog-list), not the board's own cards, so the capability was still missing. It sits in the boards core area and four competitors rate it yes (OpenProject, Plane, Kanboard, Jira).", + "change": "boards-list-toggle", + "decidedOn": "2026-09-28" } ] diff --git a/openspec/specs/board-list/spec.md b/openspec/specs/board-list/spec.md new file mode 100644 index 00000000..a45f623d --- /dev/null +++ b/openspec/specs/board-list/spec.md @@ -0,0 +1,26 @@ +# board-list Specification + +## Purpose +The board page switches between the board and a list of the same cards. Built by the archived change 2026-09-28-boards-list-toggle. + +## Requirements + +### Requirement: A member can see the board as a list + +The system MUST let a project member switch the board page between the board and a list of the +same cards, MUST show in the list every card the board shows (after the label filter) with its +column, priority and due date, in lane order and then card order, and MUST keep the choice in the +page address. Tier: V1. + +#### Scenario: Switch to the list + +- **GIVEN** project "Vergunningen" has two cards in To do and one in Done +- **WHEN** a member opens its board and chooses "List" +- **THEN** a list shows the three cards, the two To do cards first, each with its column +- **AND** after a reload the page still shows the list + +#### Scenario: The list follows the label filter + +- **GIVEN** one of the three cards carries the label "Urgent client" +- **WHEN** the member picks that label in the filter and chooses "List" +- **THEN** the list shows that one card diff --git a/src/utils/columnHelpers.js b/src/utils/columnHelpers.js index 6963b1dc..fccedf18 100644 --- a/src/utils/columnHelpers.js +++ b/src/utils/columnHelpers.js @@ -254,3 +254,20 @@ export function columnPayload(draft = {}) { } return payload } + +/** + * The board as a list: one row per card on the board, in lane order and then + * card order, each with its column. A column-less task is in the backlog and + * not listed, exactly as it is not on the board. + * + * @param {Array} tasks The tasks the board shows (after its filters). + * @param {Array} columns The project's columns. + * @return {Array<{task: object, column: object}>} + * + * @spec openspec/changes/boards-list-toggle/tasks.md#task-1.1 + */ +export function boardListRows(tasks = [], columns = []) { + const lanes = sortColumns(columns) + const grouped = groupTasksByColumn(tasks, lanes) + return lanes.flatMap((column) => (grouped[idOf(column)] || []).map((task) => ({ task, column }))) +} diff --git a/src/views/ProjectBoard.vue b/src/views/ProjectBoard.vue index fe5dd1c7..a8c2258c 100644 --- a/src/views/ProjectBoard.vue +++ b/src/views/ProjectBoard.vue @@ -40,6 +40,25 @@
+
+ + {{ t('planninq', 'Board') }} + + + {{ t('planninq', 'List') }} + +
+ + + + + + + + + + + + + + + + + + +
+ {{ t('planninq', 'Title') }} + + {{ t('planninq', 'Column') }} + + {{ t('planninq', 'Priority') }} + + {{ t('planninq', 'Due date') }} +
+ + {{ row.task.title }} + + {{ row.column.title }}{{ priorityLabel(row.task.priority) }}{{ row.task.dueDate || '' }}
+ @@ -281,6 +337,7 @@ import ColumnRemoveDialog from '../dialogs/ColumnRemoveDialog.vue' import { useProjectsStore } from '../store/projects.js' import { backlogTasks, moveToBacklogPatch } from '../utils/backlogHelpers.js' import { + boardListRows, buildMovePatch, groupTasksByColumn, orderPatchesForStep, @@ -390,6 +447,28 @@ export default { return groupTasksByColumn(this.visibleTasks, this.columns) }, + /** + * The page's view from the query string: `list`, or the board. + * + * @return {string} + * + * @spec openspec/changes/boards-list-toggle/tasks.md#task-1.2 + */ + view() { + return this.$route.query.view === 'list' ? 'list' : 'board' + }, + + /** + * The list view's rows: the board's cards in lane then card order. + * + * @return {Array<{task: object, column: object}>} + * + * @spec openspec/changes/boards-list-toggle/tasks.md#task-1.2 + */ + listRows() { + return boardListRows(this.visibleTasks, this.columns) + }, + /** * Whether the current user may manage the columns: the project owner or * an admin. The server enforces the same rule (ColumnOwnerGuardListener). @@ -719,6 +798,38 @@ export default { await this.patchTasks([{ id: task.id, ...patch }]) }, + /** + * Switch between the board and the list, keeping the rest of the query. + * + * @param {string} view `board` or `list`. + * + * @spec openspec/changes/boards-list-toggle/tasks.md#task-1.2 + */ + setView(view) { + const query = { ...this.$route.query } + if (view === 'list') { + query.view = 'list' + } else { + delete query.view + } + this.$router.replace({ query }) + }, + + /** + * @param {string} priority A task priority. + * @return {string} Its label. + * @spec exclude Display helper — the label of a priority value. + */ + priorityLabel(priority) { + const labels = { + low: this.t('planninq', 'Low'), + normal: this.t('planninq', 'Normal'), + high: this.t('planninq', 'High'), + urgent: this.t('planninq', 'Urgent'), + } + return labels[priority || 'normal'] || '' + }, + /** * Take a card off the board: no column, last in the backlog. * @@ -968,6 +1079,28 @@ export default { border: 1px solid var(--color-warning); } +.project-board__view-switch { + display: flex; + gap: 4px; +} + +.project-board__list { + width: 100%; + border-collapse: collapse; +} + +.project-board__list th, +.project-board__list td { + padding: 4px 8px; + text-align: start; + border-bottom: 1px solid var(--color-border); +} + +.project-board__list th { + font-weight: 600; + color: var(--color-text-maxcontrast); +} + .project-board__add-column { flex: 0 0 auto; padding-top: 4px; diff --git a/tests/e2e/board-list.spec.ts b/tests/e2e/board-list.spec.ts new file mode 100644 index 00000000..fad0b133 --- /dev/null +++ b/tests/e2e/board-list.spec.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * E2E coverage for the board-list capability (boards-list-toggle). + * + * @e2e board-list::switch-to-the-list + * @e2e board-list::the-list-follows-the-label-filter + */ + +import { expect, test } from '@playwright/test' +import { openFixtureProjectBoard } from './nav.ts' + +test.describe('Board as a list', () => { + // @e2e board-list::switch-to-the-list + test('the list shows the board\'s cards and survives a reload', async ({ page }) => { + await openFixtureProjectBoard(page) + const cards = await page.getByTestId('task-card').evaluateAll((els) => els.map((el) => el.getAttribute('aria-label'))) + expect(cards.length).toBeGreaterThan(0) + + await page.getByTestId('view-list').click() + await expect(page).toHaveURL(/view=list/) + const rows = await page.getByTestId('board-list-row').evaluateAll((els) => els.map((el) => el.getAttribute('data-title'))) + expect(rows).toEqual(cards) + + await page.reload() + await expect(page.getByTestId('board-list')).toBeVisible() + }) + + // @e2e board-list::the-list-follows-the-label-filter + test('the list follows the label filter', async ({ page }) => { + await openFixtureProjectBoard(page) + const chips = page.getByTestId('label-filter-chip') + test.skip(await chips.count() < 2, 'the seed carries no label to filter by') + await chips.nth(1).click() + const cards = await page.getByTestId('task-card').count() + await page.getByTestId('view-list').click() + await expect(page.getByTestId('board-list-row')).toHaveCount(cards) + }) +}) diff --git a/tests/vitest/boardColumns.spec.js b/tests/vitest/boardColumns.spec.js index d37742a8..747fe492 100644 --- a/tests/vitest/boardColumns.spec.js +++ b/tests/vitest/boardColumns.spec.js @@ -8,6 +8,7 @@ import addFormats from 'ajv-formats' import { readFileSync } from 'node:fs' import { describe, expect, it } from 'vitest' import { + boardListRows, buildMovePatch, canRemoveColumn, columnPayload, @@ -172,3 +173,15 @@ describe('columnPayload', () => { expect(validate({ title: 'x', project, order: 0, status: 'finished' })).toBe(false) }) }) + +describe('boardListRows', () => { + it('lists the board\'s cards in lane order, then card order, with their column (scenario: switch to the list)', () => { + const rows = boardListRows([ + { id: 'd', column: 'done', columnOrder: 0 }, + { id: 't2', column: 'todo', columnOrder: 2000 }, + { id: 't1', column: 'todo', columnOrder: 1000 }, + { id: 'b', column: null }, + ], columns) + expect(rows.map((row) => [row.task.id, row.column.title])).toEqual([['t1', 'To do'], ['t2', 'To do'], ['d', 'Done']]) + }) +}) From 94c2eda92ff25ada3ba8ecc5becaaee09dcd7ef8 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 28 Sep 2026 23:28:19 +0200 Subject: [PATCH 157/289] feat(projects): members open a project overview, keep a project log with actions, and a scored risk register (#708) * feat(log): a projectLogEntry schema scoped to the project's members * feat(projects): a shared row of project tabs and an overview page with progress * feat(log): a project log with type filters, entry dialog and actions as tasks * feat(log): overview and log strings in every catalogue, app version bumped for the new schema * feat(risks): a risk schema with a server-calculated score, and an admin risk scale that refuses to strand risks * feat(risks): a Risks tab with a heat map and risk dialog, the top risks on the overview, and Risks under Projects * feat(risks): the admin sets the risk scale, refused while risks use a higher level * feat(risks): risk strings in every catalogue; design and tasks follow the code at HEAD * chore(openspec): archive projects-overview-logs-risks, three project rows built * fix(risks): spec tags, visual coverage and demo data for the new pages and schemas; scale checks as instance methods --- appinfo/info.xml | 2 +- l10n/.schema-l10n-baseline.json | 2 +- l10n/be.js | 111 +++- l10n/be.json | 111 +++- l10n/bg.js | 111 +++- l10n/bg.json | 111 +++- l10n/bs.js | 111 +++- l10n/bs.json | 111 +++- l10n/ca.js | 111 +++- l10n/ca.json | 111 +++- l10n/cs.js | 111 +++- l10n/cs.json | 111 +++- l10n/da.js | 111 +++- l10n/da.json | 111 +++- l10n/de.js | 111 +++- l10n/de.json | 111 +++- l10n/el.js | 111 +++- l10n/el.json | 111 +++- l10n/en.js | 113 +++- l10n/en.json | 113 +++- l10n/es.js | 111 +++- l10n/es.json | 111 +++- l10n/et.js | 111 +++- l10n/et.json | 111 +++- l10n/fi.js | 111 +++- l10n/fi.json | 111 +++- l10n/fr.js | 111 +++- l10n/fr.json | 111 +++- l10n/ga.js | 111 +++- l10n/ga.json | 111 +++- l10n/hr.js | 111 +++- l10n/hr.json | 111 +++- l10n/hu.js | 111 +++- l10n/hu.json | 111 +++- l10n/is.js | 111 +++- l10n/is.json | 111 +++- l10n/it.js | 111 +++- l10n/it.json | 111 +++- l10n/lb.js | 111 +++- l10n/lb.json | 111 +++- l10n/lt.js | 111 +++- l10n/lt.json | 111 +++- l10n/lv.js | 111 +++- l10n/lv.json | 111 +++- l10n/mk.js | 111 +++- l10n/mk.json | 111 +++- l10n/mt.js | 111 +++- l10n/mt.json | 111 +++- l10n/nb.js | 111 +++- l10n/nb.json | 111 +++- l10n/nl.js | 111 +++- l10n/nl.json | 111 +++- l10n/pl.js | 111 +++- l10n/pl.json | 111 +++- l10n/pt.js | 111 +++- l10n/pt.json | 111 +++- l10n/rm.js | 111 +++- l10n/rm.json | 111 +++- l10n/ro.js | 111 +++- l10n/ro.json | 111 +++- l10n/ru.js | 111 +++- l10n/ru.json | 111 +++- l10n/sk.js | 111 +++- l10n/sk.json | 111 +++- l10n/sl.js | 111 +++- l10n/sl.json | 111 +++- l10n/sq.js | 111 +++- l10n/sq.json | 111 +++- l10n/sr.js | 111 +++- l10n/sr.json | 111 +++- l10n/sv.js | 111 +++- l10n/sv.json | 111 +++- l10n/tr.js | 111 +++- l10n/tr.json | 111 +++- l10n/uk.js | 111 +++- l10n/uk.json | 111 +++- lib/Controller/SettingsController.php | 47 ++ lib/Listener/ProjectMemberAccessListener.php | 2 +- lib/Service/ProjectMembershipService.php | 2 +- lib/Service/RiskScaleService.php | 258 +++++++++ lib/Service/SettingsService.php | 1 + lib/Settings/planninq_mock_register.json | 108 ++++ lib/Settings/planninq_register.json | 349 +++++++++++- .../design.md | 11 + .../proposal.md | 0 .../specs/project-logs/spec.md | 0 .../specs/project-overview/spec.md | 0 .../specs/risk-register/spec.md | 0 .../tasks.md | 24 +- openspec/parity/capabilities.json | 33 +- openspec/specs/project-delivery/spec.md | 6 +- openspec/specs/project-logs/spec.md | 36 ++ openspec/specs/project-overview/spec.md | 36 ++ openspec/specs/risk-register/spec.md | 51 ++ src/components/ProjectTabs.vue | 98 ++++ src/components/RiskHeatMap.vue | 141 +++++ src/dialogs/LogActionDialog.vue | 167 ++++++ src/dialogs/LogEntryEditDialog.vue | 207 +++++++ src/dialogs/RiskEditDialog.vue | 242 ++++++++ src/icons.js | 2 + src/manifest.json | 9 +- src/registry.js | 6 + src/store/modules/settings.js | 32 ++ src/store/projects.js | 108 ++++ src/utils/projectOverview.js | 216 ++++++++ src/utils/riskHelpers.js | 167 ++++++ src/utils/userNames.js | 53 ++ src/views/ProjectBacklog.vue | 4 + src/views/ProjectBoard.vue | 16 +- src/views/ProjectLog.vue | 519 ++++++++++++++++++ src/views/ProjectOverview.vue | 414 ++++++++++++++ src/views/ProjectRisks.vue | 353 ++++++++++++ src/views/ProjectTimeline.vue | 4 + src/views/settings/Settings.vue | 124 +++++ tests/e2e/backlog.spec.ts | 4 +- tests/e2e/project-log.spec.ts | 74 +++ tests/e2e/project-overview.spec.ts | 57 ++ tests/e2e/project-timeline.spec.ts | 4 +- tests/e2e/risk-register.spec.ts | 101 ++++ tests/e2e/visual/views.spec.ts | 41 +- .../Controller/SettingsControllerTest.php | 79 +++ .../ProjectMemberAccessListenerTest.php | 37 ++ tests/unit/Service/RiskScaleServiceTest.php | 187 +++++++ .../Settings/PlanninqRegisterSchemaTest.php | 85 ++- tests/unit/Support/MembershipFixture.php | 2 + tests/vitest/projectOverview.spec.js | 160 ++++++ tests/vitest/riskHeatMap.spec.js | 125 +++++ 127 files changed, 12863 insertions(+), 161 deletions(-) create mode 100644 lib/Service/RiskScaleService.php rename openspec/changes/{projects-overview-logs-risks => archive/2026-09-28-projects-overview-logs-risks}/design.md (74%) rename openspec/changes/{projects-overview-logs-risks => archive/2026-09-28-projects-overview-logs-risks}/proposal.md (100%) rename openspec/changes/{projects-overview-logs-risks => archive/2026-09-28-projects-overview-logs-risks}/specs/project-logs/spec.md (100%) rename openspec/changes/{projects-overview-logs-risks => archive/2026-09-28-projects-overview-logs-risks}/specs/project-overview/spec.md (100%) rename openspec/changes/{projects-overview-logs-risks => archive/2026-09-28-projects-overview-logs-risks}/specs/risk-register/spec.md (100%) rename openspec/changes/{projects-overview-logs-risks => archive/2026-09-28-projects-overview-logs-risks}/tasks.md (54%) create mode 100644 openspec/specs/project-logs/spec.md create mode 100644 openspec/specs/project-overview/spec.md create mode 100644 openspec/specs/risk-register/spec.md create mode 100644 src/components/ProjectTabs.vue create mode 100644 src/components/RiskHeatMap.vue create mode 100644 src/dialogs/LogActionDialog.vue create mode 100644 src/dialogs/LogEntryEditDialog.vue create mode 100644 src/dialogs/RiskEditDialog.vue create mode 100644 src/utils/projectOverview.js create mode 100644 src/utils/riskHelpers.js create mode 100644 src/utils/userNames.js create mode 100644 src/views/ProjectLog.vue create mode 100644 src/views/ProjectOverview.vue create mode 100644 src/views/ProjectRisks.vue create mode 100644 tests/e2e/project-log.spec.ts create mode 100644 tests/e2e/project-overview.spec.ts create mode 100644 tests/e2e/risk-register.spec.ts create mode 100644 tests/unit/Service/RiskScaleServiceTest.php create mode 100644 tests/vitest/projectOverview.spec.js create mode 100644 tests/vitest/riskHeatMap.spec.js diff --git a/appinfo/info.xml b/appinfo/info.xml index d7b44fa4..78149e5e 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -42,7 +42,7 @@ Vrij en open source onder de EUPL-1.2-licentie. **Ondersteuning:** Voor ondersteuning, neem contact op via support@conduction.nl. ]]> - 0.2.23-unstable.20260928221500 + 0.2.23-unstable.20260928234500 EUPL-1.2 Conduction Planninq diff --git a/l10n/.schema-l10n-baseline.json b/l10n/.schema-l10n-baseline.json index 7869fcbc..8251e2bb 100644 --- a/l10n/.schema-l10n-baseline.json +++ b/l10n/.schema-l10n-baseline.json @@ -1,3 +1,3 @@ { - "uncovered": 135 + "uncovered": 132 } diff --git a/l10n/be.js b/l10n/be.js index 67667378..43df7ee1 100644 --- a/l10n/be.js +++ b/l10n/be.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Дошка", "List": "Спіс", "Column": "Слупок", - "Board or list": "Дошка або спіс" + "Board or list": "Дошка або спіс", + "Log": "Журнал", + "Add entry": "Дадаць запіс", + "Show": "Паказаць", + "No actions yet": "Дзеянняў яшчэ няма", + "Closed": "Закрыта", + "Entry actions": "Дзеянні з запісам", + "Add action": "Дадаць дзеянне", + "by {author}": "аўтар {author}", + "saved {time}": "захавана {time}", + "Attendees: {names}": "Прысутныя: {names}", + "Add the first entry": "Дадаць першы запіс", + "Issues": "Праблемы", + "Lessons learned": "Вывучаныя ўрокі", + "Meetings": "Сустрэчы", + "Decisions": "Рашэнні", + "Actions": "Дзеянні", + "Issue": "Праблема", + "Lesson learned": "Вывучаны ўрок", + "Meeting": "Сустрэча", + "Decision": "Рашэнне", + "About this project": "Пра гэты праект", + "No description yet": "Апісання яшчэ няма", + "Start date": "Дата пачатку", + "Not set": "Не зададзена", + "End date": "Дата заканчэння", + "Progress": "Ход", + "No tasks yet": "Задач яшчэ няма", + "Open the board": "Адкрыць дошку", + "People": "Людзі", + "Owner": "Уладальнік", + "Latest log entries": "Апошнія запісы", + "Open the log": "Адкрыць журнал", + "{done} of {total} tasks done": "Выканана {done} з {total} задач", + "Edit entry": "Рэдагаваць запіс", + "Type": "Від", + "Attendees": "Прысутныя", + "Issue is closed": "Праблема закрыта", + "Could not save the entry. Please try again.": "Не ўдалося захаваць запіс. Паспрабуйце яшчэ раз.", + "From: {title}": "З: {title}", + "What needs to happen": "Што трэба зрабіць", + "Could not create the action. Please try again.": "Не ўдалося стварыць дзеянне. Паспрабуйце яшчэ раз.", + "The action was created but could not be linked to the entry.": "Дзеянне створана, але не ўдалося звязаць яго з запісам.", + "Project pages": "Старонкі праекта", + "Overview": "Агляд", + "Risks": "Рызыкі", + "Log entry": "Запіс журнала", + "A short name for the entry": "Кароткая назва запісу", + "Project": "Праект", + "The project this entry belongs to": "Праект, да якога належыць гэты запіс", + "What kind of entry this is": "Які гэта від запісу", + "When it happened": "Калі гэта адбылося", + "What happened, in Markdown": "Што адбылося, у Markdown", + "Whether an issue is still open": "Ці праблема яшчэ адкрытая", + "The people at a meeting": "Людзі на сустрэчы", + "Tasks that came out of this entry": "Задачы, якія ўзніклі з гэтага запісу", + "Add risk": "Дадаць рызыку", + "Risk": "Рызыка", + "Score": "Ацэнка", + "Response": "Рэагаванне", + "Review date": "Дата перагляду", + "no longer on the project": "больш не ў праекце", + "Edit {title}": "Рэдагаваць {title}", + "No risks recorded yet": "Рызык яшчэ не запісана", + "Add the first risk": "Дадаць першую рызыку", + "Medium": "Сярэдняя", + "Mitigating": "Змякчаецца", + "Occurred": "Наступіла", + "Avoid": "Пазбегнуць", + "Reduce": "Зменшыць", + "Transfer": "Перадаць", + "Accept": "Прыняць", + "Highest open risks": "Найвышэйшыя адкрытыя рызыкі", + "No open risks": "Няма адкрытых рызык", + "Open the risk register": "Адкрыць рэестр рызык", + "Edit risk": "Рэдагаваць рызыку", + "What could go wrong": "Што можа пайсці не так", + "Likelihood": "Імавернасць", + "Impact": "Уплыў", + "Score {score}": "Ацэнка {score}", + "Countermeasures": "Контрмеры", + "Could not save the risk. Please try again.": "Не ўдалося захаваць рызыку. Паспрабуйце яшчэ раз.", + "Risks by likelihood and impact": "Рызыкі паводле імавернасці і ўплыву", + "Risk scale": "Шкала рызык", + "The levels every project scores its risks on, and where low turns into medium and high": "Узроўні, па якіх кожны праект ацэньвае свае рызыкі, і дзе нізкі пераходзіць у сярэдні і высокі", + "Number of levels": "Колькасць узроўняў", + "Likelihood labels": "Назвы імавернасці", + "Impact labels": "Назвы ўплыву", + "Level {level}": "Узровень {level}", + "Medium from score": "Сярэдні ад ацэнкі", + "High from score": "Высокі ад ацэнкі", + "Risk scale saved": "Шкала рызык захавана", + "1 risk uses level {level}. Change it first.": "1 рызыка выкарыстоўвае ўзровень {level}. Спачатку змяніце яе.", + "{count} risks use level {level}. Change them first.": "Рызык на ўзроўні {level}: {count}. Спачатку змяніце іх.", + "Every level needs a label, and high must start above medium.": "Кожнаму ўзроўню патрэбна назва, а высокі мусіць пачынацца вышэй за сярэдні.", + "The project this risk belongs to": "Праект, да якога належыць гэтая рызыка", + "The cause and the effect, in Markdown": "Прычына і вынік, у Markdown", + "Category": "Катэгорыя", + "A free grouping, such as planning, budget or supplier": "Вольнае групаванне, напрыклад планаванне, бюджэт ці пастаўшчык", + "How likely it is, on the admin's risk scale": "Наколькі гэта імаверна, па шкале рызык адміністратара", + "How hard it would hit, on the admin's risk scale": "Наколькі моцна гэта ўдарыць, па шкале рызык адміністратара", + "Likelihood times impact, calculated by the server": "Імавернасць, памножаная на ўплыў, лічыць сервер", + "Where the risk stands": "На якім этапе рызыка", + "The person who watches this risk": "Чалавек, які сочыць за гэтай рызыкай", + "How the project answers the risk": "Як праект рэагуе на рызыку", + "What is done to prevent it or soften it, in Markdown": "Што робіцца, каб яе прадухіліць ці змякчыць, у Markdown", + "When to look at this risk again": "Калі зноў пераглядзець гэтую рызыку", + "Very low": "Вельмі нізкі", + "Very high": "Вельмі высокі", + "All projects": "Усе праекты" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/be.json b/l10n/be.json index c2cdeedd..e38b5be2 100644 --- a/l10n/be.json +++ b/l10n/be.json @@ -308,7 +308,116 @@ "Board": "Дошка", "List": "Спіс", "Column": "Слупок", - "Board or list": "Дошка або спіс" + "Board or list": "Дошка або спіс", + "Log": "Журнал", + "Add entry": "Дадаць запіс", + "Show": "Паказаць", + "No actions yet": "Дзеянняў яшчэ няма", + "Closed": "Закрыта", + "Entry actions": "Дзеянні з запісам", + "Add action": "Дадаць дзеянне", + "by {author}": "аўтар {author}", + "saved {time}": "захавана {time}", + "Attendees: {names}": "Прысутныя: {names}", + "Add the first entry": "Дадаць першы запіс", + "Issues": "Праблемы", + "Lessons learned": "Вывучаныя ўрокі", + "Meetings": "Сустрэчы", + "Decisions": "Рашэнні", + "Actions": "Дзеянні", + "Issue": "Праблема", + "Lesson learned": "Вывучаны ўрок", + "Meeting": "Сустрэча", + "Decision": "Рашэнне", + "About this project": "Пра гэты праект", + "No description yet": "Апісання яшчэ няма", + "Start date": "Дата пачатку", + "Not set": "Не зададзена", + "End date": "Дата заканчэння", + "Progress": "Ход", + "No tasks yet": "Задач яшчэ няма", + "Open the board": "Адкрыць дошку", + "People": "Людзі", + "Owner": "Уладальнік", + "Latest log entries": "Апошнія запісы", + "Open the log": "Адкрыць журнал", + "{done} of {total} tasks done": "Выканана {done} з {total} задач", + "Edit entry": "Рэдагаваць запіс", + "Type": "Від", + "Attendees": "Прысутныя", + "Issue is closed": "Праблема закрыта", + "Could not save the entry. Please try again.": "Не ўдалося захаваць запіс. Паспрабуйце яшчэ раз.", + "From: {title}": "З: {title}", + "What needs to happen": "Што трэба зрабіць", + "Could not create the action. Please try again.": "Не ўдалося стварыць дзеянне. Паспрабуйце яшчэ раз.", + "The action was created but could not be linked to the entry.": "Дзеянне створана, але не ўдалося звязаць яго з запісам.", + "Project pages": "Старонкі праекта", + "Overview": "Агляд", + "Risks": "Рызыкі", + "Log entry": "Запіс журнала", + "A short name for the entry": "Кароткая назва запісу", + "Project": "Праект", + "The project this entry belongs to": "Праект, да якога належыць гэты запіс", + "What kind of entry this is": "Які гэта від запісу", + "When it happened": "Калі гэта адбылося", + "What happened, in Markdown": "Што адбылося, у Markdown", + "Whether an issue is still open": "Ці праблема яшчэ адкрытая", + "The people at a meeting": "Людзі на сустрэчы", + "Tasks that came out of this entry": "Задачы, якія ўзніклі з гэтага запісу", + "Add risk": "Дадаць рызыку", + "Risk": "Рызыка", + "Score": "Ацэнка", + "Response": "Рэагаванне", + "Review date": "Дата перагляду", + "no longer on the project": "больш не ў праекце", + "Edit {title}": "Рэдагаваць {title}", + "No risks recorded yet": "Рызык яшчэ не запісана", + "Add the first risk": "Дадаць першую рызыку", + "Medium": "Сярэдняя", + "Mitigating": "Змякчаецца", + "Occurred": "Наступіла", + "Avoid": "Пазбегнуць", + "Reduce": "Зменшыць", + "Transfer": "Перадаць", + "Accept": "Прыняць", + "Highest open risks": "Найвышэйшыя адкрытыя рызыкі", + "No open risks": "Няма адкрытых рызык", + "Open the risk register": "Адкрыць рэестр рызык", + "Edit risk": "Рэдагаваць рызыку", + "What could go wrong": "Што можа пайсці не так", + "Likelihood": "Імавернасць", + "Impact": "Уплыў", + "Score {score}": "Ацэнка {score}", + "Countermeasures": "Контрмеры", + "Could not save the risk. Please try again.": "Не ўдалося захаваць рызыку. Паспрабуйце яшчэ раз.", + "Risks by likelihood and impact": "Рызыкі паводле імавернасці і ўплыву", + "Risk scale": "Шкала рызык", + "The levels every project scores its risks on, and where low turns into medium and high": "Узроўні, па якіх кожны праект ацэньвае свае рызыкі, і дзе нізкі пераходзіць у сярэдні і высокі", + "Number of levels": "Колькасць узроўняў", + "Likelihood labels": "Назвы імавернасці", + "Impact labels": "Назвы ўплыву", + "Level {level}": "Узровень {level}", + "Medium from score": "Сярэдні ад ацэнкі", + "High from score": "Высокі ад ацэнкі", + "Risk scale saved": "Шкала рызык захавана", + "1 risk uses level {level}. Change it first.": "1 рызыка выкарыстоўвае ўзровень {level}. Спачатку змяніце яе.", + "{count} risks use level {level}. Change them first.": "Рызык на ўзроўні {level}: {count}. Спачатку змяніце іх.", + "Every level needs a label, and high must start above medium.": "Кожнаму ўзроўню патрэбна назва, а высокі мусіць пачынацца вышэй за сярэдні.", + "The project this risk belongs to": "Праект, да якога належыць гэтая рызыка", + "The cause and the effect, in Markdown": "Прычына і вынік, у Markdown", + "Category": "Катэгорыя", + "A free grouping, such as planning, budget or supplier": "Вольнае групаванне, напрыклад планаванне, бюджэт ці пастаўшчык", + "How likely it is, on the admin's risk scale": "Наколькі гэта імаверна, па шкале рызык адміністратара", + "How hard it would hit, on the admin's risk scale": "Наколькі моцна гэта ўдарыць, па шкале рызык адміністратара", + "Likelihood times impact, calculated by the server": "Імавернасць, памножаная на ўплыў, лічыць сервер", + "Where the risk stands": "На якім этапе рызыка", + "The person who watches this risk": "Чалавек, які сочыць за гэтай рызыкай", + "How the project answers the risk": "Як праект рэагуе на рызыку", + "What is done to prevent it or soften it, in Markdown": "Што робіцца, каб яе прадухіліць ці змякчыць, у Markdown", + "When to look at this risk again": "Калі зноў пераглядзець гэтую рызыку", + "Very low": "Вельмі нізкі", + "Very high": "Вельмі высокі", + "All projects": "Усе праекты" }, "plurals": {} } diff --git a/l10n/bg.js b/l10n/bg.js index 80cc0281..090afbb1 100644 --- a/l10n/bg.js +++ b/l10n/bg.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Табло", "List": "Списък", "Column": "Колона", - "Board or list": "Табло или списък" + "Board or list": "Табло или списък", + "Log": "Дневник", + "Add entry": "Добавяне на запис", + "Show": "Показване", + "No actions yet": "Все още няма действия", + "Closed": "Затворен", + "Entry actions": "Действия със записа", + "Add action": "Добавяне на действие", + "by {author}": "от {author}", + "saved {time}": "запазено {time}", + "Attendees: {names}": "Присъстващи: {names}", + "Add the first entry": "Добавяне на първия запис", + "Issues": "Проблеми", + "Lessons learned": "Научени уроци", + "Meetings": "Срещи", + "Decisions": "Решения", + "Actions": "Действия", + "Issue": "Проблем", + "Lesson learned": "Научен урок", + "Meeting": "Среща", + "Decision": "Решение", + "About this project": "За този проект", + "No description yet": "Все още няма описание", + "Start date": "Начална дата", + "Not set": "Не е зададено", + "End date": "Крайна дата", + "Progress": "Напредък", + "No tasks yet": "Все още няма задачи", + "Open the board": "Отваряне на таблото", + "People": "Хора", + "Owner": "Собственик", + "Latest log entries": "Последни записи", + "Open the log": "Отваряне на дневника", + "{done} of {total} tasks done": "{done} от {total} задачи са готови", + "Edit entry": "Редактиране на запис", + "Type": "Вид", + "Attendees": "Присъстващи", + "Issue is closed": "Проблемът е затворен", + "Could not save the entry. Please try again.": "Записът не можа да бъде запазен. Опитайте отново.", + "From: {title}": "От: {title}", + "What needs to happen": "Какво трябва да се направи", + "Could not create the action. Please try again.": "Действието не можа да бъде създадено. Опитайте отново.", + "The action was created but could not be linked to the entry.": "Действието е създадено, но не можа да бъде свързано със записа.", + "Project pages": "Страници на проекта", + "Overview": "Преглед", + "Risks": "Рискове", + "Log entry": "Запис в дневника", + "A short name for the entry": "Кратко име за записа", + "Project": "Проект", + "The project this entry belongs to": "Проектът, към който принадлежи този запис", + "What kind of entry this is": "Какъв вид запис е това", + "When it happened": "Кога се е случило", + "What happened, in Markdown": "Какво се е случило, в Markdown", + "Whether an issue is still open": "Дали проблемът все още е отворен", + "The people at a meeting": "Хората на среща", + "Tasks that came out of this entry": "Задачи, произлезли от този запис", + "Add risk": "Добавяне на риск", + "Risk": "Риск", + "Score": "Оценка", + "Response": "Реакция", + "Review date": "Дата за преглед", + "no longer on the project": "вече не е в проекта", + "Edit {title}": "Редактиране на {title}", + "No risks recorded yet": "Все още няма записани рискове", + "Add the first risk": "Добавяне на първия риск", + "Medium": "Среден", + "Mitigating": "Смекчава се", + "Occurred": "Настъпил", + "Avoid": "Избягване", + "Reduce": "Намаляване", + "Transfer": "Прехвърляне", + "Accept": "Приемане", + "Highest open risks": "Най-високи отворени рискове", + "No open risks": "Няма отворени рискове", + "Open the risk register": "Отваряне на регистъра на рисковете", + "Edit risk": "Редактиране на риск", + "What could go wrong": "Какво може да се обърка", + "Likelihood": "Вероятност", + "Impact": "Въздействие", + "Score {score}": "Оценка {score}", + "Countermeasures": "Мерки", + "Could not save the risk. Please try again.": "Рискът не можа да бъде запазен. Опитайте отново.", + "Risks by likelihood and impact": "Рискове по вероятност и въздействие", + "Risk scale": "Скала на риска", + "The levels every project scores its risks on, and where low turns into medium and high": "Нивата, по които всеки проект оценява рисковете си, и къде ниско преминава в средно и високо", + "Number of levels": "Брой нива", + "Likelihood labels": "Етикети за вероятност", + "Impact labels": "Етикети за въздействие", + "Level {level}": "Ниво {level}", + "Medium from score": "Средно от оценка", + "High from score": "Високо от оценка", + "Risk scale saved": "Скалата на риска е запазена", + "1 risk uses level {level}. Change it first.": "1 риск използва ниво {level}. Първо го променете.", + "{count} risks use level {level}. Change them first.": "{count} риска използват ниво {level}. Първо ги променете.", + "Every level needs a label, and high must start above medium.": "Всяко ниво има нужда от етикет, а високо трябва да започва над средно.", + "The project this risk belongs to": "Проектът, към който принадлежи този риск", + "The cause and the effect, in Markdown": "Причината и последицата, в Markdown", + "Category": "Категория", + "A free grouping, such as planning, budget or supplier": "Свободно групиране, например планиране, бюджет или доставчик", + "How likely it is, on the admin's risk scale": "Колко вероятно е, по скалата на риска на администратора", + "How hard it would hit, on the admin's risk scale": "Колко силно би ударило, по скалата на риска на администратора", + "Likelihood times impact, calculated by the server": "Вероятност по въздействие, изчислено от сървъра", + "Where the risk stands": "Докъде е стигнал рискът", + "The person who watches this risk": "Човекът, който следи този риск", + "How the project answers the risk": "Как проектът отговаря на риска", + "What is done to prevent it or soften it, in Markdown": "Какво се прави, за да се предотврати или смекчи, в Markdown", + "When to look at this risk again": "Кога рискът да се прегледа отново", + "Very low": "Много ниско", + "Very high": "Много високо", + "All projects": "Всички проекти" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/bg.json b/l10n/bg.json index 77d81a02..1eae4dc1 100644 --- a/l10n/bg.json +++ b/l10n/bg.json @@ -308,7 +308,116 @@ "Board": "Табло", "List": "Списък", "Column": "Колона", - "Board or list": "Табло или списък" + "Board or list": "Табло или списък", + "Log": "Дневник", + "Add entry": "Добавяне на запис", + "Show": "Показване", + "No actions yet": "Все още няма действия", + "Closed": "Затворен", + "Entry actions": "Действия със записа", + "Add action": "Добавяне на действие", + "by {author}": "от {author}", + "saved {time}": "запазено {time}", + "Attendees: {names}": "Присъстващи: {names}", + "Add the first entry": "Добавяне на първия запис", + "Issues": "Проблеми", + "Lessons learned": "Научени уроци", + "Meetings": "Срещи", + "Decisions": "Решения", + "Actions": "Действия", + "Issue": "Проблем", + "Lesson learned": "Научен урок", + "Meeting": "Среща", + "Decision": "Решение", + "About this project": "За този проект", + "No description yet": "Все още няма описание", + "Start date": "Начална дата", + "Not set": "Не е зададено", + "End date": "Крайна дата", + "Progress": "Напредък", + "No tasks yet": "Все още няма задачи", + "Open the board": "Отваряне на таблото", + "People": "Хора", + "Owner": "Собственик", + "Latest log entries": "Последни записи", + "Open the log": "Отваряне на дневника", + "{done} of {total} tasks done": "{done} от {total} задачи са готови", + "Edit entry": "Редактиране на запис", + "Type": "Вид", + "Attendees": "Присъстващи", + "Issue is closed": "Проблемът е затворен", + "Could not save the entry. Please try again.": "Записът не можа да бъде запазен. Опитайте отново.", + "From: {title}": "От: {title}", + "What needs to happen": "Какво трябва да се направи", + "Could not create the action. Please try again.": "Действието не можа да бъде създадено. Опитайте отново.", + "The action was created but could not be linked to the entry.": "Действието е създадено, но не можа да бъде свързано със записа.", + "Project pages": "Страници на проекта", + "Overview": "Преглед", + "Risks": "Рискове", + "Log entry": "Запис в дневника", + "A short name for the entry": "Кратко име за записа", + "Project": "Проект", + "The project this entry belongs to": "Проектът, към който принадлежи този запис", + "What kind of entry this is": "Какъв вид запис е това", + "When it happened": "Кога се е случило", + "What happened, in Markdown": "Какво се е случило, в Markdown", + "Whether an issue is still open": "Дали проблемът все още е отворен", + "The people at a meeting": "Хората на среща", + "Tasks that came out of this entry": "Задачи, произлезли от този запис", + "Add risk": "Добавяне на риск", + "Risk": "Риск", + "Score": "Оценка", + "Response": "Реакция", + "Review date": "Дата за преглед", + "no longer on the project": "вече не е в проекта", + "Edit {title}": "Редактиране на {title}", + "No risks recorded yet": "Все още няма записани рискове", + "Add the first risk": "Добавяне на първия риск", + "Medium": "Среден", + "Mitigating": "Смекчава се", + "Occurred": "Настъпил", + "Avoid": "Избягване", + "Reduce": "Намаляване", + "Transfer": "Прехвърляне", + "Accept": "Приемане", + "Highest open risks": "Най-високи отворени рискове", + "No open risks": "Няма отворени рискове", + "Open the risk register": "Отваряне на регистъра на рисковете", + "Edit risk": "Редактиране на риск", + "What could go wrong": "Какво може да се обърка", + "Likelihood": "Вероятност", + "Impact": "Въздействие", + "Score {score}": "Оценка {score}", + "Countermeasures": "Мерки", + "Could not save the risk. Please try again.": "Рискът не можа да бъде запазен. Опитайте отново.", + "Risks by likelihood and impact": "Рискове по вероятност и въздействие", + "Risk scale": "Скала на риска", + "The levels every project scores its risks on, and where low turns into medium and high": "Нивата, по които всеки проект оценява рисковете си, и къде ниско преминава в средно и високо", + "Number of levels": "Брой нива", + "Likelihood labels": "Етикети за вероятност", + "Impact labels": "Етикети за въздействие", + "Level {level}": "Ниво {level}", + "Medium from score": "Средно от оценка", + "High from score": "Високо от оценка", + "Risk scale saved": "Скалата на риска е запазена", + "1 risk uses level {level}. Change it first.": "1 риск използва ниво {level}. Първо го променете.", + "{count} risks use level {level}. Change them first.": "{count} риска използват ниво {level}. Първо ги променете.", + "Every level needs a label, and high must start above medium.": "Всяко ниво има нужда от етикет, а високо трябва да започва над средно.", + "The project this risk belongs to": "Проектът, към който принадлежи този риск", + "The cause and the effect, in Markdown": "Причината и последицата, в Markdown", + "Category": "Категория", + "A free grouping, such as planning, budget or supplier": "Свободно групиране, например планиране, бюджет или доставчик", + "How likely it is, on the admin's risk scale": "Колко вероятно е, по скалата на риска на администратора", + "How hard it would hit, on the admin's risk scale": "Колко силно би ударило, по скалата на риска на администратора", + "Likelihood times impact, calculated by the server": "Вероятност по въздействие, изчислено от сървъра", + "Where the risk stands": "Докъде е стигнал рискът", + "The person who watches this risk": "Човекът, който следи този риск", + "How the project answers the risk": "Как проектът отговаря на риска", + "What is done to prevent it or soften it, in Markdown": "Какво се прави, за да се предотврати или смекчи, в Markdown", + "When to look at this risk again": "Кога рискът да се прегледа отново", + "Very low": "Много ниско", + "Very high": "Много високо", + "All projects": "Всички проекти" }, "plurals": null } diff --git a/l10n/bs.js b/l10n/bs.js index c3b09c46..488f8e81 100644 --- a/l10n/bs.js +++ b/l10n/bs.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tabla", "List": "Lista", "Column": "Kolona", - "Board or list": "Tabla ili lista" + "Board or list": "Tabla ili lista", + "Log": "Dnevnik", + "Add entry": "Dodaj unos", + "Show": "Prikaži", + "No actions yet": "Još nema radnji", + "Closed": "Zatvoreno", + "Entry actions": "Radnje unosa", + "Add action": "Dodaj radnju", + "by {author}": "autor {author}", + "saved {time}": "sačuvano {time}", + "Attendees: {names}": "Prisutni: {names}", + "Add the first entry": "Dodaj prvi unos", + "Issues": "Problemi", + "Lessons learned": "Naučene lekcije", + "Meetings": "Sastanci", + "Decisions": "Odluke", + "Actions": "Radnje", + "Issue": "Problem", + "Lesson learned": "Naučena lekcija", + "Meeting": "Sastanak", + "Decision": "Odluka", + "About this project": "O ovom projektu", + "No description yet": "Još nema opisa", + "Start date": "Datum početka", + "Not set": "Nije postavljeno", + "End date": "Datum završetka", + "Progress": "Napredak", + "No tasks yet": "Još nema zadataka", + "Open the board": "Otvori tablu", + "People": "Osobe", + "Owner": "Vlasnik", + "Latest log entries": "Najnoviji unosi", + "Open the log": "Otvori dnevnik", + "{done} of {total} tasks done": "Gotovo {done} od {total} zadataka", + "Edit entry": "Uredi unos", + "Type": "Vrsta", + "Attendees": "Prisutni", + "Issue is closed": "Problem je zatvoren", + "Could not save the entry. Please try again.": "Unos nije moguće sačuvati. Pokušajte ponovo.", + "From: {title}": "Iz: {title}", + "What needs to happen": "Šta treba uraditi", + "Could not create the action. Please try again.": "Radnju nije moguće kreirati. Pokušajte ponovo.", + "The action was created but could not be linked to the entry.": "Radnja je kreirana, ali je nije moguće povezati s unosom.", + "Project pages": "Stranice projekta", + "Overview": "Pregled", + "Risks": "Rizici", + "Log entry": "Unos u dnevnik", + "A short name for the entry": "Kratak naziv unosa", + "Project": "Projekat", + "The project this entry belongs to": "Projekat kojem ovaj unos pripada", + "What kind of entry this is": "Koja je ovo vrsta unosa", + "When it happened": "Kada se desilo", + "What happened, in Markdown": "Šta se desilo, u Markdownu", + "Whether an issue is still open": "Da li je problem još otvoren", + "The people at a meeting": "Osobe na sastanku", + "Tasks that came out of this entry": "Zadaci proizašli iz ovog unosa", + "Add risk": "Dodaj rizik", + "Risk": "Rizik", + "Score": "Ocjena", + "Response": "Odgovor", + "Review date": "Datum pregleda", + "no longer on the project": "više nije na projektu", + "Edit {title}": "Uredi {title}", + "No risks recorded yet": "Još nema zabilježenih rizika", + "Add the first risk": "Dodaj prvi rizik", + "Medium": "Srednje", + "Mitigating": "Ublažava se", + "Occurred": "Nastupio", + "Avoid": "Izbjeći", + "Reduce": "Smanjiti", + "Transfer": "Prenijeti", + "Accept": "Prihvatiti", + "Highest open risks": "Najviši otvoreni rizici", + "No open risks": "Nema otvorenih rizika", + "Open the risk register": "Otvori registar rizika", + "Edit risk": "Uredi rizik", + "What could go wrong": "Šta može poći po zlu", + "Likelihood": "Vjerovatnoća", + "Impact": "Utjecaj", + "Score {score}": "Ocjena {score}", + "Countermeasures": "Protumjere", + "Could not save the risk. Please try again.": "Rizik nije moguće sačuvati. Pokušajte ponovo.", + "Risks by likelihood and impact": "Rizici po vjerovatnoći i utjecaju", + "Risk scale": "Skala rizika", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivoi na kojima svaki projekat ocjenjuje svoje rizike, i gdje nisko prelazi u srednje i visoko", + "Number of levels": "Broj nivoa", + "Likelihood labels": "Oznake vjerovatnoće", + "Impact labels": "Oznake utjecaja", + "Level {level}": "Nivo {level}", + "Medium from score": "Srednje od ocjene", + "High from score": "Visoko od ocjene", + "Risk scale saved": "Skala rizika sačuvana", + "1 risk uses level {level}. Change it first.": "1 rizik koristi nivo {level}. Prvo ga promijenite.", + "{count} risks use level {level}. Change them first.": "Rizika koji koriste nivo {level}: {count}. Prvo ih promijenite.", + "Every level needs a label, and high must start above medium.": "Svaki nivo treba oznaku, a visoko mora početi iznad srednjeg.", + "The project this risk belongs to": "Projekat kojem ovaj rizik pripada", + "The cause and the effect, in Markdown": "Uzrok i posljedica, u Markdownu", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Slobodno grupisanje, npr. planiranje, budžet ili dobavljač", + "How likely it is, on the admin's risk scale": "Koliko je vjerovatno, na skali rizika administratora", + "How hard it would hit, on the admin's risk scale": "Koliko bi snažno pogodilo, na skali rizika administratora", + "Likelihood times impact, calculated by the server": "Vjerovatnoća puta utjecaj, izračunava server", + "Where the risk stands": "Gdje je rizik", + "The person who watches this risk": "Osoba koja prati ovaj rizik", + "How the project answers the risk": "Kako projekat odgovara na rizik", + "What is done to prevent it or soften it, in Markdown": "Šta se radi da se spriječi ili ublaži, u Markdownu", + "When to look at this risk again": "Kada ponovo pregledati ovaj rizik", + "Very low": "Vrlo nisko", + "Very high": "Vrlo visoko", + "All projects": "Svi projekti" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/bs.json b/l10n/bs.json index 9dfaad95..27d7b672 100644 --- a/l10n/bs.json +++ b/l10n/bs.json @@ -308,7 +308,116 @@ "Board": "Tabla", "List": "Lista", "Column": "Kolona", - "Board or list": "Tabla ili lista" + "Board or list": "Tabla ili lista", + "Log": "Dnevnik", + "Add entry": "Dodaj unos", + "Show": "Prikaži", + "No actions yet": "Još nema radnji", + "Closed": "Zatvoreno", + "Entry actions": "Radnje unosa", + "Add action": "Dodaj radnju", + "by {author}": "autor {author}", + "saved {time}": "sačuvano {time}", + "Attendees: {names}": "Prisutni: {names}", + "Add the first entry": "Dodaj prvi unos", + "Issues": "Problemi", + "Lessons learned": "Naučene lekcije", + "Meetings": "Sastanci", + "Decisions": "Odluke", + "Actions": "Radnje", + "Issue": "Problem", + "Lesson learned": "Naučena lekcija", + "Meeting": "Sastanak", + "Decision": "Odluka", + "About this project": "O ovom projektu", + "No description yet": "Još nema opisa", + "Start date": "Datum početka", + "Not set": "Nije postavljeno", + "End date": "Datum završetka", + "Progress": "Napredak", + "No tasks yet": "Još nema zadataka", + "Open the board": "Otvori tablu", + "People": "Osobe", + "Owner": "Vlasnik", + "Latest log entries": "Najnoviji unosi", + "Open the log": "Otvori dnevnik", + "{done} of {total} tasks done": "Gotovo {done} od {total} zadataka", + "Edit entry": "Uredi unos", + "Type": "Vrsta", + "Attendees": "Prisutni", + "Issue is closed": "Problem je zatvoren", + "Could not save the entry. Please try again.": "Unos nije moguće sačuvati. Pokušajte ponovo.", + "From: {title}": "Iz: {title}", + "What needs to happen": "Šta treba uraditi", + "Could not create the action. Please try again.": "Radnju nije moguće kreirati. Pokušajte ponovo.", + "The action was created but could not be linked to the entry.": "Radnja je kreirana, ali je nije moguće povezati s unosom.", + "Project pages": "Stranice projekta", + "Overview": "Pregled", + "Risks": "Rizici", + "Log entry": "Unos u dnevnik", + "A short name for the entry": "Kratak naziv unosa", + "Project": "Projekat", + "The project this entry belongs to": "Projekat kojem ovaj unos pripada", + "What kind of entry this is": "Koja je ovo vrsta unosa", + "When it happened": "Kada se desilo", + "What happened, in Markdown": "Šta se desilo, u Markdownu", + "Whether an issue is still open": "Da li je problem još otvoren", + "The people at a meeting": "Osobe na sastanku", + "Tasks that came out of this entry": "Zadaci proizašli iz ovog unosa", + "Add risk": "Dodaj rizik", + "Risk": "Rizik", + "Score": "Ocjena", + "Response": "Odgovor", + "Review date": "Datum pregleda", + "no longer on the project": "više nije na projektu", + "Edit {title}": "Uredi {title}", + "No risks recorded yet": "Još nema zabilježenih rizika", + "Add the first risk": "Dodaj prvi rizik", + "Medium": "Srednje", + "Mitigating": "Ublažava se", + "Occurred": "Nastupio", + "Avoid": "Izbjeći", + "Reduce": "Smanjiti", + "Transfer": "Prenijeti", + "Accept": "Prihvatiti", + "Highest open risks": "Najviši otvoreni rizici", + "No open risks": "Nema otvorenih rizika", + "Open the risk register": "Otvori registar rizika", + "Edit risk": "Uredi rizik", + "What could go wrong": "Šta može poći po zlu", + "Likelihood": "Vjerovatnoća", + "Impact": "Utjecaj", + "Score {score}": "Ocjena {score}", + "Countermeasures": "Protumjere", + "Could not save the risk. Please try again.": "Rizik nije moguće sačuvati. Pokušajte ponovo.", + "Risks by likelihood and impact": "Rizici po vjerovatnoći i utjecaju", + "Risk scale": "Skala rizika", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivoi na kojima svaki projekat ocjenjuje svoje rizike, i gdje nisko prelazi u srednje i visoko", + "Number of levels": "Broj nivoa", + "Likelihood labels": "Oznake vjerovatnoće", + "Impact labels": "Oznake utjecaja", + "Level {level}": "Nivo {level}", + "Medium from score": "Srednje od ocjene", + "High from score": "Visoko od ocjene", + "Risk scale saved": "Skala rizika sačuvana", + "1 risk uses level {level}. Change it first.": "1 rizik koristi nivo {level}. Prvo ga promijenite.", + "{count} risks use level {level}. Change them first.": "Rizika koji koriste nivo {level}: {count}. Prvo ih promijenite.", + "Every level needs a label, and high must start above medium.": "Svaki nivo treba oznaku, a visoko mora početi iznad srednjeg.", + "The project this risk belongs to": "Projekat kojem ovaj rizik pripada", + "The cause and the effect, in Markdown": "Uzrok i posljedica, u Markdownu", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Slobodno grupisanje, npr. planiranje, budžet ili dobavljač", + "How likely it is, on the admin's risk scale": "Koliko je vjerovatno, na skali rizika administratora", + "How hard it would hit, on the admin's risk scale": "Koliko bi snažno pogodilo, na skali rizika administratora", + "Likelihood times impact, calculated by the server": "Vjerovatnoća puta utjecaj, izračunava server", + "Where the risk stands": "Gdje je rizik", + "The person who watches this risk": "Osoba koja prati ovaj rizik", + "How the project answers the risk": "Kako projekat odgovara na rizik", + "What is done to prevent it or soften it, in Markdown": "Šta se radi da se spriječi ili ublaži, u Markdownu", + "When to look at this risk again": "Kada ponovo pregledati ovaj rizik", + "Very low": "Vrlo nisko", + "Very high": "Vrlo visoko", + "All projects": "Svi projekti" }, "plurals": null } diff --git a/l10n/ca.js b/l10n/ca.js index cf809476..360122f6 100644 --- a/l10n/ca.js +++ b/l10n/ca.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tauler", "List": "Llista", "Column": "Columna", - "Board or list": "Tauler o llista" + "Board or list": "Tauler o llista", + "Log": "Registre", + "Add entry": "Afegeix una entrada", + "Show": "Mostra", + "No actions yet": "Encara no hi ha accions", + "Closed": "Tancat", + "Entry actions": "Accions de l'entrada", + "Add action": "Afegeix una acció", + "by {author}": "per {author}", + "saved {time}": "desat {time}", + "Attendees: {names}": "Assistents: {names}", + "Add the first entry": "Afegeix la primera entrada", + "Issues": "Incidències", + "Lessons learned": "Lliçons apreses", + "Meetings": "Reunions", + "Decisions": "Decisions", + "Actions": "Accions", + "Issue": "Incidència", + "Lesson learned": "Lliçó apresa", + "Meeting": "Reunió", + "Decision": "Decisió", + "About this project": "Quant a aquest projecte", + "No description yet": "Encara no hi ha descripció", + "Start date": "Data d'inici", + "Not set": "Sense definir", + "End date": "Data de finalització", + "Progress": "Progrés", + "No tasks yet": "Encara no hi ha tasques", + "Open the board": "Obre el tauler", + "People": "Persones", + "Owner": "Propietari", + "Latest log entries": "Darreres entrades", + "Open the log": "Obre el registre", + "{done} of {total} tasks done": "{done} de {total} tasques fetes", + "Edit entry": "Edita l'entrada", + "Type": "Tipus", + "Attendees": "Assistents", + "Issue is closed": "La incidència està tancada", + "Could not save the entry. Please try again.": "No s'ha pogut desar l'entrada. Torneu-ho a provar.", + "From: {title}": "De: {title}", + "What needs to happen": "Què cal fer", + "Could not create the action. Please try again.": "No s'ha pogut crear l'acció. Torneu-ho a provar.", + "The action was created but could not be linked to the entry.": "L'acció s'ha creat, però no s'ha pogut enllaçar amb l'entrada.", + "Project pages": "Pàgines del projecte", + "Overview": "Visió general", + "Risks": "Riscos", + "Log entry": "Entrada del registre", + "A short name for the entry": "Un nom curt per a l'entrada", + "Project": "Projecte", + "The project this entry belongs to": "El projecte al qual pertany aquesta entrada", + "What kind of entry this is": "Quin tipus d'entrada és", + "When it happened": "Quan va passar", + "What happened, in Markdown": "Què va passar, en Markdown", + "Whether an issue is still open": "Si una incidència encara és oberta", + "The people at a meeting": "Les persones en una reunió", + "Tasks that came out of this entry": "Tasques sorgides d'aquesta entrada", + "Add risk": "Afegeix un risc", + "Risk": "Risc", + "Score": "Puntuació", + "Response": "Resposta", + "Review date": "Data de revisió", + "no longer on the project": "ja no és al projecte", + "Edit {title}": "Edita {title}", + "No risks recorded yet": "Encara no hi ha riscos registrats", + "Add the first risk": "Afegeix el primer risc", + "Medium": "Mitjà", + "Mitigating": "En mitigació", + "Occurred": "S'ha produït", + "Avoid": "Evitar", + "Reduce": "Reduir", + "Transfer": "Transferir", + "Accept": "Acceptar", + "Highest open risks": "Riscos oberts més alts", + "No open risks": "No hi ha riscos oberts", + "Open the risk register": "Obre el registre de riscos", + "Edit risk": "Edita el risc", + "What could go wrong": "Què podria sortir malament", + "Likelihood": "Probabilitat", + "Impact": "Impacte", + "Score {score}": "Puntuació {score}", + "Countermeasures": "Contramesures", + "Could not save the risk. Please try again.": "No s'ha pogut desar el risc. Torneu-ho a provar.", + "Risks by likelihood and impact": "Riscos per probabilitat i impacte", + "Risk scale": "Escala de risc", + "The levels every project scores its risks on, and where low turns into medium and high": "Els nivells amb què cada projecte puntua els seus riscos, i on baix passa a mitjà i alt", + "Number of levels": "Nombre de nivells", + "Likelihood labels": "Etiquetes de probabilitat", + "Impact labels": "Etiquetes d'impacte", + "Level {level}": "Nivell {level}", + "Medium from score": "Mitjà a partir de la puntuació", + "High from score": "Alt a partir de la puntuació", + "Risk scale saved": "S'ha desat l'escala de risc", + "1 risk uses level {level}. Change it first.": "1 risc fa servir el nivell {level}. Canvieu-lo primer.", + "{count} risks use level {level}. Change them first.": "{count} riscos fan servir el nivell {level}. Canvieu-los primer.", + "Every level needs a label, and high must start above medium.": "Cada nivell necessita una etiqueta, i alt ha de començar per sobre de mitjà.", + "The project this risk belongs to": "El projecte al qual pertany aquest risc", + "The cause and the effect, in Markdown": "La causa i l'efecte, en Markdown", + "Category": "Categoria", + "A free grouping, such as planning, budget or supplier": "Una agrupació lliure, com planificació, pressupost o proveïdor", + "How likely it is, on the admin's risk scale": "Com de probable és, a l'escala de risc de l'administrador", + "How hard it would hit, on the admin's risk scale": "Com de fort colpejaria, a l'escala de risc de l'administrador", + "Likelihood times impact, calculated by the server": "Probabilitat per impacte, calculat pel servidor", + "Where the risk stands": "On és el risc", + "The person who watches this risk": "La persona que vigila aquest risc", + "How the project answers the risk": "Com respon el projecte al risc", + "What is done to prevent it or soften it, in Markdown": "Què es fa per evitar-lo o suavitzar-lo, en Markdown", + "When to look at this risk again": "Quan cal revisar de nou aquest risc", + "Very low": "Molt baix", + "Very high": "Molt alt", + "All projects": "Tots els projectes" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ca.json b/l10n/ca.json index 3f74c7aa..3de7ce0b 100644 --- a/l10n/ca.json +++ b/l10n/ca.json @@ -308,7 +308,116 @@ "Board": "Tauler", "List": "Llista", "Column": "Columna", - "Board or list": "Tauler o llista" + "Board or list": "Tauler o llista", + "Log": "Registre", + "Add entry": "Afegeix una entrada", + "Show": "Mostra", + "No actions yet": "Encara no hi ha accions", + "Closed": "Tancat", + "Entry actions": "Accions de l'entrada", + "Add action": "Afegeix una acció", + "by {author}": "per {author}", + "saved {time}": "desat {time}", + "Attendees: {names}": "Assistents: {names}", + "Add the first entry": "Afegeix la primera entrada", + "Issues": "Incidències", + "Lessons learned": "Lliçons apreses", + "Meetings": "Reunions", + "Decisions": "Decisions", + "Actions": "Accions", + "Issue": "Incidència", + "Lesson learned": "Lliçó apresa", + "Meeting": "Reunió", + "Decision": "Decisió", + "About this project": "Quant a aquest projecte", + "No description yet": "Encara no hi ha descripció", + "Start date": "Data d'inici", + "Not set": "Sense definir", + "End date": "Data de finalització", + "Progress": "Progrés", + "No tasks yet": "Encara no hi ha tasques", + "Open the board": "Obre el tauler", + "People": "Persones", + "Owner": "Propietari", + "Latest log entries": "Darreres entrades", + "Open the log": "Obre el registre", + "{done} of {total} tasks done": "{done} de {total} tasques fetes", + "Edit entry": "Edita l'entrada", + "Type": "Tipus", + "Attendees": "Assistents", + "Issue is closed": "La incidència està tancada", + "Could not save the entry. Please try again.": "No s'ha pogut desar l'entrada. Torneu-ho a provar.", + "From: {title}": "De: {title}", + "What needs to happen": "Què cal fer", + "Could not create the action. Please try again.": "No s'ha pogut crear l'acció. Torneu-ho a provar.", + "The action was created but could not be linked to the entry.": "L'acció s'ha creat, però no s'ha pogut enllaçar amb l'entrada.", + "Project pages": "Pàgines del projecte", + "Overview": "Visió general", + "Risks": "Riscos", + "Log entry": "Entrada del registre", + "A short name for the entry": "Un nom curt per a l'entrada", + "Project": "Projecte", + "The project this entry belongs to": "El projecte al qual pertany aquesta entrada", + "What kind of entry this is": "Quin tipus d'entrada és", + "When it happened": "Quan va passar", + "What happened, in Markdown": "Què va passar, en Markdown", + "Whether an issue is still open": "Si una incidència encara és oberta", + "The people at a meeting": "Les persones en una reunió", + "Tasks that came out of this entry": "Tasques sorgides d'aquesta entrada", + "Add risk": "Afegeix un risc", + "Risk": "Risc", + "Score": "Puntuació", + "Response": "Resposta", + "Review date": "Data de revisió", + "no longer on the project": "ja no és al projecte", + "Edit {title}": "Edita {title}", + "No risks recorded yet": "Encara no hi ha riscos registrats", + "Add the first risk": "Afegeix el primer risc", + "Medium": "Mitjà", + "Mitigating": "En mitigació", + "Occurred": "S'ha produït", + "Avoid": "Evitar", + "Reduce": "Reduir", + "Transfer": "Transferir", + "Accept": "Acceptar", + "Highest open risks": "Riscos oberts més alts", + "No open risks": "No hi ha riscos oberts", + "Open the risk register": "Obre el registre de riscos", + "Edit risk": "Edita el risc", + "What could go wrong": "Què podria sortir malament", + "Likelihood": "Probabilitat", + "Impact": "Impacte", + "Score {score}": "Puntuació {score}", + "Countermeasures": "Contramesures", + "Could not save the risk. Please try again.": "No s'ha pogut desar el risc. Torneu-ho a provar.", + "Risks by likelihood and impact": "Riscos per probabilitat i impacte", + "Risk scale": "Escala de risc", + "The levels every project scores its risks on, and where low turns into medium and high": "Els nivells amb què cada projecte puntua els seus riscos, i on baix passa a mitjà i alt", + "Number of levels": "Nombre de nivells", + "Likelihood labels": "Etiquetes de probabilitat", + "Impact labels": "Etiquetes d'impacte", + "Level {level}": "Nivell {level}", + "Medium from score": "Mitjà a partir de la puntuació", + "High from score": "Alt a partir de la puntuació", + "Risk scale saved": "S'ha desat l'escala de risc", + "1 risk uses level {level}. Change it first.": "1 risc fa servir el nivell {level}. Canvieu-lo primer.", + "{count} risks use level {level}. Change them first.": "{count} riscos fan servir el nivell {level}. Canvieu-los primer.", + "Every level needs a label, and high must start above medium.": "Cada nivell necessita una etiqueta, i alt ha de començar per sobre de mitjà.", + "The project this risk belongs to": "El projecte al qual pertany aquest risc", + "The cause and the effect, in Markdown": "La causa i l'efecte, en Markdown", + "Category": "Categoria", + "A free grouping, such as planning, budget or supplier": "Una agrupació lliure, com planificació, pressupost o proveïdor", + "How likely it is, on the admin's risk scale": "Com de probable és, a l'escala de risc de l'administrador", + "How hard it would hit, on the admin's risk scale": "Com de fort colpejaria, a l'escala de risc de l'administrador", + "Likelihood times impact, calculated by the server": "Probabilitat per impacte, calculat pel servidor", + "Where the risk stands": "On és el risc", + "The person who watches this risk": "La persona que vigila aquest risc", + "How the project answers the risk": "Com respon el projecte al risc", + "What is done to prevent it or soften it, in Markdown": "Què es fa per evitar-lo o suavitzar-lo, en Markdown", + "When to look at this risk again": "Quan cal revisar de nou aquest risc", + "Very low": "Molt baix", + "Very high": "Molt alt", + "All projects": "Tots els projectes" }, "plurals": null } diff --git a/l10n/cs.js b/l10n/cs.js index 35f7dbec..823e5a7e 100644 --- a/l10n/cs.js +++ b/l10n/cs.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Nástěnka", "List": "Seznam", "Column": "Sloupec", - "Board or list": "Nástěnka nebo seznam" + "Board or list": "Nástěnka nebo seznam", + "Log": "Deník", + "Add entry": "Přidat záznam", + "Show": "Zobrazit", + "No actions yet": "Zatím žádné akce", + "Closed": "Uzavřeno", + "Entry actions": "Akce záznamu", + "Add action": "Přidat akci", + "by {author}": "autor {author}", + "saved {time}": "uloženo {time}", + "Attendees: {names}": "Účastníci: {names}", + "Add the first entry": "Přidat první záznam", + "Issues": "Problémy", + "Lessons learned": "Poučení", + "Meetings": "Schůzky", + "Decisions": "Rozhodnutí", + "Actions": "Akce", + "Issue": "Problém", + "Lesson learned": "Poučení", + "Meeting": "Schůzka", + "Decision": "Rozhodnutí", + "About this project": "O tomto projektu", + "No description yet": "Zatím bez popisu", + "Start date": "Datum zahájení", + "Not set": "Nenastaveno", + "End date": "Datum ukončení", + "Progress": "Postup", + "No tasks yet": "Zatím žádné úkoly", + "Open the board": "Otevřít nástěnku", + "People": "Lidé", + "Owner": "Vlastník", + "Latest log entries": "Nejnovější záznamy", + "Open the log": "Otevřít deník", + "{done} of {total} tasks done": "Hotovo {done} z {total} úkolů", + "Edit entry": "Upravit záznam", + "Type": "Druh", + "Attendees": "Účastníci", + "Issue is closed": "Problém je uzavřen", + "Could not save the entry. Please try again.": "Záznam se nepodařilo uložit. Zkuste to znovu.", + "From: {title}": "Z: {title}", + "What needs to happen": "Co je třeba udělat", + "Could not create the action. Please try again.": "Akci se nepodařilo vytvořit. Zkuste to znovu.", + "The action was created but could not be linked to the entry.": "Akce byla vytvořena, ale nepodařilo se ji propojit se záznamem.", + "Project pages": "Stránky projektu", + "Overview": "Přehled", + "Risks": "Rizika", + "Log entry": "Záznam deníku", + "A short name for the entry": "Krátký název záznamu", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, ke kterému záznam patří", + "What kind of entry this is": "Jaký je to druh záznamu", + "When it happened": "Kdy se to stalo", + "What happened, in Markdown": "Co se stalo, v Markdownu", + "Whether an issue is still open": "Zda je problém stále otevřený", + "The people at a meeting": "Lidé na schůzce", + "Tasks that came out of this entry": "Úkoly, které vzešly z tohoto záznamu", + "Add risk": "Přidat riziko", + "Risk": "Riziko", + "Score": "Skóre", + "Response": "Reakce", + "Review date": "Datum revize", + "no longer on the project": "už není v projektu", + "Edit {title}": "Upravit {title}", + "No risks recorded yet": "Zatím žádná zaznamenaná rizika", + "Add the first risk": "Přidat první riziko", + "Medium": "Střední", + "Mitigating": "Zmírňuje se", + "Occurred": "Nastalo", + "Avoid": "Vyhnout se", + "Reduce": "Snížit", + "Transfer": "Přenést", + "Accept": "Přijmout", + "Highest open risks": "Nejvyšší otevřená rizika", + "No open risks": "Žádná otevřená rizika", + "Open the risk register": "Otevřít registr rizik", + "Edit risk": "Upravit riziko", + "What could go wrong": "Co se může pokazit", + "Likelihood": "Pravděpodobnost", + "Impact": "Dopad", + "Score {score}": "Skóre {score}", + "Countermeasures": "Protiopatření", + "Could not save the risk. Please try again.": "Riziko se nepodařilo uložit. Zkuste to znovu.", + "Risks by likelihood and impact": "Rizika podle pravděpodobnosti a dopadu", + "Risk scale": "Škála rizik", + "The levels every project scores its risks on, and where low turns into medium and high": "Úrovně, na kterých každý projekt hodnotí svá rizika, a kde nízké přechází ve střední a vysoké", + "Number of levels": "Počet úrovní", + "Likelihood labels": "Popisky pravděpodobnosti", + "Impact labels": "Popisky dopadu", + "Level {level}": "Úroveň {level}", + "Medium from score": "Střední od skóre", + "High from score": "Vysoké od skóre", + "Risk scale saved": "Škála rizik uložena", + "1 risk uses level {level}. Change it first.": "1 riziko používá úroveň {level}. Nejprve ho změňte.", + "{count} risks use level {level}. Change them first.": "{count} rizik používá úroveň {level}. Nejprve je změňte.", + "Every level needs a label, and high must start above medium.": "Každá úroveň potřebuje popisek a vysoké musí začínat nad středním.", + "The project this risk belongs to": "Projekt, ke kterému riziko patří", + "The cause and the effect, in Markdown": "Příčina a následek, v Markdownu", + "Category": "Kategorie", + "A free grouping, such as planning, budget or supplier": "Volné seskupení, např. plánování, rozpočet nebo dodavatel", + "How likely it is, on the admin's risk scale": "Jak je pravděpodobné, na škále rizik správce", + "How hard it would hit, on the admin's risk scale": "Jak tvrdě by zasáhlo, na škále rizik správce", + "Likelihood times impact, calculated by the server": "Pravděpodobnost krát dopad, spočítané serverem", + "Where the risk stands": "Jak na tom riziko je", + "The person who watches this risk": "Osoba, která riziko sleduje", + "How the project answers the risk": "Jak projekt na riziko odpovídá", + "What is done to prevent it or soften it, in Markdown": "Co se dělá pro jeho prevenci nebo zmírnění, v Markdownu", + "When to look at this risk again": "Kdy riziko znovu posoudit", + "Very low": "Velmi nízké", + "Very high": "Velmi vysoké", + "All projects": "Všechny projekty" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/cs.json b/l10n/cs.json index 8cc07f6f..8f6b6b05 100644 --- a/l10n/cs.json +++ b/l10n/cs.json @@ -308,7 +308,116 @@ "Board": "Nástěnka", "List": "Seznam", "Column": "Sloupec", - "Board or list": "Nástěnka nebo seznam" + "Board or list": "Nástěnka nebo seznam", + "Log": "Deník", + "Add entry": "Přidat záznam", + "Show": "Zobrazit", + "No actions yet": "Zatím žádné akce", + "Closed": "Uzavřeno", + "Entry actions": "Akce záznamu", + "Add action": "Přidat akci", + "by {author}": "autor {author}", + "saved {time}": "uloženo {time}", + "Attendees: {names}": "Účastníci: {names}", + "Add the first entry": "Přidat první záznam", + "Issues": "Problémy", + "Lessons learned": "Poučení", + "Meetings": "Schůzky", + "Decisions": "Rozhodnutí", + "Actions": "Akce", + "Issue": "Problém", + "Lesson learned": "Poučení", + "Meeting": "Schůzka", + "Decision": "Rozhodnutí", + "About this project": "O tomto projektu", + "No description yet": "Zatím bez popisu", + "Start date": "Datum zahájení", + "Not set": "Nenastaveno", + "End date": "Datum ukončení", + "Progress": "Postup", + "No tasks yet": "Zatím žádné úkoly", + "Open the board": "Otevřít nástěnku", + "People": "Lidé", + "Owner": "Vlastník", + "Latest log entries": "Nejnovější záznamy", + "Open the log": "Otevřít deník", + "{done} of {total} tasks done": "Hotovo {done} z {total} úkolů", + "Edit entry": "Upravit záznam", + "Type": "Druh", + "Attendees": "Účastníci", + "Issue is closed": "Problém je uzavřen", + "Could not save the entry. Please try again.": "Záznam se nepodařilo uložit. Zkuste to znovu.", + "From: {title}": "Z: {title}", + "What needs to happen": "Co je třeba udělat", + "Could not create the action. Please try again.": "Akci se nepodařilo vytvořit. Zkuste to znovu.", + "The action was created but could not be linked to the entry.": "Akce byla vytvořena, ale nepodařilo se ji propojit se záznamem.", + "Project pages": "Stránky projektu", + "Overview": "Přehled", + "Risks": "Rizika", + "Log entry": "Záznam deníku", + "A short name for the entry": "Krátký název záznamu", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, ke kterému záznam patří", + "What kind of entry this is": "Jaký je to druh záznamu", + "When it happened": "Kdy se to stalo", + "What happened, in Markdown": "Co se stalo, v Markdownu", + "Whether an issue is still open": "Zda je problém stále otevřený", + "The people at a meeting": "Lidé na schůzce", + "Tasks that came out of this entry": "Úkoly, které vzešly z tohoto záznamu", + "Add risk": "Přidat riziko", + "Risk": "Riziko", + "Score": "Skóre", + "Response": "Reakce", + "Review date": "Datum revize", + "no longer on the project": "už není v projektu", + "Edit {title}": "Upravit {title}", + "No risks recorded yet": "Zatím žádná zaznamenaná rizika", + "Add the first risk": "Přidat první riziko", + "Medium": "Střední", + "Mitigating": "Zmírňuje se", + "Occurred": "Nastalo", + "Avoid": "Vyhnout se", + "Reduce": "Snížit", + "Transfer": "Přenést", + "Accept": "Přijmout", + "Highest open risks": "Nejvyšší otevřená rizika", + "No open risks": "Žádná otevřená rizika", + "Open the risk register": "Otevřít registr rizik", + "Edit risk": "Upravit riziko", + "What could go wrong": "Co se může pokazit", + "Likelihood": "Pravděpodobnost", + "Impact": "Dopad", + "Score {score}": "Skóre {score}", + "Countermeasures": "Protiopatření", + "Could not save the risk. Please try again.": "Riziko se nepodařilo uložit. Zkuste to znovu.", + "Risks by likelihood and impact": "Rizika podle pravděpodobnosti a dopadu", + "Risk scale": "Škála rizik", + "The levels every project scores its risks on, and where low turns into medium and high": "Úrovně, na kterých každý projekt hodnotí svá rizika, a kde nízké přechází ve střední a vysoké", + "Number of levels": "Počet úrovní", + "Likelihood labels": "Popisky pravděpodobnosti", + "Impact labels": "Popisky dopadu", + "Level {level}": "Úroveň {level}", + "Medium from score": "Střední od skóre", + "High from score": "Vysoké od skóre", + "Risk scale saved": "Škála rizik uložena", + "1 risk uses level {level}. Change it first.": "1 riziko používá úroveň {level}. Nejprve ho změňte.", + "{count} risks use level {level}. Change them first.": "{count} rizik používá úroveň {level}. Nejprve je změňte.", + "Every level needs a label, and high must start above medium.": "Každá úroveň potřebuje popisek a vysoké musí začínat nad středním.", + "The project this risk belongs to": "Projekt, ke kterému riziko patří", + "The cause and the effect, in Markdown": "Příčina a následek, v Markdownu", + "Category": "Kategorie", + "A free grouping, such as planning, budget or supplier": "Volné seskupení, např. plánování, rozpočet nebo dodavatel", + "How likely it is, on the admin's risk scale": "Jak je pravděpodobné, na škále rizik správce", + "How hard it would hit, on the admin's risk scale": "Jak tvrdě by zasáhlo, na škále rizik správce", + "Likelihood times impact, calculated by the server": "Pravděpodobnost krát dopad, spočítané serverem", + "Where the risk stands": "Jak na tom riziko je", + "The person who watches this risk": "Osoba, která riziko sleduje", + "How the project answers the risk": "Jak projekt na riziko odpovídá", + "What is done to prevent it or soften it, in Markdown": "Co se dělá pro jeho prevenci nebo zmírnění, v Markdownu", + "When to look at this risk again": "Kdy riziko znovu posoudit", + "Very low": "Velmi nízké", + "Very high": "Velmi vysoké", + "All projects": "Všechny projekty" }, "plurals": null } diff --git a/l10n/da.js b/l10n/da.js index 3296ba99..765be95b 100644 --- a/l10n/da.js +++ b/l10n/da.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tavle", "List": "Liste", "Column": "Kolonne", - "Board or list": "Tavle eller liste" + "Board or list": "Tavle eller liste", + "Log": "Log", + "Add entry": "Tilføj post", + "Show": "Vis", + "No actions yet": "Ingen handlinger endnu", + "Closed": "Lukket", + "Entry actions": "Posthandlinger", + "Add action": "Tilføj handling", + "by {author}": "af {author}", + "saved {time}": "gemt {time}", + "Attendees: {names}": "Deltagere: {names}", + "Add the first entry": "Tilføj den første post", + "Issues": "Problemer", + "Lessons learned": "Erfaringer", + "Meetings": "Møder", + "Decisions": "Beslutninger", + "Actions": "Handlinger", + "Issue": "Problem", + "Lesson learned": "Erfaring", + "Meeting": "Møde", + "Decision": "Beslutning", + "About this project": "Om dette projekt", + "No description yet": "Ingen beskrivelse endnu", + "Start date": "Startdato", + "Not set": "Ikke angivet", + "End date": "Slutdato", + "Progress": "Fremdrift", + "No tasks yet": "Ingen opgaver endnu", + "Open the board": "Åbn tavlen", + "People": "Personer", + "Owner": "Ejer", + "Latest log entries": "Seneste poster", + "Open the log": "Åbn loggen", + "{done} of {total} tasks done": "{done} af {total} opgaver udført", + "Edit entry": "Rediger post", + "Type": "Type", + "Attendees": "Deltagere", + "Issue is closed": "Problemet er lukket", + "Could not save the entry. Please try again.": "Posten kunne ikke gemmes. Prøv igen.", + "From: {title}": "Fra: {title}", + "What needs to happen": "Hvad der skal ske", + "Could not create the action. Please try again.": "Handlingen kunne ikke oprettes. Prøv igen.", + "The action was created but could not be linked to the entry.": "Handlingen blev oprettet, men kunne ikke knyttes til posten.", + "Project pages": "Projektsider", + "Overview": "Oversigt", + "Risks": "Risici", + "Log entry": "Logpost", + "A short name for the entry": "Et kort navn til posten", + "Project": "Projekt", + "The project this entry belongs to": "Det projekt, posten hører til", + "What kind of entry this is": "Hvilken slags post dette er", + "When it happened": "Hvornår det skete", + "What happened, in Markdown": "Hvad der skete, i Markdown", + "Whether an issue is still open": "Om et problem stadig er åbent", + "The people at a meeting": "Personerne ved et møde", + "Tasks that came out of this entry": "Opgaver, der kom ud af denne post", + "Add risk": "Tilføj risiko", + "Risk": "Risiko", + "Score": "Score", + "Response": "Håndtering", + "Review date": "Revisionsdato", + "no longer on the project": "er ikke længere med i projektet", + "Edit {title}": "Rediger {title}", + "No risks recorded yet": "Ingen risici registreret endnu", + "Add the first risk": "Tilføj den første risiko", + "Medium": "Mellem", + "Mitigating": "Afbødes", + "Occurred": "Indtruffet", + "Avoid": "Undgå", + "Reduce": "Reducere", + "Transfer": "Overføre", + "Accept": "Acceptere", + "Highest open risks": "Højeste åbne risici", + "No open risks": "Ingen åbne risici", + "Open the risk register": "Åbn risikoregistret", + "Edit risk": "Rediger risiko", + "What could go wrong": "Hvad der kan gå galt", + "Likelihood": "Sandsynlighed", + "Impact": "Konsekvens", + "Score {score}": "Score {score}", + "Countermeasures": "Modforanstaltninger", + "Could not save the risk. Please try again.": "Risikoen kunne ikke gemmes. Prøv igen.", + "Risks by likelihood and impact": "Risici efter sandsynlighed og konsekvens", + "Risk scale": "Risikoskala", + "The levels every project scores its risks on, and where low turns into medium and high": "De niveauer, hvert projekt scorer sine risici på, og hvor lav går over i mellem og høj", + "Number of levels": "Antal niveauer", + "Likelihood labels": "Etiketter for sandsynlighed", + "Impact labels": "Etiketter for konsekvens", + "Level {level}": "Niveau {level}", + "Medium from score": "Mellem fra score", + "High from score": "Høj fra score", + "Risk scale saved": "Risikoskala gemt", + "1 risk uses level {level}. Change it first.": "1 risiko bruger niveau {level}. Ret den først.", + "{count} risks use level {level}. Change them first.": "{count} risici bruger niveau {level}. Ret dem først.", + "Every level needs a label, and high must start above medium.": "Hvert niveau skal have en etiket, og høj skal starte over mellem.", + "The project this risk belongs to": "Det projekt, risikoen hører til", + "The cause and the effect, in Markdown": "Årsag og virkning, i Markdown", + "Category": "Kategori", + "A free grouping, such as planning, budget or supplier": "En fri gruppering, fx planlægning, budget eller leverandør", + "How likely it is, on the admin's risk scale": "Hvor sandsynligt det er, på administratorens risikoskala", + "How hard it would hit, on the admin's risk scale": "Hvor hårdt det ville ramme, på administratorens risikoskala", + "Likelihood times impact, calculated by the server": "Sandsynlighed gange konsekvens, beregnet af serveren", + "Where the risk stands": "Hvor risikoen står", + "The person who watches this risk": "Den person, der holder øje med risikoen", + "How the project answers the risk": "Hvordan projektet håndterer risikoen", + "What is done to prevent it or soften it, in Markdown": "Hvad der gøres for at forhindre eller mildne den, i Markdown", + "When to look at this risk again": "Hvornår risikoen skal ses på igen", + "Very low": "Meget lav", + "Very high": "Meget høj", + "All projects": "Alle projekter" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/da.json b/l10n/da.json index 17e7e2ce..86e2aec5 100644 --- a/l10n/da.json +++ b/l10n/da.json @@ -308,7 +308,116 @@ "Board": "Tavle", "List": "Liste", "Column": "Kolonne", - "Board or list": "Tavle eller liste" + "Board or list": "Tavle eller liste", + "Log": "Log", + "Add entry": "Tilføj post", + "Show": "Vis", + "No actions yet": "Ingen handlinger endnu", + "Closed": "Lukket", + "Entry actions": "Posthandlinger", + "Add action": "Tilføj handling", + "by {author}": "af {author}", + "saved {time}": "gemt {time}", + "Attendees: {names}": "Deltagere: {names}", + "Add the first entry": "Tilføj den første post", + "Issues": "Problemer", + "Lessons learned": "Erfaringer", + "Meetings": "Møder", + "Decisions": "Beslutninger", + "Actions": "Handlinger", + "Issue": "Problem", + "Lesson learned": "Erfaring", + "Meeting": "Møde", + "Decision": "Beslutning", + "About this project": "Om dette projekt", + "No description yet": "Ingen beskrivelse endnu", + "Start date": "Startdato", + "Not set": "Ikke angivet", + "End date": "Slutdato", + "Progress": "Fremdrift", + "No tasks yet": "Ingen opgaver endnu", + "Open the board": "Åbn tavlen", + "People": "Personer", + "Owner": "Ejer", + "Latest log entries": "Seneste poster", + "Open the log": "Åbn loggen", + "{done} of {total} tasks done": "{done} af {total} opgaver udført", + "Edit entry": "Rediger post", + "Type": "Type", + "Attendees": "Deltagere", + "Issue is closed": "Problemet er lukket", + "Could not save the entry. Please try again.": "Posten kunne ikke gemmes. Prøv igen.", + "From: {title}": "Fra: {title}", + "What needs to happen": "Hvad der skal ske", + "Could not create the action. Please try again.": "Handlingen kunne ikke oprettes. Prøv igen.", + "The action was created but could not be linked to the entry.": "Handlingen blev oprettet, men kunne ikke knyttes til posten.", + "Project pages": "Projektsider", + "Overview": "Oversigt", + "Risks": "Risici", + "Log entry": "Logpost", + "A short name for the entry": "Et kort navn til posten", + "Project": "Projekt", + "The project this entry belongs to": "Det projekt, posten hører til", + "What kind of entry this is": "Hvilken slags post dette er", + "When it happened": "Hvornår det skete", + "What happened, in Markdown": "Hvad der skete, i Markdown", + "Whether an issue is still open": "Om et problem stadig er åbent", + "The people at a meeting": "Personerne ved et møde", + "Tasks that came out of this entry": "Opgaver, der kom ud af denne post", + "Add risk": "Tilføj risiko", + "Risk": "Risiko", + "Score": "Score", + "Response": "Håndtering", + "Review date": "Revisionsdato", + "no longer on the project": "er ikke længere med i projektet", + "Edit {title}": "Rediger {title}", + "No risks recorded yet": "Ingen risici registreret endnu", + "Add the first risk": "Tilføj den første risiko", + "Medium": "Mellem", + "Mitigating": "Afbødes", + "Occurred": "Indtruffet", + "Avoid": "Undgå", + "Reduce": "Reducere", + "Transfer": "Overføre", + "Accept": "Acceptere", + "Highest open risks": "Højeste åbne risici", + "No open risks": "Ingen åbne risici", + "Open the risk register": "Åbn risikoregistret", + "Edit risk": "Rediger risiko", + "What could go wrong": "Hvad der kan gå galt", + "Likelihood": "Sandsynlighed", + "Impact": "Konsekvens", + "Score {score}": "Score {score}", + "Countermeasures": "Modforanstaltninger", + "Could not save the risk. Please try again.": "Risikoen kunne ikke gemmes. Prøv igen.", + "Risks by likelihood and impact": "Risici efter sandsynlighed og konsekvens", + "Risk scale": "Risikoskala", + "The levels every project scores its risks on, and where low turns into medium and high": "De niveauer, hvert projekt scorer sine risici på, og hvor lav går over i mellem og høj", + "Number of levels": "Antal niveauer", + "Likelihood labels": "Etiketter for sandsynlighed", + "Impact labels": "Etiketter for konsekvens", + "Level {level}": "Niveau {level}", + "Medium from score": "Mellem fra score", + "High from score": "Høj fra score", + "Risk scale saved": "Risikoskala gemt", + "1 risk uses level {level}. Change it first.": "1 risiko bruger niveau {level}. Ret den først.", + "{count} risks use level {level}. Change them first.": "{count} risici bruger niveau {level}. Ret dem først.", + "Every level needs a label, and high must start above medium.": "Hvert niveau skal have en etiket, og høj skal starte over mellem.", + "The project this risk belongs to": "Det projekt, risikoen hører til", + "The cause and the effect, in Markdown": "Årsag og virkning, i Markdown", + "Category": "Kategori", + "A free grouping, such as planning, budget or supplier": "En fri gruppering, fx planlægning, budget eller leverandør", + "How likely it is, on the admin's risk scale": "Hvor sandsynligt det er, på administratorens risikoskala", + "How hard it would hit, on the admin's risk scale": "Hvor hårdt det ville ramme, på administratorens risikoskala", + "Likelihood times impact, calculated by the server": "Sandsynlighed gange konsekvens, beregnet af serveren", + "Where the risk stands": "Hvor risikoen står", + "The person who watches this risk": "Den person, der holder øje med risikoen", + "How the project answers the risk": "Hvordan projektet håndterer risikoen", + "What is done to prevent it or soften it, in Markdown": "Hvad der gøres for at forhindre eller mildne den, i Markdown", + "When to look at this risk again": "Hvornår risikoen skal ses på igen", + "Very low": "Meget lav", + "Very high": "Meget høj", + "All projects": "Alle projekter" }, "plurals": null } diff --git a/l10n/de.js b/l10n/de.js index 483c2838..2dc51e13 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Board", "List": "Liste", "Column": "Spalte", - "Board or list": "Board oder Liste" + "Board or list": "Board oder Liste", + "Log": "Protokoll", + "Add entry": "Eintrag hinzufügen", + "Show": "Anzeigen", + "No actions yet": "Noch keine Aktionen", + "Closed": "Geschlossen", + "Entry actions": "Eintragsaktionen", + "Add action": "Aktion hinzufügen", + "by {author}": "von {author}", + "saved {time}": "gespeichert {time}", + "Attendees: {names}": "Teilnehmende: {names}", + "Add the first entry": "Ersten Eintrag hinzufügen", + "Issues": "Probleme", + "Lessons learned": "Erkenntnisse", + "Meetings": "Besprechungen", + "Decisions": "Entscheidungen", + "Actions": "Aktionen", + "Issue": "Problem", + "Lesson learned": "Erkenntnis", + "Meeting": "Besprechung", + "Decision": "Entscheidung", + "About this project": "Über dieses Projekt", + "No description yet": "Noch keine Beschreibung", + "Start date": "Startdatum", + "Not set": "Nicht festgelegt", + "End date": "Enddatum", + "Progress": "Fortschritt", + "No tasks yet": "Noch keine Aufgaben", + "Open the board": "Board öffnen", + "People": "Personen", + "Owner": "Eigentümer", + "Latest log entries": "Neueste Einträge", + "Open the log": "Protokoll öffnen", + "{done} of {total} tasks done": "{done} von {total} Aufgaben erledigt", + "Edit entry": "Eintrag bearbeiten", + "Type": "Art", + "Attendees": "Teilnehmende", + "Issue is closed": "Problem ist geschlossen", + "Could not save the entry. Please try again.": "Der Eintrag konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.", + "From: {title}": "Aus: {title}", + "What needs to happen": "Was passieren muss", + "Could not create the action. Please try again.": "Die Aktion konnte nicht erstellt werden. Bitte versuchen Sie es erneut.", + "The action was created but could not be linked to the entry.": "Die Aktion wurde erstellt, konnte aber nicht mit dem Eintrag verknüpft werden.", + "Project pages": "Projektseiten", + "Overview": "Übersicht", + "Risks": "Risiken", + "Log entry": "Protokolleintrag", + "A short name for the entry": "Ein kurzer Name für den Eintrag", + "Project": "Projekt", + "The project this entry belongs to": "Das Projekt, zu dem dieser Eintrag gehört", + "What kind of entry this is": "Welche Art Eintrag dies ist", + "When it happened": "Wann es passiert ist", + "What happened, in Markdown": "Was passiert ist, in Markdown", + "Whether an issue is still open": "Ob ein Problem noch offen ist", + "The people at a meeting": "Die Personen bei einer Besprechung", + "Tasks that came out of this entry": "Aufgaben, die aus diesem Eintrag entstanden sind", + "Add risk": "Risiko hinzufügen", + "Risk": "Risiko", + "Score": "Wert", + "Response": "Reaktion", + "Review date": "Prüfdatum", + "no longer on the project": "nicht mehr im Projekt", + "Edit {title}": "{title} bearbeiten", + "No risks recorded yet": "Noch keine Risiken erfasst", + "Add the first risk": "Erstes Risiko hinzufügen", + "Medium": "Mittel", + "Mitigating": "Wird gemindert", + "Occurred": "Eingetreten", + "Avoid": "Vermeiden", + "Reduce": "Mindern", + "Transfer": "Übertragen", + "Accept": "Akzeptieren", + "Highest open risks": "Höchste offene Risiken", + "No open risks": "Keine offenen Risiken", + "Open the risk register": "Risikoregister öffnen", + "Edit risk": "Risiko bearbeiten", + "What could go wrong": "Was schiefgehen kann", + "Likelihood": "Wahrscheinlichkeit", + "Impact": "Auswirkung", + "Score {score}": "Wert {score}", + "Countermeasures": "Gegenmaßnahmen", + "Could not save the risk. Please try again.": "Das Risiko konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.", + "Risks by likelihood and impact": "Risiken nach Wahrscheinlichkeit und Auswirkung", + "Risk scale": "Risikoskala", + "The levels every project scores its risks on, and where low turns into medium and high": "Die Stufen, auf denen jedes Projekt seine Risiken bewertet, und wo niedrig in mittel und hoch übergeht", + "Number of levels": "Anzahl der Stufen", + "Likelihood labels": "Bezeichnungen für Wahrscheinlichkeit", + "Impact labels": "Bezeichnungen für Auswirkung", + "Level {level}": "Stufe {level}", + "Medium from score": "Mittel ab Wert", + "High from score": "Hoch ab Wert", + "Risk scale saved": "Risikoskala gespeichert", + "1 risk uses level {level}. Change it first.": "1 Risiko nutzt Stufe {level}. Ändern Sie es zuerst.", + "{count} risks use level {level}. Change them first.": "{count} Risiken nutzen Stufe {level}. Ändern Sie diese zuerst.", + "Every level needs a label, and high must start above medium.": "Jede Stufe braucht eine Bezeichnung, und hoch muss über mittel beginnen.", + "The project this risk belongs to": "Das Projekt, zu dem dieses Risiko gehört", + "The cause and the effect, in Markdown": "Ursache und Wirkung, in Markdown", + "Category": "Kategorie", + "A free grouping, such as planning, budget or supplier": "Eine freie Einteilung, etwa Planung, Budget oder Lieferant", + "How likely it is, on the admin's risk scale": "Wie wahrscheinlich es ist, auf der Risikoskala der Administration", + "How hard it would hit, on the admin's risk scale": "Wie hart es treffen würde, auf der Risikoskala der Administration", + "Likelihood times impact, calculated by the server": "Wahrscheinlichkeit mal Auswirkung, vom Server berechnet", + "Where the risk stands": "Wo das Risiko steht", + "The person who watches this risk": "Die Person, die dieses Risiko beobachtet", + "How the project answers the risk": "Wie das Projekt auf das Risiko reagiert", + "What is done to prevent it or soften it, in Markdown": "Was getan wird, um es zu verhindern oder abzuschwächen, in Markdown", + "When to look at this risk again": "Wann dieses Risiko erneut geprüft wird", + "Very low": "Sehr niedrig", + "Very high": "Sehr hoch", + "All projects": "Alle Projekte" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/de.json b/l10n/de.json index c814879c..e60dc9f5 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -308,7 +308,116 @@ "Board": "Board", "List": "Liste", "Column": "Spalte", - "Board or list": "Board oder Liste" + "Board or list": "Board oder Liste", + "Log": "Protokoll", + "Add entry": "Eintrag hinzufügen", + "Show": "Anzeigen", + "No actions yet": "Noch keine Aktionen", + "Closed": "Geschlossen", + "Entry actions": "Eintragsaktionen", + "Add action": "Aktion hinzufügen", + "by {author}": "von {author}", + "saved {time}": "gespeichert {time}", + "Attendees: {names}": "Teilnehmende: {names}", + "Add the first entry": "Ersten Eintrag hinzufügen", + "Issues": "Probleme", + "Lessons learned": "Erkenntnisse", + "Meetings": "Besprechungen", + "Decisions": "Entscheidungen", + "Actions": "Aktionen", + "Issue": "Problem", + "Lesson learned": "Erkenntnis", + "Meeting": "Besprechung", + "Decision": "Entscheidung", + "About this project": "Über dieses Projekt", + "No description yet": "Noch keine Beschreibung", + "Start date": "Startdatum", + "Not set": "Nicht festgelegt", + "End date": "Enddatum", + "Progress": "Fortschritt", + "No tasks yet": "Noch keine Aufgaben", + "Open the board": "Board öffnen", + "People": "Personen", + "Owner": "Eigentümer", + "Latest log entries": "Neueste Einträge", + "Open the log": "Protokoll öffnen", + "{done} of {total} tasks done": "{done} von {total} Aufgaben erledigt", + "Edit entry": "Eintrag bearbeiten", + "Type": "Art", + "Attendees": "Teilnehmende", + "Issue is closed": "Problem ist geschlossen", + "Could not save the entry. Please try again.": "Der Eintrag konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.", + "From: {title}": "Aus: {title}", + "What needs to happen": "Was passieren muss", + "Could not create the action. Please try again.": "Die Aktion konnte nicht erstellt werden. Bitte versuchen Sie es erneut.", + "The action was created but could not be linked to the entry.": "Die Aktion wurde erstellt, konnte aber nicht mit dem Eintrag verknüpft werden.", + "Project pages": "Projektseiten", + "Overview": "Übersicht", + "Risks": "Risiken", + "Log entry": "Protokolleintrag", + "A short name for the entry": "Ein kurzer Name für den Eintrag", + "Project": "Projekt", + "The project this entry belongs to": "Das Projekt, zu dem dieser Eintrag gehört", + "What kind of entry this is": "Welche Art Eintrag dies ist", + "When it happened": "Wann es passiert ist", + "What happened, in Markdown": "Was passiert ist, in Markdown", + "Whether an issue is still open": "Ob ein Problem noch offen ist", + "The people at a meeting": "Die Personen bei einer Besprechung", + "Tasks that came out of this entry": "Aufgaben, die aus diesem Eintrag entstanden sind", + "Add risk": "Risiko hinzufügen", + "Risk": "Risiko", + "Score": "Wert", + "Response": "Reaktion", + "Review date": "Prüfdatum", + "no longer on the project": "nicht mehr im Projekt", + "Edit {title}": "{title} bearbeiten", + "No risks recorded yet": "Noch keine Risiken erfasst", + "Add the first risk": "Erstes Risiko hinzufügen", + "Medium": "Mittel", + "Mitigating": "Wird gemindert", + "Occurred": "Eingetreten", + "Avoid": "Vermeiden", + "Reduce": "Mindern", + "Transfer": "Übertragen", + "Accept": "Akzeptieren", + "Highest open risks": "Höchste offene Risiken", + "No open risks": "Keine offenen Risiken", + "Open the risk register": "Risikoregister öffnen", + "Edit risk": "Risiko bearbeiten", + "What could go wrong": "Was schiefgehen kann", + "Likelihood": "Wahrscheinlichkeit", + "Impact": "Auswirkung", + "Score {score}": "Wert {score}", + "Countermeasures": "Gegenmaßnahmen", + "Could not save the risk. Please try again.": "Das Risiko konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.", + "Risks by likelihood and impact": "Risiken nach Wahrscheinlichkeit und Auswirkung", + "Risk scale": "Risikoskala", + "The levels every project scores its risks on, and where low turns into medium and high": "Die Stufen, auf denen jedes Projekt seine Risiken bewertet, und wo niedrig in mittel und hoch übergeht", + "Number of levels": "Anzahl der Stufen", + "Likelihood labels": "Bezeichnungen für Wahrscheinlichkeit", + "Impact labels": "Bezeichnungen für Auswirkung", + "Level {level}": "Stufe {level}", + "Medium from score": "Mittel ab Wert", + "High from score": "Hoch ab Wert", + "Risk scale saved": "Risikoskala gespeichert", + "1 risk uses level {level}. Change it first.": "1 Risiko nutzt Stufe {level}. Ändern Sie es zuerst.", + "{count} risks use level {level}. Change them first.": "{count} Risiken nutzen Stufe {level}. Ändern Sie diese zuerst.", + "Every level needs a label, and high must start above medium.": "Jede Stufe braucht eine Bezeichnung, und hoch muss über mittel beginnen.", + "The project this risk belongs to": "Das Projekt, zu dem dieses Risiko gehört", + "The cause and the effect, in Markdown": "Ursache und Wirkung, in Markdown", + "Category": "Kategorie", + "A free grouping, such as planning, budget or supplier": "Eine freie Einteilung, etwa Planung, Budget oder Lieferant", + "How likely it is, on the admin's risk scale": "Wie wahrscheinlich es ist, auf der Risikoskala der Administration", + "How hard it would hit, on the admin's risk scale": "Wie hart es treffen würde, auf der Risikoskala der Administration", + "Likelihood times impact, calculated by the server": "Wahrscheinlichkeit mal Auswirkung, vom Server berechnet", + "Where the risk stands": "Wo das Risiko steht", + "The person who watches this risk": "Die Person, die dieses Risiko beobachtet", + "How the project answers the risk": "Wie das Projekt auf das Risiko reagiert", + "What is done to prevent it or soften it, in Markdown": "Was getan wird, um es zu verhindern oder abzuschwächen, in Markdown", + "When to look at this risk again": "Wann dieses Risiko erneut geprüft wird", + "Very low": "Sehr niedrig", + "Very high": "Sehr hoch", + "All projects": "Alle Projekte" }, "plurals": null } diff --git a/l10n/el.js b/l10n/el.js index e9ec65ca..863205c6 100644 --- a/l10n/el.js +++ b/l10n/el.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Πίνακας", "List": "Λίστα", "Column": "Στήλη", - "Board or list": "Πίνακας ή λίστα" + "Board or list": "Πίνακας ή λίστα", + "Log": "Ημερολόγιο", + "Add entry": "Προσθήκη καταχώρισης", + "Show": "Εμφάνιση", + "No actions yet": "Δεν υπάρχουν ακόμη ενέργειες", + "Closed": "Κλειστό", + "Entry actions": "Ενέργειες καταχώρισης", + "Add action": "Προσθήκη ενέργειας", + "by {author}": "από {author}", + "saved {time}": "αποθηκεύτηκε {time}", + "Attendees: {names}": "Συμμετέχοντες: {names}", + "Add the first entry": "Προσθήκη της πρώτης καταχώρισης", + "Issues": "Ζητήματα", + "Lessons learned": "Διδάγματα", + "Meetings": "Συσκέψεις", + "Decisions": "Αποφάσεις", + "Actions": "Ενέργειες", + "Issue": "Ζήτημα", + "Lesson learned": "Δίδαγμα", + "Meeting": "Σύσκεψη", + "Decision": "Απόφαση", + "About this project": "Σχετικά με το έργο", + "No description yet": "Δεν υπάρχει ακόμη περιγραφή", + "Start date": "Ημερομηνία έναρξης", + "Not set": "Δεν έχει οριστεί", + "End date": "Ημερομηνία λήξης", + "Progress": "Πρόοδος", + "No tasks yet": "Δεν υπάρχουν ακόμη εργασίες", + "Open the board": "Άνοιγμα πίνακα", + "People": "Άτομα", + "Owner": "Κάτοχος", + "Latest log entries": "Πιο πρόσφατες καταχωρίσεις", + "Open the log": "Άνοιγμα ημερολογίου", + "{done} of {total} tasks done": "{done} από {total} εργασίες ολοκληρώθηκαν", + "Edit entry": "Επεξεργασία καταχώρισης", + "Type": "Είδος", + "Attendees": "Συμμετέχοντες", + "Issue is closed": "Το ζήτημα έκλεισε", + "Could not save the entry. Please try again.": "Δεν ήταν δυνατή η αποθήκευση της καταχώρισης. Δοκιμάστε ξανά.", + "From: {title}": "Από: {title}", + "What needs to happen": "Τι πρέπει να γίνει", + "Could not create the action. Please try again.": "Δεν ήταν δυνατή η δημιουργία της ενέργειας. Δοκιμάστε ξανά.", + "The action was created but could not be linked to the entry.": "Η ενέργεια δημιουργήθηκε, αλλά δεν ήταν δυνατή η σύνδεσή της με την καταχώριση.", + "Project pages": "Σελίδες έργου", + "Overview": "Επισκόπηση", + "Risks": "Κίνδυνοι", + "Log entry": "Καταχώριση ημερολογίου", + "A short name for the entry": "Ένα σύντομο όνομα για την καταχώριση", + "Project": "Έργο", + "The project this entry belongs to": "Το έργο στο οποίο ανήκει η καταχώριση", + "What kind of entry this is": "Τι είδους καταχώριση είναι", + "When it happened": "Πότε συνέβη", + "What happened, in Markdown": "Τι συνέβη, σε Markdown", + "Whether an issue is still open": "Αν ένα ζήτημα είναι ακόμη ανοιχτό", + "The people at a meeting": "Τα άτομα σε μια σύσκεψη", + "Tasks that came out of this entry": "Εργασίες που προέκυψαν από αυτή την καταχώριση", + "Add risk": "Προσθήκη κινδύνου", + "Risk": "Κίνδυνος", + "Score": "Βαθμολογία", + "Response": "Αντιμετώπιση", + "Review date": "Ημερομηνία επανεξέτασης", + "no longer on the project": "δεν είναι πλέον στο έργο", + "Edit {title}": "Επεξεργασία {title}", + "No risks recorded yet": "Δεν έχουν καταγραφεί ακόμη κίνδυνοι", + "Add the first risk": "Προσθήκη του πρώτου κινδύνου", + "Medium": "Μεσαίος", + "Mitigating": "Σε μετριασμό", + "Occurred": "Επήλθε", + "Avoid": "Αποφυγή", + "Reduce": "Μείωση", + "Transfer": "Μεταφορά", + "Accept": "Αποδοχή", + "Highest open risks": "Υψηλότεροι ανοιχτοί κίνδυνοι", + "No open risks": "Δεν υπάρχουν ανοιχτοί κίνδυνοι", + "Open the risk register": "Άνοιγμα μητρώου κινδύνων", + "Edit risk": "Επεξεργασία κινδύνου", + "What could go wrong": "Τι μπορεί να πάει στραβά", + "Likelihood": "Πιθανότητα", + "Impact": "Επίπτωση", + "Score {score}": "Βαθμολογία {score}", + "Countermeasures": "Αντίμετρα", + "Could not save the risk. Please try again.": "Δεν ήταν δυνατή η αποθήκευση του κινδύνου. Δοκιμάστε ξανά.", + "Risks by likelihood and impact": "Κίνδυνοι ανά πιθανότητα και επίπτωση", + "Risk scale": "Κλίμακα κινδύνου", + "The levels every project scores its risks on, and where low turns into medium and high": "Τα επίπεδα στα οποία κάθε έργο βαθμολογεί τους κινδύνους του, και πού το χαμηλό γίνεται μεσαίο και υψηλό", + "Number of levels": "Αριθμός επιπέδων", + "Likelihood labels": "Ετικέτες πιθανότητας", + "Impact labels": "Ετικέτες επίπτωσης", + "Level {level}": "Επίπεδο {level}", + "Medium from score": "Μεσαίο από βαθμολογία", + "High from score": "Υψηλό από βαθμολογία", + "Risk scale saved": "Η κλίμακα κινδύνου αποθηκεύτηκε", + "1 risk uses level {level}. Change it first.": "1 κίνδυνος χρησιμοποιεί το επίπεδο {level}. Αλλάξτε τον πρώτα.", + "{count} risks use level {level}. Change them first.": "{count} κίνδυνοι χρησιμοποιούν το επίπεδο {level}. Αλλάξτε τους πρώτα.", + "Every level needs a label, and high must start above medium.": "Κάθε επίπεδο χρειάζεται ετικέτα, και το υψηλό πρέπει να ξεκινά πάνω από το μεσαίο.", + "The project this risk belongs to": "Το έργο στο οποίο ανήκει ο κίνδυνος", + "The cause and the effect, in Markdown": "Η αιτία και το αποτέλεσμα, σε Markdown", + "Category": "Κατηγορία", + "A free grouping, such as planning, budget or supplier": "Ελεύθερη ομαδοποίηση, όπως χρονοπρογραμματισμός, προϋπολογισμός ή προμηθευτής", + "How likely it is, on the admin's risk scale": "Πόσο πιθανό είναι, στην κλίμακα κινδύνου του διαχειριστή", + "How hard it would hit, on the admin's risk scale": "Πόσο σκληρά θα έπληττε, στην κλίμακα κινδύνου του διαχειριστή", + "Likelihood times impact, calculated by the server": "Πιθανότητα επί επίπτωση, υπολογισμένη από τον διακομιστή", + "Where the risk stands": "Πού βρίσκεται ο κίνδυνος", + "The person who watches this risk": "Το άτομο που παρακολουθεί τον κίνδυνο", + "How the project answers the risk": "Πώς απαντά το έργο στον κίνδυνο", + "What is done to prevent it or soften it, in Markdown": "Τι γίνεται για να αποτραπεί ή να μετριαστεί, σε Markdown", + "When to look at this risk again": "Πότε να επανεξεταστεί ο κίνδυνος", + "Very low": "Πολύ χαμηλό", + "Very high": "Πολύ υψηλό", + "All projects": "Όλα τα έργα" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/el.json b/l10n/el.json index e1d3c2eb..1f96287b 100644 --- a/l10n/el.json +++ b/l10n/el.json @@ -308,7 +308,116 @@ "Board": "Πίνακας", "List": "Λίστα", "Column": "Στήλη", - "Board or list": "Πίνακας ή λίστα" + "Board or list": "Πίνακας ή λίστα", + "Log": "Ημερολόγιο", + "Add entry": "Προσθήκη καταχώρισης", + "Show": "Εμφάνιση", + "No actions yet": "Δεν υπάρχουν ακόμη ενέργειες", + "Closed": "Κλειστό", + "Entry actions": "Ενέργειες καταχώρισης", + "Add action": "Προσθήκη ενέργειας", + "by {author}": "από {author}", + "saved {time}": "αποθηκεύτηκε {time}", + "Attendees: {names}": "Συμμετέχοντες: {names}", + "Add the first entry": "Προσθήκη της πρώτης καταχώρισης", + "Issues": "Ζητήματα", + "Lessons learned": "Διδάγματα", + "Meetings": "Συσκέψεις", + "Decisions": "Αποφάσεις", + "Actions": "Ενέργειες", + "Issue": "Ζήτημα", + "Lesson learned": "Δίδαγμα", + "Meeting": "Σύσκεψη", + "Decision": "Απόφαση", + "About this project": "Σχετικά με το έργο", + "No description yet": "Δεν υπάρχει ακόμη περιγραφή", + "Start date": "Ημερομηνία έναρξης", + "Not set": "Δεν έχει οριστεί", + "End date": "Ημερομηνία λήξης", + "Progress": "Πρόοδος", + "No tasks yet": "Δεν υπάρχουν ακόμη εργασίες", + "Open the board": "Άνοιγμα πίνακα", + "People": "Άτομα", + "Owner": "Κάτοχος", + "Latest log entries": "Πιο πρόσφατες καταχωρίσεις", + "Open the log": "Άνοιγμα ημερολογίου", + "{done} of {total} tasks done": "{done} από {total} εργασίες ολοκληρώθηκαν", + "Edit entry": "Επεξεργασία καταχώρισης", + "Type": "Είδος", + "Attendees": "Συμμετέχοντες", + "Issue is closed": "Το ζήτημα έκλεισε", + "Could not save the entry. Please try again.": "Δεν ήταν δυνατή η αποθήκευση της καταχώρισης. Δοκιμάστε ξανά.", + "From: {title}": "Από: {title}", + "What needs to happen": "Τι πρέπει να γίνει", + "Could not create the action. Please try again.": "Δεν ήταν δυνατή η δημιουργία της ενέργειας. Δοκιμάστε ξανά.", + "The action was created but could not be linked to the entry.": "Η ενέργεια δημιουργήθηκε, αλλά δεν ήταν δυνατή η σύνδεσή της με την καταχώριση.", + "Project pages": "Σελίδες έργου", + "Overview": "Επισκόπηση", + "Risks": "Κίνδυνοι", + "Log entry": "Καταχώριση ημερολογίου", + "A short name for the entry": "Ένα σύντομο όνομα για την καταχώριση", + "Project": "Έργο", + "The project this entry belongs to": "Το έργο στο οποίο ανήκει η καταχώριση", + "What kind of entry this is": "Τι είδους καταχώριση είναι", + "When it happened": "Πότε συνέβη", + "What happened, in Markdown": "Τι συνέβη, σε Markdown", + "Whether an issue is still open": "Αν ένα ζήτημα είναι ακόμη ανοιχτό", + "The people at a meeting": "Τα άτομα σε μια σύσκεψη", + "Tasks that came out of this entry": "Εργασίες που προέκυψαν από αυτή την καταχώριση", + "Add risk": "Προσθήκη κινδύνου", + "Risk": "Κίνδυνος", + "Score": "Βαθμολογία", + "Response": "Αντιμετώπιση", + "Review date": "Ημερομηνία επανεξέτασης", + "no longer on the project": "δεν είναι πλέον στο έργο", + "Edit {title}": "Επεξεργασία {title}", + "No risks recorded yet": "Δεν έχουν καταγραφεί ακόμη κίνδυνοι", + "Add the first risk": "Προσθήκη του πρώτου κινδύνου", + "Medium": "Μεσαίος", + "Mitigating": "Σε μετριασμό", + "Occurred": "Επήλθε", + "Avoid": "Αποφυγή", + "Reduce": "Μείωση", + "Transfer": "Μεταφορά", + "Accept": "Αποδοχή", + "Highest open risks": "Υψηλότεροι ανοιχτοί κίνδυνοι", + "No open risks": "Δεν υπάρχουν ανοιχτοί κίνδυνοι", + "Open the risk register": "Άνοιγμα μητρώου κινδύνων", + "Edit risk": "Επεξεργασία κινδύνου", + "What could go wrong": "Τι μπορεί να πάει στραβά", + "Likelihood": "Πιθανότητα", + "Impact": "Επίπτωση", + "Score {score}": "Βαθμολογία {score}", + "Countermeasures": "Αντίμετρα", + "Could not save the risk. Please try again.": "Δεν ήταν δυνατή η αποθήκευση του κινδύνου. Δοκιμάστε ξανά.", + "Risks by likelihood and impact": "Κίνδυνοι ανά πιθανότητα και επίπτωση", + "Risk scale": "Κλίμακα κινδύνου", + "The levels every project scores its risks on, and where low turns into medium and high": "Τα επίπεδα στα οποία κάθε έργο βαθμολογεί τους κινδύνους του, και πού το χαμηλό γίνεται μεσαίο και υψηλό", + "Number of levels": "Αριθμός επιπέδων", + "Likelihood labels": "Ετικέτες πιθανότητας", + "Impact labels": "Ετικέτες επίπτωσης", + "Level {level}": "Επίπεδο {level}", + "Medium from score": "Μεσαίο από βαθμολογία", + "High from score": "Υψηλό από βαθμολογία", + "Risk scale saved": "Η κλίμακα κινδύνου αποθηκεύτηκε", + "1 risk uses level {level}. Change it first.": "1 κίνδυνος χρησιμοποιεί το επίπεδο {level}. Αλλάξτε τον πρώτα.", + "{count} risks use level {level}. Change them first.": "{count} κίνδυνοι χρησιμοποιούν το επίπεδο {level}. Αλλάξτε τους πρώτα.", + "Every level needs a label, and high must start above medium.": "Κάθε επίπεδο χρειάζεται ετικέτα, και το υψηλό πρέπει να ξεκινά πάνω από το μεσαίο.", + "The project this risk belongs to": "Το έργο στο οποίο ανήκει ο κίνδυνος", + "The cause and the effect, in Markdown": "Η αιτία και το αποτέλεσμα, σε Markdown", + "Category": "Κατηγορία", + "A free grouping, such as planning, budget or supplier": "Ελεύθερη ομαδοποίηση, όπως χρονοπρογραμματισμός, προϋπολογισμός ή προμηθευτής", + "How likely it is, on the admin's risk scale": "Πόσο πιθανό είναι, στην κλίμακα κινδύνου του διαχειριστή", + "How hard it would hit, on the admin's risk scale": "Πόσο σκληρά θα έπληττε, στην κλίμακα κινδύνου του διαχειριστή", + "Likelihood times impact, calculated by the server": "Πιθανότητα επί επίπτωση, υπολογισμένη από τον διακομιστή", + "Where the risk stands": "Πού βρίσκεται ο κίνδυνος", + "The person who watches this risk": "Το άτομο που παρακολουθεί τον κίνδυνο", + "How the project answers the risk": "Πώς απαντά το έργο στον κίνδυνο", + "What is done to prevent it or soften it, in Markdown": "Τι γίνεται για να αποτραπεί ή να μετριαστεί, σε Markdown", + "When to look at this risk again": "Πότε να επανεξεταστεί ο κίνδυνος", + "Very low": "Πολύ χαμηλό", + "Very high": "Πολύ υψηλό", + "All projects": "Όλα τα έργα" }, "plurals": null } diff --git a/l10n/en.js b/l10n/en.js index 9b92fa0b..8daa3893 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -283,7 +283,118 @@ OC.L10N.register( "Board": "Board", "List": "List", "Column": "Column", - "Board or list": "Board or list" + "Board or list": "Board or list", + "Log": "Log", + "Add entry": "Add entry", + "Show": "Show", + "No actions yet": "No actions yet", + "Closed": "Closed", + "Entry actions": "Entry actions", + "Add action": "Add action", + "by {author}": "by {author}", + "saved {time}": "saved {time}", + "Attendees: {names}": "Attendees: {names}", + "Nothing logged yet": "Nothing logged yet", + "Add the first entry": "Add the first entry", + "Issues": "Issues", + "Lessons learned": "Lessons learned", + "Meetings": "Meetings", + "Decisions": "Decisions", + "Actions": "Actions", + "Issue": "Issue", + "Lesson learned": "Lesson learned", + "Meeting": "Meeting", + "Decision": "Decision", + "About this project": "About this project", + "No description yet": "No description yet", + "Start date": "Start date", + "Not set": "Not set", + "End date": "End date", + "Progress": "Progress", + "No tasks yet": "No tasks yet", + "Open the board": "Open the board", + "People": "People", + "Owner": "Owner", + "Latest log entries": "Latest log entries", + "Open the log": "Open the log", + "{done} of {total} tasks done": "{done} of {total} tasks done", + "Edit entry": "Edit entry", + "Type": "Type", + "Date": "Date", + "Attendees": "Attendees", + "Issue is closed": "Issue is closed", + "Could not save the entry. Please try again.": "Could not save the entry. Please try again.", + "From: {title}": "From: {title}", + "What needs to happen": "What needs to happen", + "Could not create the action. Please try again.": "Could not create the action. Please try again.", + "The action was created but could not be linked to the entry.": "The action was created but could not be linked to the entry.", + "Project pages": "Project pages", + "Overview": "Overview", + "Risks": "Risks", + "Log entry": "Log entry", + "A short name for the entry": "A short name for the entry", + "Project": "Project", + "The project this entry belongs to": "The project this entry belongs to", + "What kind of entry this is": "What kind of entry this is", + "When it happened": "When it happened", + "What happened, in Markdown": "What happened, in Markdown", + "Whether an issue is still open": "Whether an issue is still open", + "The people at a meeting": "The people at a meeting", + "Tasks that came out of this entry": "Tasks that came out of this entry", + "Add risk": "Add risk", + "Risk": "Risk", + "Score": "Score", + "Response": "Response", + "Review date": "Review date", + "no longer on the project": "no longer on the project", + "Edit {title}": "Edit {title}", + "No risks recorded yet": "No risks recorded yet", + "Add the first risk": "Add the first risk", + "Medium": "Medium", + "Mitigating": "Mitigating", + "Occurred": "Occurred", + "Avoid": "Avoid", + "Reduce": "Reduce", + "Transfer": "Transfer", + "Accept": "Accept", + "Highest open risks": "Highest open risks", + "No open risks": "No open risks", + "Open the risk register": "Open the risk register", + "Edit risk": "Edit risk", + "What could go wrong": "What could go wrong", + "Likelihood": "Likelihood", + "Impact": "Impact", + "Score {score}": "Score {score}", + "Countermeasures": "Countermeasures", + "Could not save the risk. Please try again.": "Could not save the risk. Please try again.", + "Risks by likelihood and impact": "Risks by likelihood and impact", + "Risk scale": "Risk scale", + "The levels every project scores its risks on, and where low turns into medium and high": "The levels every project scores its risks on, and where low turns into medium and high", + "Number of levels": "Number of levels", + "Likelihood labels": "Likelihood labels", + "Impact labels": "Impact labels", + "Level {level}": "Level {level}", + "Medium from score": "Medium from score", + "High from score": "High from score", + "Risk scale saved": "Risk scale saved", + "1 risk uses level {level}. Change it first.": "1 risk uses level {level}. Change it first.", + "{count} risks use level {level}. Change them first.": "{count} risks use level {level}. Change them first.", + "Every level needs a label, and high must start above medium.": "Every level needs a label, and high must start above medium.", + "The project this risk belongs to": "The project this risk belongs to", + "The cause and the effect, in Markdown": "The cause and the effect, in Markdown", + "Category": "Category", + "A free grouping, such as planning, budget or supplier": "A free grouping, such as planning, budget or supplier", + "How likely it is, on the admin's risk scale": "How likely it is, on the admin's risk scale", + "How hard it would hit, on the admin's risk scale": "How hard it would hit, on the admin's risk scale", + "Likelihood times impact, calculated by the server": "Likelihood times impact, calculated by the server", + "Where the risk stands": "Where the risk stands", + "The person who watches this risk": "The person who watches this risk", + "How the project answers the risk": "How the project answers the risk", + "What is done to prevent it or soften it, in Markdown": "What is done to prevent it or soften it, in Markdown", + "When to look at this risk again": "When to look at this risk again", + "Very low": "Very low", + "Very high": "Very high", + "All projects": "All projects" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/en.json b/l10n/en.json index 8288dea4..db88c8d4 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -282,7 +282,118 @@ "Board": "Board", "List": "List", "Column": "Column", - "Board or list": "Board or list" + "Board or list": "Board or list", + "Log": "Log", + "Add entry": "Add entry", + "Show": "Show", + "No actions yet": "No actions yet", + "Closed": "Closed", + "Entry actions": "Entry actions", + "Add action": "Add action", + "by {author}": "by {author}", + "saved {time}": "saved {time}", + "Attendees: {names}": "Attendees: {names}", + "Nothing logged yet": "Nothing logged yet", + "Add the first entry": "Add the first entry", + "Issues": "Issues", + "Lessons learned": "Lessons learned", + "Meetings": "Meetings", + "Decisions": "Decisions", + "Actions": "Actions", + "Issue": "Issue", + "Lesson learned": "Lesson learned", + "Meeting": "Meeting", + "Decision": "Decision", + "About this project": "About this project", + "No description yet": "No description yet", + "Start date": "Start date", + "Not set": "Not set", + "End date": "End date", + "Progress": "Progress", + "No tasks yet": "No tasks yet", + "Open the board": "Open the board", + "People": "People", + "Owner": "Owner", + "Latest log entries": "Latest log entries", + "Open the log": "Open the log", + "{done} of {total} tasks done": "{done} of {total} tasks done", + "Edit entry": "Edit entry", + "Type": "Type", + "Date": "Date", + "Attendees": "Attendees", + "Issue is closed": "Issue is closed", + "Could not save the entry. Please try again.": "Could not save the entry. Please try again.", + "From: {title}": "From: {title}", + "What needs to happen": "What needs to happen", + "Could not create the action. Please try again.": "Could not create the action. Please try again.", + "The action was created but could not be linked to the entry.": "The action was created but could not be linked to the entry.", + "Project pages": "Project pages", + "Overview": "Overview", + "Risks": "Risks", + "Log entry": "Log entry", + "A short name for the entry": "A short name for the entry", + "Project": "Project", + "The project this entry belongs to": "The project this entry belongs to", + "What kind of entry this is": "What kind of entry this is", + "When it happened": "When it happened", + "What happened, in Markdown": "What happened, in Markdown", + "Whether an issue is still open": "Whether an issue is still open", + "The people at a meeting": "The people at a meeting", + "Tasks that came out of this entry": "Tasks that came out of this entry", + "Add risk": "Add risk", + "Risk": "Risk", + "Score": "Score", + "Response": "Response", + "Review date": "Review date", + "no longer on the project": "no longer on the project", + "Edit {title}": "Edit {title}", + "No risks recorded yet": "No risks recorded yet", + "Add the first risk": "Add the first risk", + "Medium": "Medium", + "Mitigating": "Mitigating", + "Occurred": "Occurred", + "Avoid": "Avoid", + "Reduce": "Reduce", + "Transfer": "Transfer", + "Accept": "Accept", + "Highest open risks": "Highest open risks", + "No open risks": "No open risks", + "Open the risk register": "Open the risk register", + "Edit risk": "Edit risk", + "What could go wrong": "What could go wrong", + "Likelihood": "Likelihood", + "Impact": "Impact", + "Score {score}": "Score {score}", + "Countermeasures": "Countermeasures", + "Could not save the risk. Please try again.": "Could not save the risk. Please try again.", + "Risks by likelihood and impact": "Risks by likelihood and impact", + "Risk scale": "Risk scale", + "The levels every project scores its risks on, and where low turns into medium and high": "The levels every project scores its risks on, and where low turns into medium and high", + "Number of levels": "Number of levels", + "Likelihood labels": "Likelihood labels", + "Impact labels": "Impact labels", + "Level {level}": "Level {level}", + "Medium from score": "Medium from score", + "High from score": "High from score", + "Risk scale saved": "Risk scale saved", + "1 risk uses level {level}. Change it first.": "1 risk uses level {level}. Change it first.", + "{count} risks use level {level}. Change them first.": "{count} risks use level {level}. Change them first.", + "Every level needs a label, and high must start above medium.": "Every level needs a label, and high must start above medium.", + "The project this risk belongs to": "The project this risk belongs to", + "The cause and the effect, in Markdown": "The cause and the effect, in Markdown", + "Category": "Category", + "A free grouping, such as planning, budget or supplier": "A free grouping, such as planning, budget or supplier", + "How likely it is, on the admin's risk scale": "How likely it is, on the admin's risk scale", + "How hard it would hit, on the admin's risk scale": "How hard it would hit, on the admin's risk scale", + "Likelihood times impact, calculated by the server": "Likelihood times impact, calculated by the server", + "Where the risk stands": "Where the risk stands", + "The person who watches this risk": "The person who watches this risk", + "How the project answers the risk": "How the project answers the risk", + "What is done to prevent it or soften it, in Markdown": "What is done to prevent it or soften it, in Markdown", + "When to look at this risk again": "When to look at this risk again", + "Very low": "Very low", + "Very high": "Very high", + "All projects": "All projects" }, "plurals": {}, "pluralForm": "nplurals=2; plural=(n != 1);" diff --git a/l10n/es.js b/l10n/es.js index 6275bb93..1cf071e7 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tablero", "List": "Lista", "Column": "Columna", - "Board or list": "Tablero o lista" + "Board or list": "Tablero o lista", + "Log": "Registro", + "Add entry": "Añadir entrada", + "Show": "Mostrar", + "No actions yet": "Aún no hay acciones", + "Closed": "Cerrado", + "Entry actions": "Acciones de la entrada", + "Add action": "Añadir acción", + "by {author}": "por {author}", + "saved {time}": "guardado {time}", + "Attendees: {names}": "Asistentes: {names}", + "Add the first entry": "Añadir la primera entrada", + "Issues": "Incidencias", + "Lessons learned": "Lecciones aprendidas", + "Meetings": "Reuniones", + "Decisions": "Decisiones", + "Actions": "Acciones", + "Issue": "Incidencia", + "Lesson learned": "Lección aprendida", + "Meeting": "Reunión", + "Decision": "Decisión", + "About this project": "Acerca de este proyecto", + "No description yet": "Aún no hay descripción", + "Start date": "Fecha de inicio", + "Not set": "Sin definir", + "End date": "Fecha de fin", + "Progress": "Progreso", + "No tasks yet": "Aún no hay tareas", + "Open the board": "Abrir el tablero", + "People": "Personas", + "Owner": "Propietario", + "Latest log entries": "Últimas entradas", + "Open the log": "Abrir el registro", + "{done} of {total} tasks done": "{done} de {total} tareas hechas", + "Edit entry": "Editar entrada", + "Type": "Tipo", + "Attendees": "Asistentes", + "Issue is closed": "La incidencia está cerrada", + "Could not save the entry. Please try again.": "No se pudo guardar la entrada. Inténtelo de nuevo.", + "From: {title}": "De: {title}", + "What needs to happen": "Qué hay que hacer", + "Could not create the action. Please try again.": "No se pudo crear la acción. Inténtelo de nuevo.", + "The action was created but could not be linked to the entry.": "La acción se creó, pero no se pudo vincular a la entrada.", + "Project pages": "Páginas del proyecto", + "Overview": "Resumen", + "Risks": "Riesgos", + "Log entry": "Entrada de registro", + "A short name for the entry": "Un nombre corto para la entrada", + "Project": "Proyecto", + "The project this entry belongs to": "El proyecto al que pertenece esta entrada", + "What kind of entry this is": "Qué tipo de entrada es", + "When it happened": "Cuándo ocurrió", + "What happened, in Markdown": "Qué ocurrió, en Markdown", + "Whether an issue is still open": "Si una incidencia sigue abierta", + "The people at a meeting": "Las personas en una reunión", + "Tasks that came out of this entry": "Tareas surgidas de esta entrada", + "Add risk": "Añadir riesgo", + "Risk": "Riesgo", + "Score": "Puntuación", + "Response": "Respuesta", + "Review date": "Fecha de revisión", + "no longer on the project": "ya no está en el proyecto", + "Edit {title}": "Editar {title}", + "No risks recorded yet": "Aún no hay riesgos registrados", + "Add the first risk": "Añadir el primer riesgo", + "Medium": "Medio", + "Mitigating": "En mitigación", + "Occurred": "Ocurrido", + "Avoid": "Evitar", + "Reduce": "Reducir", + "Transfer": "Transferir", + "Accept": "Aceptar", + "Highest open risks": "Riesgos abiertos más altos", + "No open risks": "No hay riesgos abiertos", + "Open the risk register": "Abrir el registro de riesgos", + "Edit risk": "Editar riesgo", + "What could go wrong": "Qué podría salir mal", + "Likelihood": "Probabilidad", + "Impact": "Impacto", + "Score {score}": "Puntuación {score}", + "Countermeasures": "Contramedidas", + "Could not save the risk. Please try again.": "No se pudo guardar el riesgo. Inténtelo de nuevo.", + "Risks by likelihood and impact": "Riesgos por probabilidad e impacto", + "Risk scale": "Escala de riesgo", + "The levels every project scores its risks on, and where low turns into medium and high": "Los niveles en los que cada proyecto puntúa sus riesgos, y dónde bajo pasa a medio y alto", + "Number of levels": "Número de niveles", + "Likelihood labels": "Etiquetas de probabilidad", + "Impact labels": "Etiquetas de impacto", + "Level {level}": "Nivel {level}", + "Medium from score": "Medio a partir de la puntuación", + "High from score": "Alto a partir de la puntuación", + "Risk scale saved": "Escala de riesgo guardada", + "1 risk uses level {level}. Change it first.": "1 riesgo usa el nivel {level}. Cámbielo primero.", + "{count} risks use level {level}. Change them first.": "{count} riesgos usan el nivel {level}. Cámbielos primero.", + "Every level needs a label, and high must start above medium.": "Cada nivel necesita una etiqueta, y alto debe empezar por encima de medio.", + "The project this risk belongs to": "El proyecto al que pertenece este riesgo", + "The cause and the effect, in Markdown": "La causa y el efecto, en Markdown", + "Category": "Categoría", + "A free grouping, such as planning, budget or supplier": "Una agrupación libre, como planificación, presupuesto o proveedor", + "How likely it is, on the admin's risk scale": "Qué probabilidad tiene, en la escala de riesgo del administrador", + "How hard it would hit, on the admin's risk scale": "Cuánto afectaría, en la escala de riesgo del administrador", + "Likelihood times impact, calculated by the server": "Probabilidad por impacto, calculada por el servidor", + "Where the risk stands": "En qué punto está el riesgo", + "The person who watches this risk": "La persona que vigila este riesgo", + "How the project answers the risk": "Cómo responde el proyecto al riesgo", + "What is done to prevent it or soften it, in Markdown": "Qué se hace para evitarlo o suavizarlo, en Markdown", + "When to look at this risk again": "Cuándo revisar de nuevo este riesgo", + "Very low": "Muy bajo", + "Very high": "Muy alto", + "All projects": "Todos los proyectos" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/es.json b/l10n/es.json index d57eb0e1..64c20273 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -308,7 +308,116 @@ "Board": "Tablero", "List": "Lista", "Column": "Columna", - "Board or list": "Tablero o lista" + "Board or list": "Tablero o lista", + "Log": "Registro", + "Add entry": "Añadir entrada", + "Show": "Mostrar", + "No actions yet": "Aún no hay acciones", + "Closed": "Cerrado", + "Entry actions": "Acciones de la entrada", + "Add action": "Añadir acción", + "by {author}": "por {author}", + "saved {time}": "guardado {time}", + "Attendees: {names}": "Asistentes: {names}", + "Add the first entry": "Añadir la primera entrada", + "Issues": "Incidencias", + "Lessons learned": "Lecciones aprendidas", + "Meetings": "Reuniones", + "Decisions": "Decisiones", + "Actions": "Acciones", + "Issue": "Incidencia", + "Lesson learned": "Lección aprendida", + "Meeting": "Reunión", + "Decision": "Decisión", + "About this project": "Acerca de este proyecto", + "No description yet": "Aún no hay descripción", + "Start date": "Fecha de inicio", + "Not set": "Sin definir", + "End date": "Fecha de fin", + "Progress": "Progreso", + "No tasks yet": "Aún no hay tareas", + "Open the board": "Abrir el tablero", + "People": "Personas", + "Owner": "Propietario", + "Latest log entries": "Últimas entradas", + "Open the log": "Abrir el registro", + "{done} of {total} tasks done": "{done} de {total} tareas hechas", + "Edit entry": "Editar entrada", + "Type": "Tipo", + "Attendees": "Asistentes", + "Issue is closed": "La incidencia está cerrada", + "Could not save the entry. Please try again.": "No se pudo guardar la entrada. Inténtelo de nuevo.", + "From: {title}": "De: {title}", + "What needs to happen": "Qué hay que hacer", + "Could not create the action. Please try again.": "No se pudo crear la acción. Inténtelo de nuevo.", + "The action was created but could not be linked to the entry.": "La acción se creó, pero no se pudo vincular a la entrada.", + "Project pages": "Páginas del proyecto", + "Overview": "Resumen", + "Risks": "Riesgos", + "Log entry": "Entrada de registro", + "A short name for the entry": "Un nombre corto para la entrada", + "Project": "Proyecto", + "The project this entry belongs to": "El proyecto al que pertenece esta entrada", + "What kind of entry this is": "Qué tipo de entrada es", + "When it happened": "Cuándo ocurrió", + "What happened, in Markdown": "Qué ocurrió, en Markdown", + "Whether an issue is still open": "Si una incidencia sigue abierta", + "The people at a meeting": "Las personas en una reunión", + "Tasks that came out of this entry": "Tareas surgidas de esta entrada", + "Add risk": "Añadir riesgo", + "Risk": "Riesgo", + "Score": "Puntuación", + "Response": "Respuesta", + "Review date": "Fecha de revisión", + "no longer on the project": "ya no está en el proyecto", + "Edit {title}": "Editar {title}", + "No risks recorded yet": "Aún no hay riesgos registrados", + "Add the first risk": "Añadir el primer riesgo", + "Medium": "Medio", + "Mitigating": "En mitigación", + "Occurred": "Ocurrido", + "Avoid": "Evitar", + "Reduce": "Reducir", + "Transfer": "Transferir", + "Accept": "Aceptar", + "Highest open risks": "Riesgos abiertos más altos", + "No open risks": "No hay riesgos abiertos", + "Open the risk register": "Abrir el registro de riesgos", + "Edit risk": "Editar riesgo", + "What could go wrong": "Qué podría salir mal", + "Likelihood": "Probabilidad", + "Impact": "Impacto", + "Score {score}": "Puntuación {score}", + "Countermeasures": "Contramedidas", + "Could not save the risk. Please try again.": "No se pudo guardar el riesgo. Inténtelo de nuevo.", + "Risks by likelihood and impact": "Riesgos por probabilidad e impacto", + "Risk scale": "Escala de riesgo", + "The levels every project scores its risks on, and where low turns into medium and high": "Los niveles en los que cada proyecto puntúa sus riesgos, y dónde bajo pasa a medio y alto", + "Number of levels": "Número de niveles", + "Likelihood labels": "Etiquetas de probabilidad", + "Impact labels": "Etiquetas de impacto", + "Level {level}": "Nivel {level}", + "Medium from score": "Medio a partir de la puntuación", + "High from score": "Alto a partir de la puntuación", + "Risk scale saved": "Escala de riesgo guardada", + "1 risk uses level {level}. Change it first.": "1 riesgo usa el nivel {level}. Cámbielo primero.", + "{count} risks use level {level}. Change them first.": "{count} riesgos usan el nivel {level}. Cámbielos primero.", + "Every level needs a label, and high must start above medium.": "Cada nivel necesita una etiqueta, y alto debe empezar por encima de medio.", + "The project this risk belongs to": "El proyecto al que pertenece este riesgo", + "The cause and the effect, in Markdown": "La causa y el efecto, en Markdown", + "Category": "Categoría", + "A free grouping, such as planning, budget or supplier": "Una agrupación libre, como planificación, presupuesto o proveedor", + "How likely it is, on the admin's risk scale": "Qué probabilidad tiene, en la escala de riesgo del administrador", + "How hard it would hit, on the admin's risk scale": "Cuánto afectaría, en la escala de riesgo del administrador", + "Likelihood times impact, calculated by the server": "Probabilidad por impacto, calculada por el servidor", + "Where the risk stands": "En qué punto está el riesgo", + "The person who watches this risk": "La persona que vigila este riesgo", + "How the project answers the risk": "Cómo responde el proyecto al riesgo", + "What is done to prevent it or soften it, in Markdown": "Qué se hace para evitarlo o suavizarlo, en Markdown", + "When to look at this risk again": "Cuándo revisar de nuevo este riesgo", + "Very low": "Muy bajo", + "Very high": "Muy alto", + "All projects": "Todos los proyectos" }, "plurals": null } diff --git a/l10n/et.js b/l10n/et.js index f9885437..f99596fd 100644 --- a/l10n/et.js +++ b/l10n/et.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tahvel", "List": "Loend", "Column": "Veerg", - "Board or list": "Tahvel või loend" + "Board or list": "Tahvel või loend", + "Log": "Logi", + "Add entry": "Lisa kirje", + "Show": "Näita", + "No actions yet": "Tegevusi veel pole", + "Closed": "Suletud", + "Entry actions": "Kirje toimingud", + "Add action": "Lisa tegevus", + "by {author}": "autor {author}", + "saved {time}": "salvestatud {time}", + "Attendees: {names}": "Osalejad: {names}", + "Add the first entry": "Lisa esimene kirje", + "Issues": "Probleemid", + "Lessons learned": "Õppetunnid", + "Meetings": "Koosolekud", + "Decisions": "Otsused", + "Actions": "Tegevused", + "Issue": "Probleem", + "Lesson learned": "Õppetund", + "Meeting": "Koosolek", + "Decision": "Otsus", + "About this project": "Projektist", + "No description yet": "Kirjeldust veel pole", + "Start date": "Alguskuupäev", + "Not set": "Määramata", + "End date": "Lõppkuupäev", + "Progress": "Edenemine", + "No tasks yet": "Ülesandeid veel pole", + "Open the board": "Ava tahvel", + "People": "Inimesed", + "Owner": "Omanik", + "Latest log entries": "Viimased kirjed", + "Open the log": "Ava logi", + "{done} of {total} tasks done": "{done} ülesannet {total}-st tehtud", + "Edit entry": "Muuda kirjet", + "Type": "Liik", + "Attendees": "Osalejad", + "Issue is closed": "Probleem on suletud", + "Could not save the entry. Please try again.": "Kirjet ei õnnestunud salvestada. Proovi uuesti.", + "From: {title}": "Allikas: {title}", + "What needs to happen": "Mis peab juhtuma", + "Could not create the action. Please try again.": "Tegevust ei õnnestunud luua. Proovi uuesti.", + "The action was created but could not be linked to the entry.": "Tegevus loodi, kuid seda ei õnnestunud kirjega siduda.", + "Project pages": "Projekti lehed", + "Overview": "Ülevaade", + "Risks": "Riskid", + "Log entry": "Logikirje", + "A short name for the entry": "Kirje lühinimi", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, millesse see kirje kuulub", + "What kind of entry this is": "Mis liiki kirje see on", + "When it happened": "Millal see juhtus", + "What happened, in Markdown": "Mis juhtus, Markdownis", + "Whether an issue is still open": "Kas probleem on veel avatud", + "The people at a meeting": "Koosolekul osalejad", + "Tasks that came out of this entry": "Sellest kirjest tulenevad ülesanded", + "Add risk": "Lisa risk", + "Risk": "Risk", + "Score": "Skoor", + "Response": "Vastus", + "Review date": "Ülevaatuse kuupäev", + "no longer on the project": "pole enam projektis", + "Edit {title}": "Muuda: {title}", + "No risks recorded yet": "Riske pole veel kirja pandud", + "Add the first risk": "Lisa esimene risk", + "Medium": "Keskmine", + "Mitigating": "Leevendamisel", + "Occurred": "Realiseerunud", + "Avoid": "Väldi", + "Reduce": "Vähenda", + "Transfer": "Kanna üle", + "Accept": "Aktsepteeri", + "Highest open risks": "Kõrgeimad avatud riskid", + "No open risks": "Avatud riske pole", + "Open the risk register": "Ava riskiregister", + "Edit risk": "Muuda riski", + "What could go wrong": "Mis võib valesti minna", + "Likelihood": "Tõenäosus", + "Impact": "Mõju", + "Score {score}": "Skoor {score}", + "Countermeasures": "Vastumeetmed", + "Could not save the risk. Please try again.": "Riski ei õnnestunud salvestada. Proovi uuesti.", + "Risks by likelihood and impact": "Riskid tõenäosuse ja mõju järgi", + "Risk scale": "Riskiskaala", + "The levels every project scores its risks on, and where low turns into medium and high": "Tasemed, millel iga projekt oma riske hindab, ja kus madal läheb üle keskmiseks ja kõrgeks", + "Number of levels": "Tasemete arv", + "Likelihood labels": "Tõenäosuse sildid", + "Impact labels": "Mõju sildid", + "Level {level}": "Tase {level}", + "Medium from score": "Keskmine alates skoorist", + "High from score": "Kõrge alates skoorist", + "Risk scale saved": "Riskiskaala salvestatud", + "1 risk uses level {level}. Change it first.": "1 risk kasutab taset {level}. Muuda see enne ära.", + "{count} risks use level {level}. Change them first.": "{count} riski kasutavad taset {level}. Muuda need enne ära.", + "Every level needs a label, and high must start above medium.": "Igal tasemel peab olema silt ja kõrge peab algama keskmisest kõrgemalt.", + "The project this risk belongs to": "Projekt, millesse see risk kuulub", + "The cause and the effect, in Markdown": "Põhjus ja tagajärg, Markdownis", + "Category": "Kategooria", + "A free grouping, such as planning, budget or supplier": "Vaba rühmitus, nt planeerimine, eelarve või tarnija", + "How likely it is, on the admin's risk scale": "Kui tõenäoline see on, administraatori riskiskaalal", + "How hard it would hit, on the admin's risk scale": "Kui rängalt see mõjuks, administraatori riskiskaalal", + "Likelihood times impact, calculated by the server": "Tõenäosus korda mõju, arvutab server", + "Where the risk stands": "Kus risk on", + "The person who watches this risk": "Inimene, kes seda riski jälgib", + "How the project answers the risk": "Kuidas projekt riskile vastab", + "What is done to prevent it or soften it, in Markdown": "Mida tehakse selle ärahoidmiseks või leevendamiseks, Markdownis", + "When to look at this risk again": "Millal riski uuesti vaadata", + "Very low": "Väga madal", + "Very high": "Väga kõrge", + "All projects": "Kõik projektid" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/et.json b/l10n/et.json index 779bdce7..e46e1883 100644 --- a/l10n/et.json +++ b/l10n/et.json @@ -308,7 +308,116 @@ "Board": "Tahvel", "List": "Loend", "Column": "Veerg", - "Board or list": "Tahvel või loend" + "Board or list": "Tahvel või loend", + "Log": "Logi", + "Add entry": "Lisa kirje", + "Show": "Näita", + "No actions yet": "Tegevusi veel pole", + "Closed": "Suletud", + "Entry actions": "Kirje toimingud", + "Add action": "Lisa tegevus", + "by {author}": "autor {author}", + "saved {time}": "salvestatud {time}", + "Attendees: {names}": "Osalejad: {names}", + "Add the first entry": "Lisa esimene kirje", + "Issues": "Probleemid", + "Lessons learned": "Õppetunnid", + "Meetings": "Koosolekud", + "Decisions": "Otsused", + "Actions": "Tegevused", + "Issue": "Probleem", + "Lesson learned": "Õppetund", + "Meeting": "Koosolek", + "Decision": "Otsus", + "About this project": "Projektist", + "No description yet": "Kirjeldust veel pole", + "Start date": "Alguskuupäev", + "Not set": "Määramata", + "End date": "Lõppkuupäev", + "Progress": "Edenemine", + "No tasks yet": "Ülesandeid veel pole", + "Open the board": "Ava tahvel", + "People": "Inimesed", + "Owner": "Omanik", + "Latest log entries": "Viimased kirjed", + "Open the log": "Ava logi", + "{done} of {total} tasks done": "{done} ülesannet {total}-st tehtud", + "Edit entry": "Muuda kirjet", + "Type": "Liik", + "Attendees": "Osalejad", + "Issue is closed": "Probleem on suletud", + "Could not save the entry. Please try again.": "Kirjet ei õnnestunud salvestada. Proovi uuesti.", + "From: {title}": "Allikas: {title}", + "What needs to happen": "Mis peab juhtuma", + "Could not create the action. Please try again.": "Tegevust ei õnnestunud luua. Proovi uuesti.", + "The action was created but could not be linked to the entry.": "Tegevus loodi, kuid seda ei õnnestunud kirjega siduda.", + "Project pages": "Projekti lehed", + "Overview": "Ülevaade", + "Risks": "Riskid", + "Log entry": "Logikirje", + "A short name for the entry": "Kirje lühinimi", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, millesse see kirje kuulub", + "What kind of entry this is": "Mis liiki kirje see on", + "When it happened": "Millal see juhtus", + "What happened, in Markdown": "Mis juhtus, Markdownis", + "Whether an issue is still open": "Kas probleem on veel avatud", + "The people at a meeting": "Koosolekul osalejad", + "Tasks that came out of this entry": "Sellest kirjest tulenevad ülesanded", + "Add risk": "Lisa risk", + "Risk": "Risk", + "Score": "Skoor", + "Response": "Vastus", + "Review date": "Ülevaatuse kuupäev", + "no longer on the project": "pole enam projektis", + "Edit {title}": "Muuda: {title}", + "No risks recorded yet": "Riske pole veel kirja pandud", + "Add the first risk": "Lisa esimene risk", + "Medium": "Keskmine", + "Mitigating": "Leevendamisel", + "Occurred": "Realiseerunud", + "Avoid": "Väldi", + "Reduce": "Vähenda", + "Transfer": "Kanna üle", + "Accept": "Aktsepteeri", + "Highest open risks": "Kõrgeimad avatud riskid", + "No open risks": "Avatud riske pole", + "Open the risk register": "Ava riskiregister", + "Edit risk": "Muuda riski", + "What could go wrong": "Mis võib valesti minna", + "Likelihood": "Tõenäosus", + "Impact": "Mõju", + "Score {score}": "Skoor {score}", + "Countermeasures": "Vastumeetmed", + "Could not save the risk. Please try again.": "Riski ei õnnestunud salvestada. Proovi uuesti.", + "Risks by likelihood and impact": "Riskid tõenäosuse ja mõju järgi", + "Risk scale": "Riskiskaala", + "The levels every project scores its risks on, and where low turns into medium and high": "Tasemed, millel iga projekt oma riske hindab, ja kus madal läheb üle keskmiseks ja kõrgeks", + "Number of levels": "Tasemete arv", + "Likelihood labels": "Tõenäosuse sildid", + "Impact labels": "Mõju sildid", + "Level {level}": "Tase {level}", + "Medium from score": "Keskmine alates skoorist", + "High from score": "Kõrge alates skoorist", + "Risk scale saved": "Riskiskaala salvestatud", + "1 risk uses level {level}. Change it first.": "1 risk kasutab taset {level}. Muuda see enne ära.", + "{count} risks use level {level}. Change them first.": "{count} riski kasutavad taset {level}. Muuda need enne ära.", + "Every level needs a label, and high must start above medium.": "Igal tasemel peab olema silt ja kõrge peab algama keskmisest kõrgemalt.", + "The project this risk belongs to": "Projekt, millesse see risk kuulub", + "The cause and the effect, in Markdown": "Põhjus ja tagajärg, Markdownis", + "Category": "Kategooria", + "A free grouping, such as planning, budget or supplier": "Vaba rühmitus, nt planeerimine, eelarve või tarnija", + "How likely it is, on the admin's risk scale": "Kui tõenäoline see on, administraatori riskiskaalal", + "How hard it would hit, on the admin's risk scale": "Kui rängalt see mõjuks, administraatori riskiskaalal", + "Likelihood times impact, calculated by the server": "Tõenäosus korda mõju, arvutab server", + "Where the risk stands": "Kus risk on", + "The person who watches this risk": "Inimene, kes seda riski jälgib", + "How the project answers the risk": "Kuidas projekt riskile vastab", + "What is done to prevent it or soften it, in Markdown": "Mida tehakse selle ärahoidmiseks või leevendamiseks, Markdownis", + "When to look at this risk again": "Millal riski uuesti vaadata", + "Very low": "Väga madal", + "Very high": "Väga kõrge", + "All projects": "Kõik projektid" }, "plurals": null } diff --git a/l10n/fi.js b/l10n/fi.js index 72c07c84..9de07b3e 100644 --- a/l10n/fi.js +++ b/l10n/fi.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Taulu", "List": "Luettelo", "Column": "Sarake", - "Board or list": "Taulu tai luettelo" + "Board or list": "Taulu tai luettelo", + "Log": "Loki", + "Add entry": "Lisää merkintä", + "Show": "Näytä", + "No actions yet": "Ei vielä toimenpiteitä", + "Closed": "Suljettu", + "Entry actions": "Merkinnän toiminnot", + "Add action": "Lisää toimenpide", + "by {author}": "tekijä {author}", + "saved {time}": "tallennettu {time}", + "Attendees: {names}": "Osallistujat: {names}", + "Add the first entry": "Lisää ensimmäinen merkintä", + "Issues": "Ongelmat", + "Lessons learned": "Opit", + "Meetings": "Kokoukset", + "Decisions": "Päätökset", + "Actions": "Toimenpiteet", + "Issue": "Ongelma", + "Lesson learned": "Opittu asia", + "Meeting": "Kokous", + "Decision": "Päätös", + "About this project": "Tietoa projektista", + "No description yet": "Ei vielä kuvausta", + "Start date": "Alkupäivä", + "Not set": "Ei asetettu", + "End date": "Loppupäivä", + "Progress": "Edistyminen", + "No tasks yet": "Ei vielä tehtäviä", + "Open the board": "Avaa taulu", + "People": "Henkilöt", + "Owner": "Omistaja", + "Latest log entries": "Uusimmat merkinnät", + "Open the log": "Avaa loki", + "{done} of {total} tasks done": "{done}/{total} tehtävää valmiina", + "Edit entry": "Muokkaa merkintää", + "Type": "Tyyppi", + "Attendees": "Osallistujat", + "Issue is closed": "Ongelma on suljettu", + "Could not save the entry. Please try again.": "Merkintää ei voitu tallentaa. Yritä uudelleen.", + "From: {title}": "Lähde: {title}", + "What needs to happen": "Mitä pitää tehdä", + "Could not create the action. Please try again.": "Toimenpidettä ei voitu luoda. Yritä uudelleen.", + "The action was created but could not be linked to the entry.": "Toimenpide luotiin, mutta sitä ei voitu liittää merkintään.", + "Project pages": "Projektin sivut", + "Overview": "Yleiskatsaus", + "Risks": "Riskit", + "Log entry": "Lokimerkintä", + "A short name for the entry": "Merkinnän lyhyt nimi", + "Project": "Projekti", + "The project this entry belongs to": "Projekti, johon merkintä kuuluu", + "What kind of entry this is": "Minkä tyyppinen merkintä on", + "When it happened": "Milloin se tapahtui", + "What happened, in Markdown": "Mitä tapahtui, Markdownina", + "Whether an issue is still open": "Onko ongelma yhä auki", + "The people at a meeting": "Kokouksen osallistujat", + "Tasks that came out of this entry": "Tästä merkinnästä syntyneet tehtävät", + "Add risk": "Lisää riski", + "Risk": "Riski", + "Score": "Pisteet", + "Response": "Toimintatapa", + "Review date": "Tarkistuspäivä", + "no longer on the project": "ei enää projektissa", + "Edit {title}": "Muokkaa: {title}", + "No risks recorded yet": "Riskejä ei ole vielä kirjattu", + "Add the first risk": "Lisää ensimmäinen riski", + "Medium": "Keskitaso", + "Mitigating": "Lievennetään", + "Occurred": "Toteutunut", + "Avoid": "Vältä", + "Reduce": "Pienennä", + "Transfer": "Siirrä", + "Accept": "Hyväksy", + "Highest open risks": "Suurimmat avoimet riskit", + "No open risks": "Ei avoimia riskejä", + "Open the risk register": "Avaa riskirekisteri", + "Edit risk": "Muokkaa riskiä", + "What could go wrong": "Mikä voi mennä pieleen", + "Likelihood": "Todennäköisyys", + "Impact": "Vaikutus", + "Score {score}": "Pisteet {score}", + "Countermeasures": "Vastatoimet", + "Could not save the risk. Please try again.": "Riskiä ei voitu tallentaa. Yritä uudelleen.", + "Risks by likelihood and impact": "Riskit todennäköisyyden ja vaikutuksen mukaan", + "Risk scale": "Riskiasteikko", + "The levels every project scores its risks on, and where low turns into medium and high": "Tasot, joilla jokainen projekti pisteyttää riskinsä, ja missä matala vaihtuu keskitasoon ja korkeaan", + "Number of levels": "Tasojen määrä", + "Likelihood labels": "Todennäköisyyden nimet", + "Impact labels": "Vaikutuksen nimet", + "Level {level}": "Taso {level}", + "Medium from score": "Keskitaso pisteistä", + "High from score": "Korkea pisteistä", + "Risk scale saved": "Riskiasteikko tallennettu", + "1 risk uses level {level}. Change it first.": "1 riski käyttää tasoa {level}. Muuta se ensin.", + "{count} risks use level {level}. Change them first.": "{count} riskiä käyttää tasoa {level}. Muuta ne ensin.", + "Every level needs a label, and high must start above medium.": "Jokainen taso tarvitsee nimen, ja korkean on alettava keskitason yläpuolelta.", + "The project this risk belongs to": "Projekti, johon riski kuuluu", + "The cause and the effect, in Markdown": "Syy ja seuraus, Markdownina", + "Category": "Luokka", + "A free grouping, such as planning, budget or supplier": "Vapaa ryhmittely, kuten aikataulu, budjetti tai toimittaja", + "How likely it is, on the admin's risk scale": "Kuinka todennäköinen se on ylläpitäjän riskiasteikolla", + "How hard it would hit, on the admin's risk scale": "Kuinka kovaa se osuisi ylläpitäjän riskiasteikolla", + "Likelihood times impact, calculated by the server": "Todennäköisyys kertaa vaikutus, palvelimen laskema", + "Where the risk stands": "Missä vaiheessa riski on", + "The person who watches this risk": "Henkilö, joka seuraa tätä riskiä", + "How the project answers the risk": "Miten projekti vastaa riskiin", + "What is done to prevent it or soften it, in Markdown": "Mitä tehdään sen estämiseksi tai lieventämiseksi, Markdownina", + "When to look at this risk again": "Milloin riski tarkistetaan uudelleen", + "Very low": "Hyvin matala", + "Very high": "Hyvin korkea", + "All projects": "Kaikki projektit" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/fi.json b/l10n/fi.json index 8aa6185a..aed39839 100644 --- a/l10n/fi.json +++ b/l10n/fi.json @@ -308,7 +308,116 @@ "Board": "Taulu", "List": "Luettelo", "Column": "Sarake", - "Board or list": "Taulu tai luettelo" + "Board or list": "Taulu tai luettelo", + "Log": "Loki", + "Add entry": "Lisää merkintä", + "Show": "Näytä", + "No actions yet": "Ei vielä toimenpiteitä", + "Closed": "Suljettu", + "Entry actions": "Merkinnän toiminnot", + "Add action": "Lisää toimenpide", + "by {author}": "tekijä {author}", + "saved {time}": "tallennettu {time}", + "Attendees: {names}": "Osallistujat: {names}", + "Add the first entry": "Lisää ensimmäinen merkintä", + "Issues": "Ongelmat", + "Lessons learned": "Opit", + "Meetings": "Kokoukset", + "Decisions": "Päätökset", + "Actions": "Toimenpiteet", + "Issue": "Ongelma", + "Lesson learned": "Opittu asia", + "Meeting": "Kokous", + "Decision": "Päätös", + "About this project": "Tietoa projektista", + "No description yet": "Ei vielä kuvausta", + "Start date": "Alkupäivä", + "Not set": "Ei asetettu", + "End date": "Loppupäivä", + "Progress": "Edistyminen", + "No tasks yet": "Ei vielä tehtäviä", + "Open the board": "Avaa taulu", + "People": "Henkilöt", + "Owner": "Omistaja", + "Latest log entries": "Uusimmat merkinnät", + "Open the log": "Avaa loki", + "{done} of {total} tasks done": "{done}/{total} tehtävää valmiina", + "Edit entry": "Muokkaa merkintää", + "Type": "Tyyppi", + "Attendees": "Osallistujat", + "Issue is closed": "Ongelma on suljettu", + "Could not save the entry. Please try again.": "Merkintää ei voitu tallentaa. Yritä uudelleen.", + "From: {title}": "Lähde: {title}", + "What needs to happen": "Mitä pitää tehdä", + "Could not create the action. Please try again.": "Toimenpidettä ei voitu luoda. Yritä uudelleen.", + "The action was created but could not be linked to the entry.": "Toimenpide luotiin, mutta sitä ei voitu liittää merkintään.", + "Project pages": "Projektin sivut", + "Overview": "Yleiskatsaus", + "Risks": "Riskit", + "Log entry": "Lokimerkintä", + "A short name for the entry": "Merkinnän lyhyt nimi", + "Project": "Projekti", + "The project this entry belongs to": "Projekti, johon merkintä kuuluu", + "What kind of entry this is": "Minkä tyyppinen merkintä on", + "When it happened": "Milloin se tapahtui", + "What happened, in Markdown": "Mitä tapahtui, Markdownina", + "Whether an issue is still open": "Onko ongelma yhä auki", + "The people at a meeting": "Kokouksen osallistujat", + "Tasks that came out of this entry": "Tästä merkinnästä syntyneet tehtävät", + "Add risk": "Lisää riski", + "Risk": "Riski", + "Score": "Pisteet", + "Response": "Toimintatapa", + "Review date": "Tarkistuspäivä", + "no longer on the project": "ei enää projektissa", + "Edit {title}": "Muokkaa: {title}", + "No risks recorded yet": "Riskejä ei ole vielä kirjattu", + "Add the first risk": "Lisää ensimmäinen riski", + "Medium": "Keskitaso", + "Mitigating": "Lievennetään", + "Occurred": "Toteutunut", + "Avoid": "Vältä", + "Reduce": "Pienennä", + "Transfer": "Siirrä", + "Accept": "Hyväksy", + "Highest open risks": "Suurimmat avoimet riskit", + "No open risks": "Ei avoimia riskejä", + "Open the risk register": "Avaa riskirekisteri", + "Edit risk": "Muokkaa riskiä", + "What could go wrong": "Mikä voi mennä pieleen", + "Likelihood": "Todennäköisyys", + "Impact": "Vaikutus", + "Score {score}": "Pisteet {score}", + "Countermeasures": "Vastatoimet", + "Could not save the risk. Please try again.": "Riskiä ei voitu tallentaa. Yritä uudelleen.", + "Risks by likelihood and impact": "Riskit todennäköisyyden ja vaikutuksen mukaan", + "Risk scale": "Riskiasteikko", + "The levels every project scores its risks on, and where low turns into medium and high": "Tasot, joilla jokainen projekti pisteyttää riskinsä, ja missä matala vaihtuu keskitasoon ja korkeaan", + "Number of levels": "Tasojen määrä", + "Likelihood labels": "Todennäköisyyden nimet", + "Impact labels": "Vaikutuksen nimet", + "Level {level}": "Taso {level}", + "Medium from score": "Keskitaso pisteistä", + "High from score": "Korkea pisteistä", + "Risk scale saved": "Riskiasteikko tallennettu", + "1 risk uses level {level}. Change it first.": "1 riski käyttää tasoa {level}. Muuta se ensin.", + "{count} risks use level {level}. Change them first.": "{count} riskiä käyttää tasoa {level}. Muuta ne ensin.", + "Every level needs a label, and high must start above medium.": "Jokainen taso tarvitsee nimen, ja korkean on alettava keskitason yläpuolelta.", + "The project this risk belongs to": "Projekti, johon riski kuuluu", + "The cause and the effect, in Markdown": "Syy ja seuraus, Markdownina", + "Category": "Luokka", + "A free grouping, such as planning, budget or supplier": "Vapaa ryhmittely, kuten aikataulu, budjetti tai toimittaja", + "How likely it is, on the admin's risk scale": "Kuinka todennäköinen se on ylläpitäjän riskiasteikolla", + "How hard it would hit, on the admin's risk scale": "Kuinka kovaa se osuisi ylläpitäjän riskiasteikolla", + "Likelihood times impact, calculated by the server": "Todennäköisyys kertaa vaikutus, palvelimen laskema", + "Where the risk stands": "Missä vaiheessa riski on", + "The person who watches this risk": "Henkilö, joka seuraa tätä riskiä", + "How the project answers the risk": "Miten projekti vastaa riskiin", + "What is done to prevent it or soften it, in Markdown": "Mitä tehdään sen estämiseksi tai lieventämiseksi, Markdownina", + "When to look at this risk again": "Milloin riski tarkistetaan uudelleen", + "Very low": "Hyvin matala", + "Very high": "Hyvin korkea", + "All projects": "Kaikki projektit" }, "plurals": null } diff --git a/l10n/fr.js b/l10n/fr.js index b94d18f9..bc5aabbf 100644 --- a/l10n/fr.js +++ b/l10n/fr.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tableau", "List": "Liste", "Column": "Colonne", - "Board or list": "Tableau ou liste" + "Board or list": "Tableau ou liste", + "Log": "Journal", + "Add entry": "Ajouter une entrée", + "Show": "Afficher", + "No actions yet": "Aucune action pour l'instant", + "Closed": "Fermé", + "Entry actions": "Actions de l'entrée", + "Add action": "Ajouter une action", + "by {author}": "par {author}", + "saved {time}": "enregistré {time}", + "Attendees: {names}": "Participants : {names}", + "Add the first entry": "Ajouter la première entrée", + "Issues": "Problèmes", + "Lessons learned": "Enseignements", + "Meetings": "Réunions", + "Decisions": "Décisions", + "Actions": "Actions", + "Issue": "Problème", + "Lesson learned": "Enseignement", + "Meeting": "Réunion", + "Decision": "Décision", + "About this project": "À propos de ce projet", + "No description yet": "Pas encore de description", + "Start date": "Date de début", + "Not set": "Non défini", + "End date": "Date de fin", + "Progress": "Avancement", + "No tasks yet": "Pas encore de tâches", + "Open the board": "Ouvrir le tableau", + "People": "Personnes", + "Owner": "Propriétaire", + "Latest log entries": "Dernières entrées", + "Open the log": "Ouvrir le journal", + "{done} of {total} tasks done": "{done} tâches terminées sur {total}", + "Edit entry": "Modifier l'entrée", + "Type": "Type", + "Attendees": "Participants", + "Issue is closed": "Le problème est fermé", + "Could not save the entry. Please try again.": "Impossible d'enregistrer l'entrée. Veuillez réessayer.", + "From: {title}": "Issu de : {title}", + "What needs to happen": "Ce qui doit être fait", + "Could not create the action. Please try again.": "Impossible de créer l'action. Veuillez réessayer.", + "The action was created but could not be linked to the entry.": "L'action a été créée mais n'a pas pu être liée à l'entrée.", + "Project pages": "Pages du projet", + "Overview": "Vue d'ensemble", + "Risks": "Risques", + "Log entry": "Entrée de journal", + "A short name for the entry": "Un nom court pour l'entrée", + "Project": "Projet", + "The project this entry belongs to": "Le projet auquel appartient cette entrée", + "What kind of entry this is": "Le type d'entrée", + "When it happened": "Quand cela s'est produit", + "What happened, in Markdown": "Ce qui s'est passé, en Markdown", + "Whether an issue is still open": "Si un problème est encore ouvert", + "The people at a meeting": "Les personnes présentes à une réunion", + "Tasks that came out of this entry": "Les tâches issues de cette entrée", + "Add risk": "Ajouter un risque", + "Risk": "Risque", + "Score": "Score", + "Response": "Réponse", + "Review date": "Date de révision", + "no longer on the project": "ne fait plus partie du projet", + "Edit {title}": "Modifier {title}", + "No risks recorded yet": "Aucun risque consigné pour l'instant", + "Add the first risk": "Ajouter le premier risque", + "Medium": "Moyen", + "Mitigating": "En atténuation", + "Occurred": "Survenu", + "Avoid": "Éviter", + "Reduce": "Réduire", + "Transfer": "Transférer", + "Accept": "Accepter", + "Highest open risks": "Risques ouverts les plus élevés", + "No open risks": "Aucun risque ouvert", + "Open the risk register": "Ouvrir le registre des risques", + "Edit risk": "Modifier le risque", + "What could go wrong": "Ce qui pourrait mal tourner", + "Likelihood": "Probabilité", + "Impact": "Impact", + "Score {score}": "Score {score}", + "Countermeasures": "Contre-mesures", + "Could not save the risk. Please try again.": "Impossible d'enregistrer le risque. Veuillez réessayer.", + "Risks by likelihood and impact": "Risques par probabilité et impact", + "Risk scale": "Échelle de risque", + "The levels every project scores its risks on, and where low turns into medium and high": "Les niveaux sur lesquels chaque projet évalue ses risques, et où faible devient moyen puis élevé", + "Number of levels": "Nombre de niveaux", + "Likelihood labels": "Libellés de probabilité", + "Impact labels": "Libellés d'impact", + "Level {level}": "Niveau {level}", + "Medium from score": "Moyen à partir du score", + "High from score": "Élevé à partir du score", + "Risk scale saved": "Échelle de risque enregistrée", + "1 risk uses level {level}. Change it first.": "1 risque utilise le niveau {level}. Modifiez-le d'abord.", + "{count} risks use level {level}. Change them first.": "{count} risques utilisent le niveau {level}. Modifiez-les d'abord.", + "Every level needs a label, and high must start above medium.": "Chaque niveau a besoin d'un libellé, et élevé doit commencer au-dessus de moyen.", + "The project this risk belongs to": "Le projet auquel appartient ce risque", + "The cause and the effect, in Markdown": "La cause et l'effet, en Markdown", + "Category": "Catégorie", + "A free grouping, such as planning, budget or supplier": "Un regroupement libre, comme planning, budget ou fournisseur", + "How likely it is, on the admin's risk scale": "Sa probabilité, sur l'échelle de risque de l'administrateur", + "How hard it would hit, on the admin's risk scale": "La gravité de son impact, sur l'échelle de risque de l'administrateur", + "Likelihood times impact, calculated by the server": "Probabilité multipliée par l'impact, calculée par le serveur", + "Where the risk stands": "Où en est le risque", + "The person who watches this risk": "La personne qui surveille ce risque", + "How the project answers the risk": "Comment le projet répond au risque", + "What is done to prevent it or soften it, in Markdown": "Ce qui est fait pour l'éviter ou l'atténuer, en Markdown", + "When to look at this risk again": "Quand réexaminer ce risque", + "Very low": "Très faible", + "Very high": "Très élevé", + "All projects": "Tous les projets" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/fr.json b/l10n/fr.json index 4c162a4e..0294b1b3 100644 --- a/l10n/fr.json +++ b/l10n/fr.json @@ -308,7 +308,116 @@ "Board": "Tableau", "List": "Liste", "Column": "Colonne", - "Board or list": "Tableau ou liste" + "Board or list": "Tableau ou liste", + "Log": "Journal", + "Add entry": "Ajouter une entrée", + "Show": "Afficher", + "No actions yet": "Aucune action pour l'instant", + "Closed": "Fermé", + "Entry actions": "Actions de l'entrée", + "Add action": "Ajouter une action", + "by {author}": "par {author}", + "saved {time}": "enregistré {time}", + "Attendees: {names}": "Participants : {names}", + "Add the first entry": "Ajouter la première entrée", + "Issues": "Problèmes", + "Lessons learned": "Enseignements", + "Meetings": "Réunions", + "Decisions": "Décisions", + "Actions": "Actions", + "Issue": "Problème", + "Lesson learned": "Enseignement", + "Meeting": "Réunion", + "Decision": "Décision", + "About this project": "À propos de ce projet", + "No description yet": "Pas encore de description", + "Start date": "Date de début", + "Not set": "Non défini", + "End date": "Date de fin", + "Progress": "Avancement", + "No tasks yet": "Pas encore de tâches", + "Open the board": "Ouvrir le tableau", + "People": "Personnes", + "Owner": "Propriétaire", + "Latest log entries": "Dernières entrées", + "Open the log": "Ouvrir le journal", + "{done} of {total} tasks done": "{done} tâches terminées sur {total}", + "Edit entry": "Modifier l'entrée", + "Type": "Type", + "Attendees": "Participants", + "Issue is closed": "Le problème est fermé", + "Could not save the entry. Please try again.": "Impossible d'enregistrer l'entrée. Veuillez réessayer.", + "From: {title}": "Issu de : {title}", + "What needs to happen": "Ce qui doit être fait", + "Could not create the action. Please try again.": "Impossible de créer l'action. Veuillez réessayer.", + "The action was created but could not be linked to the entry.": "L'action a été créée mais n'a pas pu être liée à l'entrée.", + "Project pages": "Pages du projet", + "Overview": "Vue d'ensemble", + "Risks": "Risques", + "Log entry": "Entrée de journal", + "A short name for the entry": "Un nom court pour l'entrée", + "Project": "Projet", + "The project this entry belongs to": "Le projet auquel appartient cette entrée", + "What kind of entry this is": "Le type d'entrée", + "When it happened": "Quand cela s'est produit", + "What happened, in Markdown": "Ce qui s'est passé, en Markdown", + "Whether an issue is still open": "Si un problème est encore ouvert", + "The people at a meeting": "Les personnes présentes à une réunion", + "Tasks that came out of this entry": "Les tâches issues de cette entrée", + "Add risk": "Ajouter un risque", + "Risk": "Risque", + "Score": "Score", + "Response": "Réponse", + "Review date": "Date de révision", + "no longer on the project": "ne fait plus partie du projet", + "Edit {title}": "Modifier {title}", + "No risks recorded yet": "Aucun risque consigné pour l'instant", + "Add the first risk": "Ajouter le premier risque", + "Medium": "Moyen", + "Mitigating": "En atténuation", + "Occurred": "Survenu", + "Avoid": "Éviter", + "Reduce": "Réduire", + "Transfer": "Transférer", + "Accept": "Accepter", + "Highest open risks": "Risques ouverts les plus élevés", + "No open risks": "Aucun risque ouvert", + "Open the risk register": "Ouvrir le registre des risques", + "Edit risk": "Modifier le risque", + "What could go wrong": "Ce qui pourrait mal tourner", + "Likelihood": "Probabilité", + "Impact": "Impact", + "Score {score}": "Score {score}", + "Countermeasures": "Contre-mesures", + "Could not save the risk. Please try again.": "Impossible d'enregistrer le risque. Veuillez réessayer.", + "Risks by likelihood and impact": "Risques par probabilité et impact", + "Risk scale": "Échelle de risque", + "The levels every project scores its risks on, and where low turns into medium and high": "Les niveaux sur lesquels chaque projet évalue ses risques, et où faible devient moyen puis élevé", + "Number of levels": "Nombre de niveaux", + "Likelihood labels": "Libellés de probabilité", + "Impact labels": "Libellés d'impact", + "Level {level}": "Niveau {level}", + "Medium from score": "Moyen à partir du score", + "High from score": "Élevé à partir du score", + "Risk scale saved": "Échelle de risque enregistrée", + "1 risk uses level {level}. Change it first.": "1 risque utilise le niveau {level}. Modifiez-le d'abord.", + "{count} risks use level {level}. Change them first.": "{count} risques utilisent le niveau {level}. Modifiez-les d'abord.", + "Every level needs a label, and high must start above medium.": "Chaque niveau a besoin d'un libellé, et élevé doit commencer au-dessus de moyen.", + "The project this risk belongs to": "Le projet auquel appartient ce risque", + "The cause and the effect, in Markdown": "La cause et l'effet, en Markdown", + "Category": "Catégorie", + "A free grouping, such as planning, budget or supplier": "Un regroupement libre, comme planning, budget ou fournisseur", + "How likely it is, on the admin's risk scale": "Sa probabilité, sur l'échelle de risque de l'administrateur", + "How hard it would hit, on the admin's risk scale": "La gravité de son impact, sur l'échelle de risque de l'administrateur", + "Likelihood times impact, calculated by the server": "Probabilité multipliée par l'impact, calculée par le serveur", + "Where the risk stands": "Où en est le risque", + "The person who watches this risk": "La personne qui surveille ce risque", + "How the project answers the risk": "Comment le projet répond au risque", + "What is done to prevent it or soften it, in Markdown": "Ce qui est fait pour l'éviter ou l'atténuer, en Markdown", + "When to look at this risk again": "Quand réexaminer ce risque", + "Very low": "Très faible", + "Very high": "Très élevé", + "All projects": "Tous les projets" }, "plurals": null } diff --git a/l10n/ga.js b/l10n/ga.js index e7f72cf2..a270f96f 100644 --- a/l10n/ga.js +++ b/l10n/ga.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Clár", "List": "Liosta", "Column": "Colún", - "Board or list": "Clár nó liosta" + "Board or list": "Clár nó liosta", + "Log": "Loga", + "Add entry": "Cuir iontráil leis", + "Show": "Taispeáin", + "No actions yet": "Níl aon ghníomhartha fós", + "Closed": "Dúnta", + "Entry actions": "Gníomhartha iontrála", + "Add action": "Cuir gníomh leis", + "by {author}": "le {author}", + "saved {time}": "sábháilte {time}", + "Attendees: {names}": "Lucht freastail: {names}", + "Add the first entry": "Cuir an chéad iontráil leis", + "Issues": "Saincheisteanna", + "Lessons learned": "Ceachtanna foghlamtha", + "Meetings": "Cruinnithe", + "Decisions": "Cinntí", + "Actions": "Gníomhartha", + "Issue": "Saincheist", + "Lesson learned": "Ceacht foghlamtha", + "Meeting": "Cruinniú", + "Decision": "Cinneadh", + "About this project": "Faoin tionscadal seo", + "No description yet": "Níl aon chur síos fós", + "Start date": "Dáta tosaigh", + "Not set": "Gan socrú", + "End date": "Dáta deiridh", + "Progress": "Dul chun cinn", + "No tasks yet": "Níl aon tascanna fós", + "Open the board": "Oscail an clár", + "People": "Daoine", + "Owner": "Úinéir", + "Latest log entries": "Na hiontrálacha is déanaí", + "Open the log": "Oscail an loga", + "{done} of {total} tasks done": "{done} as {total} tasc déanta", + "Edit entry": "Cuir an iontráil in eagar", + "Type": "Cineál", + "Attendees": "Lucht freastail", + "Issue is closed": "Tá an tsaincheist dúnta", + "Could not save the entry. Please try again.": "Níorbh fhéidir an iontráil a shábháil. Bain triail eile as.", + "From: {title}": "Ó: {title}", + "What needs to happen": "Cad is gá a dhéanamh", + "Could not create the action. Please try again.": "Níorbh fhéidir an gníomh a chruthú. Bain triail eile as.", + "The action was created but could not be linked to the entry.": "Cruthaíodh an gníomh ach níorbh fhéidir é a nascadh leis an iontráil.", + "Project pages": "Leathanaigh an tionscadail", + "Overview": "Forbhreathnú", + "Risks": "Rioscaí", + "Log entry": "Iontráil loga", + "A short name for the entry": "Ainm gearr don iontráil", + "Project": "Tionscadal", + "The project this entry belongs to": "An tionscadal lena mbaineann an iontráil seo", + "What kind of entry this is": "Cén cineál iontrála é seo", + "When it happened": "Cathain a tharla sé", + "What happened, in Markdown": "Cad a tharla, i Markdown", + "Whether an issue is still open": "An bhfuil saincheist fós oscailte", + "The people at a meeting": "Na daoine ag cruinniú", + "Tasks that came out of this entry": "Tascanna a d'eascair ón iontráil seo", + "Add risk": "Cuir riosca leis", + "Risk": "Riosca", + "Score": "Scór", + "Response": "Freagairt", + "Review date": "Dáta athbhreithnithe", + "no longer on the project": "níl sé ar an tionscadal a thuilleadh", + "Edit {title}": "Cuir {title} in eagar", + "No risks recorded yet": "Níl aon rioscaí taifeadta fós", + "Add the first risk": "Cuir an chéad riosca leis", + "Medium": "Meánach", + "Mitigating": "Á mhaolú", + "Occurred": "Tharla", + "Avoid": "Seachain", + "Reduce": "Laghdaigh", + "Transfer": "Aistrigh", + "Accept": "Glac leis", + "Highest open risks": "Na rioscaí oscailte is airde", + "No open risks": "Níl aon rioscaí oscailte", + "Open the risk register": "Oscail an clár rioscaí", + "Edit risk": "Cuir riosca in eagar", + "What could go wrong": "Cad a d'fhéadfadh dul mícheart", + "Likelihood": "Dóchúlacht", + "Impact": "Tionchar", + "Score {score}": "Scór {score}", + "Countermeasures": "Frithbhearta", + "Could not save the risk. Please try again.": "Níorbh fhéidir an riosca a shábháil. Bain triail eile as.", + "Risks by likelihood and impact": "Rioscaí de réir dóchúlachta agus tionchair", + "Risk scale": "Scála riosca", + "The levels every project scores its risks on, and where low turns into medium and high": "Na leibhéil ar a scórálann gach tionscadal a rioscaí, agus an áit a n-éiríonn íseal ina mheánach agus ina ard", + "Number of levels": "Líon na leibhéal", + "Likelihood labels": "Lipéid dóchúlachta", + "Impact labels": "Lipéid tionchair", + "Level {level}": "Leibhéal {level}", + "Medium from score": "Meánach ón scór", + "High from score": "Ard ón scór", + "Risk scale saved": "Sábháladh an scála riosca", + "1 risk uses level {level}. Change it first.": "Úsáideann 1 riosca leibhéal {level}. Athraigh é ar dtús.", + "{count} risks use level {level}. Change them first.": "Úsáideann {count} riosca leibhéal {level}. Athraigh iad ar dtús.", + "Every level needs a label, and high must start above medium.": "Tá lipéad de dhíth ar gach leibhéal, agus caithfidh ard tosú os cionn meánach.", + "The project this risk belongs to": "An tionscadal lena mbaineann an riosca seo", + "The cause and the effect, in Markdown": "An chúis agus an toradh, i Markdown", + "Category": "Catagóir", + "A free grouping, such as planning, budget or supplier": "Grúpáil shaor, mar phleanáil, buiséad nó soláthraí", + "How likely it is, on the admin's risk scale": "Cé chomh dóchúil is atá sé, ar scála riosca an riarthóra", + "How hard it would hit, on the admin's risk scale": "Cé chomh crua is a bhuailfeadh sé, ar scála riosca an riarthóra", + "Likelihood times impact, calculated by the server": "Dóchúlacht faoi thionchar, arna ríomh ag an bhfreastalaí", + "Where the risk stands": "Cá bhfuil an riosca", + "The person who watches this risk": "An duine a choinníonn súil ar an riosca seo", + "How the project answers the risk": "Conas a fhreagraíonn an tionscadal an riosca", + "What is done to prevent it or soften it, in Markdown": "Cad a dhéantar chun é a chosc nó a mhaolú, i Markdown", + "When to look at this risk again": "Cathain an riosca a athbhreithniú", + "Very low": "An-íseal", + "Very high": "An-ard", + "All projects": "Gach tionscadal" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ga.json b/l10n/ga.json index 1c9f5e5c..d6534ce6 100644 --- a/l10n/ga.json +++ b/l10n/ga.json @@ -308,7 +308,116 @@ "Board": "Clár", "List": "Liosta", "Column": "Colún", - "Board or list": "Clár nó liosta" + "Board or list": "Clár nó liosta", + "Log": "Loga", + "Add entry": "Cuir iontráil leis", + "Show": "Taispeáin", + "No actions yet": "Níl aon ghníomhartha fós", + "Closed": "Dúnta", + "Entry actions": "Gníomhartha iontrála", + "Add action": "Cuir gníomh leis", + "by {author}": "le {author}", + "saved {time}": "sábháilte {time}", + "Attendees: {names}": "Lucht freastail: {names}", + "Add the first entry": "Cuir an chéad iontráil leis", + "Issues": "Saincheisteanna", + "Lessons learned": "Ceachtanna foghlamtha", + "Meetings": "Cruinnithe", + "Decisions": "Cinntí", + "Actions": "Gníomhartha", + "Issue": "Saincheist", + "Lesson learned": "Ceacht foghlamtha", + "Meeting": "Cruinniú", + "Decision": "Cinneadh", + "About this project": "Faoin tionscadal seo", + "No description yet": "Níl aon chur síos fós", + "Start date": "Dáta tosaigh", + "Not set": "Gan socrú", + "End date": "Dáta deiridh", + "Progress": "Dul chun cinn", + "No tasks yet": "Níl aon tascanna fós", + "Open the board": "Oscail an clár", + "People": "Daoine", + "Owner": "Úinéir", + "Latest log entries": "Na hiontrálacha is déanaí", + "Open the log": "Oscail an loga", + "{done} of {total} tasks done": "{done} as {total} tasc déanta", + "Edit entry": "Cuir an iontráil in eagar", + "Type": "Cineál", + "Attendees": "Lucht freastail", + "Issue is closed": "Tá an tsaincheist dúnta", + "Could not save the entry. Please try again.": "Níorbh fhéidir an iontráil a shábháil. Bain triail eile as.", + "From: {title}": "Ó: {title}", + "What needs to happen": "Cad is gá a dhéanamh", + "Could not create the action. Please try again.": "Níorbh fhéidir an gníomh a chruthú. Bain triail eile as.", + "The action was created but could not be linked to the entry.": "Cruthaíodh an gníomh ach níorbh fhéidir é a nascadh leis an iontráil.", + "Project pages": "Leathanaigh an tionscadail", + "Overview": "Forbhreathnú", + "Risks": "Rioscaí", + "Log entry": "Iontráil loga", + "A short name for the entry": "Ainm gearr don iontráil", + "Project": "Tionscadal", + "The project this entry belongs to": "An tionscadal lena mbaineann an iontráil seo", + "What kind of entry this is": "Cén cineál iontrála é seo", + "When it happened": "Cathain a tharla sé", + "What happened, in Markdown": "Cad a tharla, i Markdown", + "Whether an issue is still open": "An bhfuil saincheist fós oscailte", + "The people at a meeting": "Na daoine ag cruinniú", + "Tasks that came out of this entry": "Tascanna a d'eascair ón iontráil seo", + "Add risk": "Cuir riosca leis", + "Risk": "Riosca", + "Score": "Scór", + "Response": "Freagairt", + "Review date": "Dáta athbhreithnithe", + "no longer on the project": "níl sé ar an tionscadal a thuilleadh", + "Edit {title}": "Cuir {title} in eagar", + "No risks recorded yet": "Níl aon rioscaí taifeadta fós", + "Add the first risk": "Cuir an chéad riosca leis", + "Medium": "Meánach", + "Mitigating": "Á mhaolú", + "Occurred": "Tharla", + "Avoid": "Seachain", + "Reduce": "Laghdaigh", + "Transfer": "Aistrigh", + "Accept": "Glac leis", + "Highest open risks": "Na rioscaí oscailte is airde", + "No open risks": "Níl aon rioscaí oscailte", + "Open the risk register": "Oscail an clár rioscaí", + "Edit risk": "Cuir riosca in eagar", + "What could go wrong": "Cad a d'fhéadfadh dul mícheart", + "Likelihood": "Dóchúlacht", + "Impact": "Tionchar", + "Score {score}": "Scór {score}", + "Countermeasures": "Frithbhearta", + "Could not save the risk. Please try again.": "Níorbh fhéidir an riosca a shábháil. Bain triail eile as.", + "Risks by likelihood and impact": "Rioscaí de réir dóchúlachta agus tionchair", + "Risk scale": "Scála riosca", + "The levels every project scores its risks on, and where low turns into medium and high": "Na leibhéil ar a scórálann gach tionscadal a rioscaí, agus an áit a n-éiríonn íseal ina mheánach agus ina ard", + "Number of levels": "Líon na leibhéal", + "Likelihood labels": "Lipéid dóchúlachta", + "Impact labels": "Lipéid tionchair", + "Level {level}": "Leibhéal {level}", + "Medium from score": "Meánach ón scór", + "High from score": "Ard ón scór", + "Risk scale saved": "Sábháladh an scála riosca", + "1 risk uses level {level}. Change it first.": "Úsáideann 1 riosca leibhéal {level}. Athraigh é ar dtús.", + "{count} risks use level {level}. Change them first.": "Úsáideann {count} riosca leibhéal {level}. Athraigh iad ar dtús.", + "Every level needs a label, and high must start above medium.": "Tá lipéad de dhíth ar gach leibhéal, agus caithfidh ard tosú os cionn meánach.", + "The project this risk belongs to": "An tionscadal lena mbaineann an riosca seo", + "The cause and the effect, in Markdown": "An chúis agus an toradh, i Markdown", + "Category": "Catagóir", + "A free grouping, such as planning, budget or supplier": "Grúpáil shaor, mar phleanáil, buiséad nó soláthraí", + "How likely it is, on the admin's risk scale": "Cé chomh dóchúil is atá sé, ar scála riosca an riarthóra", + "How hard it would hit, on the admin's risk scale": "Cé chomh crua is a bhuailfeadh sé, ar scála riosca an riarthóra", + "Likelihood times impact, calculated by the server": "Dóchúlacht faoi thionchar, arna ríomh ag an bhfreastalaí", + "Where the risk stands": "Cá bhfuil an riosca", + "The person who watches this risk": "An duine a choinníonn súil ar an riosca seo", + "How the project answers the risk": "Conas a fhreagraíonn an tionscadal an riosca", + "What is done to prevent it or soften it, in Markdown": "Cad a dhéantar chun é a chosc nó a mhaolú, i Markdown", + "When to look at this risk again": "Cathain an riosca a athbhreithniú", + "Very low": "An-íseal", + "Very high": "An-ard", + "All projects": "Gach tionscadal" }, "plurals": null } diff --git a/l10n/hr.js b/l10n/hr.js index 329b05ef..264324cf 100644 --- a/l10n/hr.js +++ b/l10n/hr.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Ploča", "List": "Popis", "Column": "Stupac", - "Board or list": "Ploča ili popis" + "Board or list": "Ploča ili popis", + "Log": "Dnevnik", + "Add entry": "Dodaj zapis", + "Show": "Prikaži", + "No actions yet": "Još nema radnji", + "Closed": "Zatvoreno", + "Entry actions": "Radnje zapisa", + "Add action": "Dodaj radnju", + "by {author}": "autor {author}", + "saved {time}": "spremljeno {time}", + "Attendees: {names}": "Prisutni: {names}", + "Add the first entry": "Dodaj prvi zapis", + "Issues": "Problemi", + "Lessons learned": "Naučene lekcije", + "Meetings": "Sastanci", + "Decisions": "Odluke", + "Actions": "Radnje", + "Issue": "Problem", + "Lesson learned": "Naučena lekcija", + "Meeting": "Sastanak", + "Decision": "Odluka", + "About this project": "O ovom projektu", + "No description yet": "Još nema opisa", + "Start date": "Datum početka", + "Not set": "Nije postavljeno", + "End date": "Datum završetka", + "Progress": "Napredak", + "No tasks yet": "Još nema zadataka", + "Open the board": "Otvori ploču", + "People": "Osobe", + "Owner": "Vlasnik", + "Latest log entries": "Najnoviji zapisi", + "Open the log": "Otvori dnevnik", + "{done} of {total} tasks done": "{done} od {total} zadataka gotovo", + "Edit entry": "Uredi zapis", + "Type": "Vrsta", + "Attendees": "Prisutni", + "Issue is closed": "Problem je zatvoren", + "Could not save the entry. Please try again.": "Zapis nije moguće spremiti. Pokušajte ponovno.", + "From: {title}": "Iz: {title}", + "What needs to happen": "Što treba učiniti", + "Could not create the action. Please try again.": "Radnju nije moguće stvoriti. Pokušajte ponovno.", + "The action was created but could not be linked to the entry.": "Radnja je stvorena, ali je nije moguće povezati sa zapisom.", + "Project pages": "Stranice projekta", + "Overview": "Pregled", + "Risks": "Rizici", + "Log entry": "Zapis dnevnika", + "A short name for the entry": "Kratak naziv zapisa", + "Project": "Projekt", + "The project this entry belongs to": "Projekt kojem ovaj zapis pripada", + "What kind of entry this is": "Koja je ovo vrsta zapisa", + "When it happened": "Kada se dogodilo", + "What happened, in Markdown": "Što se dogodilo, u Markdownu", + "Whether an issue is still open": "Je li problem još otvoren", + "The people at a meeting": "Osobe na sastanku", + "Tasks that came out of this entry": "Zadaci proizašli iz ovog zapisa", + "Add risk": "Dodaj rizik", + "Risk": "Rizik", + "Score": "Ocjena", + "Response": "Odgovor", + "Review date": "Datum pregleda", + "no longer on the project": "više nije na projektu", + "Edit {title}": "Uredi {title}", + "No risks recorded yet": "Još nema zabilježenih rizika", + "Add the first risk": "Dodaj prvi rizik", + "Medium": "Srednje", + "Mitigating": "Ublažava se", + "Occurred": "Nastupio", + "Avoid": "Izbjeći", + "Reduce": "Smanjiti", + "Transfer": "Prenijeti", + "Accept": "Prihvatiti", + "Highest open risks": "Najviši otvoreni rizici", + "No open risks": "Nema otvorenih rizika", + "Open the risk register": "Otvori registar rizika", + "Edit risk": "Uredi rizik", + "What could go wrong": "Što može poći po zlu", + "Likelihood": "Vjerojatnost", + "Impact": "Utjecaj", + "Score {score}": "Ocjena {score}", + "Countermeasures": "Protumjere", + "Could not save the risk. Please try again.": "Rizik nije moguće spremiti. Pokušajte ponovno.", + "Risks by likelihood and impact": "Rizici po vjerojatnosti i utjecaju", + "Risk scale": "Ljestvica rizika", + "The levels every project scores its risks on, and where low turns into medium and high": "Razine na kojima svaki projekt ocjenjuje rizike i gdje nisko prelazi u srednje i visoko", + "Number of levels": "Broj razina", + "Likelihood labels": "Oznake vjerojatnosti", + "Impact labels": "Oznake utjecaja", + "Level {level}": "Razina {level}", + "Medium from score": "Srednje od ocjene", + "High from score": "Visoko od ocjene", + "Risk scale saved": "Ljestvica rizika spremljena", + "1 risk uses level {level}. Change it first.": "1 rizik koristi razinu {level}. Najprije ga promijenite.", + "{count} risks use level {level}. Change them first.": "{count} rizika koristi razinu {level}. Najprije ih promijenite.", + "Every level needs a label, and high must start above medium.": "Svaka razina treba oznaku, a visoko mora početi iznad srednjeg.", + "The project this risk belongs to": "Projekt kojem ovaj rizik pripada", + "The cause and the effect, in Markdown": "Uzrok i posljedica, u Markdownu", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Slobodno grupiranje, npr. planiranje, proračun ili dobavljač", + "How likely it is, on the admin's risk scale": "Koliko je vjerojatno, na ljestvici rizika administratora", + "How hard it would hit, on the admin's risk scale": "Koliko bi snažno pogodilo, na ljestvici rizika administratora", + "Likelihood times impact, calculated by the server": "Vjerojatnost puta utjecaj, izračunao poslužitelj", + "Where the risk stands": "Gdje je rizik", + "The person who watches this risk": "Osoba koja prati ovaj rizik", + "How the project answers the risk": "Kako projekt odgovara na rizik", + "What is done to prevent it or soften it, in Markdown": "Što se čini da se spriječi ili ublaži, u Markdownu", + "When to look at this risk again": "Kada ponovno pregledati ovaj rizik", + "Very low": "Vrlo nisko", + "Very high": "Vrlo visoko", + "All projects": "Svi projekti" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/hr.json b/l10n/hr.json index 22c02bca..efdaea32 100644 --- a/l10n/hr.json +++ b/l10n/hr.json @@ -308,7 +308,116 @@ "Board": "Ploča", "List": "Popis", "Column": "Stupac", - "Board or list": "Ploča ili popis" + "Board or list": "Ploča ili popis", + "Log": "Dnevnik", + "Add entry": "Dodaj zapis", + "Show": "Prikaži", + "No actions yet": "Još nema radnji", + "Closed": "Zatvoreno", + "Entry actions": "Radnje zapisa", + "Add action": "Dodaj radnju", + "by {author}": "autor {author}", + "saved {time}": "spremljeno {time}", + "Attendees: {names}": "Prisutni: {names}", + "Add the first entry": "Dodaj prvi zapis", + "Issues": "Problemi", + "Lessons learned": "Naučene lekcije", + "Meetings": "Sastanci", + "Decisions": "Odluke", + "Actions": "Radnje", + "Issue": "Problem", + "Lesson learned": "Naučena lekcija", + "Meeting": "Sastanak", + "Decision": "Odluka", + "About this project": "O ovom projektu", + "No description yet": "Još nema opisa", + "Start date": "Datum početka", + "Not set": "Nije postavljeno", + "End date": "Datum završetka", + "Progress": "Napredak", + "No tasks yet": "Još nema zadataka", + "Open the board": "Otvori ploču", + "People": "Osobe", + "Owner": "Vlasnik", + "Latest log entries": "Najnoviji zapisi", + "Open the log": "Otvori dnevnik", + "{done} of {total} tasks done": "{done} od {total} zadataka gotovo", + "Edit entry": "Uredi zapis", + "Type": "Vrsta", + "Attendees": "Prisutni", + "Issue is closed": "Problem je zatvoren", + "Could not save the entry. Please try again.": "Zapis nije moguće spremiti. Pokušajte ponovno.", + "From: {title}": "Iz: {title}", + "What needs to happen": "Što treba učiniti", + "Could not create the action. Please try again.": "Radnju nije moguće stvoriti. Pokušajte ponovno.", + "The action was created but could not be linked to the entry.": "Radnja je stvorena, ali je nije moguće povezati sa zapisom.", + "Project pages": "Stranice projekta", + "Overview": "Pregled", + "Risks": "Rizici", + "Log entry": "Zapis dnevnika", + "A short name for the entry": "Kratak naziv zapisa", + "Project": "Projekt", + "The project this entry belongs to": "Projekt kojem ovaj zapis pripada", + "What kind of entry this is": "Koja je ovo vrsta zapisa", + "When it happened": "Kada se dogodilo", + "What happened, in Markdown": "Što se dogodilo, u Markdownu", + "Whether an issue is still open": "Je li problem još otvoren", + "The people at a meeting": "Osobe na sastanku", + "Tasks that came out of this entry": "Zadaci proizašli iz ovog zapisa", + "Add risk": "Dodaj rizik", + "Risk": "Rizik", + "Score": "Ocjena", + "Response": "Odgovor", + "Review date": "Datum pregleda", + "no longer on the project": "više nije na projektu", + "Edit {title}": "Uredi {title}", + "No risks recorded yet": "Još nema zabilježenih rizika", + "Add the first risk": "Dodaj prvi rizik", + "Medium": "Srednje", + "Mitigating": "Ublažava se", + "Occurred": "Nastupio", + "Avoid": "Izbjeći", + "Reduce": "Smanjiti", + "Transfer": "Prenijeti", + "Accept": "Prihvatiti", + "Highest open risks": "Najviši otvoreni rizici", + "No open risks": "Nema otvorenih rizika", + "Open the risk register": "Otvori registar rizika", + "Edit risk": "Uredi rizik", + "What could go wrong": "Što može poći po zlu", + "Likelihood": "Vjerojatnost", + "Impact": "Utjecaj", + "Score {score}": "Ocjena {score}", + "Countermeasures": "Protumjere", + "Could not save the risk. Please try again.": "Rizik nije moguće spremiti. Pokušajte ponovno.", + "Risks by likelihood and impact": "Rizici po vjerojatnosti i utjecaju", + "Risk scale": "Ljestvica rizika", + "The levels every project scores its risks on, and where low turns into medium and high": "Razine na kojima svaki projekt ocjenjuje rizike i gdje nisko prelazi u srednje i visoko", + "Number of levels": "Broj razina", + "Likelihood labels": "Oznake vjerojatnosti", + "Impact labels": "Oznake utjecaja", + "Level {level}": "Razina {level}", + "Medium from score": "Srednje od ocjene", + "High from score": "Visoko od ocjene", + "Risk scale saved": "Ljestvica rizika spremljena", + "1 risk uses level {level}. Change it first.": "1 rizik koristi razinu {level}. Najprije ga promijenite.", + "{count} risks use level {level}. Change them first.": "{count} rizika koristi razinu {level}. Najprije ih promijenite.", + "Every level needs a label, and high must start above medium.": "Svaka razina treba oznaku, a visoko mora početi iznad srednjeg.", + "The project this risk belongs to": "Projekt kojem ovaj rizik pripada", + "The cause and the effect, in Markdown": "Uzrok i posljedica, u Markdownu", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Slobodno grupiranje, npr. planiranje, proračun ili dobavljač", + "How likely it is, on the admin's risk scale": "Koliko je vjerojatno, na ljestvici rizika administratora", + "How hard it would hit, on the admin's risk scale": "Koliko bi snažno pogodilo, na ljestvici rizika administratora", + "Likelihood times impact, calculated by the server": "Vjerojatnost puta utjecaj, izračunao poslužitelj", + "Where the risk stands": "Gdje je rizik", + "The person who watches this risk": "Osoba koja prati ovaj rizik", + "How the project answers the risk": "Kako projekt odgovara na rizik", + "What is done to prevent it or soften it, in Markdown": "Što se čini da se spriječi ili ublaži, u Markdownu", + "When to look at this risk again": "Kada ponovno pregledati ovaj rizik", + "Very low": "Vrlo nisko", + "Very high": "Vrlo visoko", + "All projects": "Svi projekti" }, "plurals": null } diff --git a/l10n/hu.js b/l10n/hu.js index 26a25ff7..8863f4f2 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tábla", "List": "Lista", "Column": "Oszlop", - "Board or list": "Tábla vagy lista" + "Board or list": "Tábla vagy lista", + "Log": "Napló", + "Add entry": "Bejegyzés hozzáadása", + "Show": "Megjelenítés", + "No actions yet": "Még nincsenek teendők", + "Closed": "Lezárva", + "Entry actions": "Bejegyzés műveletei", + "Add action": "Teendő hozzáadása", + "by {author}": "szerző: {author}", + "saved {time}": "mentve: {time}", + "Attendees: {names}": "Résztvevők: {names}", + "Add the first entry": "Az első bejegyzés hozzáadása", + "Issues": "Problémák", + "Lessons learned": "Tanulságok", + "Meetings": "Megbeszélések", + "Decisions": "Döntések", + "Actions": "Teendők", + "Issue": "Probléma", + "Lesson learned": "Tanulság", + "Meeting": "Megbeszélés", + "Decision": "Döntés", + "About this project": "A projektről", + "No description yet": "Még nincs leírás", + "Start date": "Kezdő dátum", + "Not set": "Nincs beállítva", + "End date": "Záró dátum", + "Progress": "Előrehaladás", + "No tasks yet": "Még nincsenek feladatok", + "Open the board": "Tábla megnyitása", + "People": "Személyek", + "Owner": "Tulajdonos", + "Latest log entries": "Legutóbbi bejegyzések", + "Open the log": "Napló megnyitása", + "{done} of {total} tasks done": "{total} feladatból {done} kész", + "Edit entry": "Bejegyzés szerkesztése", + "Type": "Fajta", + "Attendees": "Résztvevők", + "Issue is closed": "A probléma lezárva", + "Could not save the entry. Please try again.": "A bejegyzést nem sikerült menteni. Próbálja újra.", + "From: {title}": "Forrás: {title}", + "What needs to happen": "Mit kell tenni", + "Could not create the action. Please try again.": "A teendőt nem sikerült létrehozni. Próbálja újra.", + "The action was created but could not be linked to the entry.": "A teendő létrejött, de nem sikerült a bejegyzéshez kapcsolni.", + "Project pages": "Projektoldalak", + "Overview": "Áttekintés", + "Risks": "Kockázatok", + "Log entry": "Naplóbejegyzés", + "A short name for the entry": "A bejegyzés rövid neve", + "Project": "Projekt", + "The project this entry belongs to": "A projekt, amelyhez a bejegyzés tartozik", + "What kind of entry this is": "Milyen fajta bejegyzés ez", + "When it happened": "Mikor történt", + "What happened, in Markdown": "Mi történt, Markdownban", + "Whether an issue is still open": "Nyitott-e még a probléma", + "The people at a meeting": "A megbeszélés résztvevői", + "Tasks that came out of this entry": "A bejegyzésből született feladatok", + "Add risk": "Kockázat hozzáadása", + "Risk": "Kockázat", + "Score": "Pontszám", + "Response": "Válasz", + "Review date": "Felülvizsgálat dátuma", + "no longer on the project": "már nem tagja a projektnek", + "Edit {title}": "{title} szerkesztése", + "No risks recorded yet": "Még nincsenek rögzített kockázatok", + "Add the first risk": "Az első kockázat hozzáadása", + "Medium": "Közepes", + "Mitigating": "Enyhítés alatt", + "Occurred": "Bekövetkezett", + "Avoid": "Elkerülés", + "Reduce": "Csökkentés", + "Transfer": "Áthárítás", + "Accept": "Elfogadás", + "Highest open risks": "Legmagasabb nyitott kockázatok", + "No open risks": "Nincsenek nyitott kockázatok", + "Open the risk register": "Kockázatnyilvántartás megnyitása", + "Edit risk": "Kockázat szerkesztése", + "What could go wrong": "Mi mehet félre", + "Likelihood": "Valószínűség", + "Impact": "Hatás", + "Score {score}": "Pontszám: {score}", + "Countermeasures": "Ellenintézkedések", + "Could not save the risk. Please try again.": "A kockázatot nem sikerült menteni. Próbálja újra.", + "Risks by likelihood and impact": "Kockázatok valószínűség és hatás szerint", + "Risk scale": "Kockázati skála", + "The levels every project scores its risks on, and where low turns into medium and high": "A szintek, amelyeken minden projekt pontozza a kockázatait, és ahol az alacsony közepesbe és magasba vált", + "Number of levels": "Szintek száma", + "Likelihood labels": "Valószínűség címkéi", + "Impact labels": "Hatás címkéi", + "Level {level}": "{level}. szint", + "Medium from score": "Közepes ettől a pontszámtól", + "High from score": "Magas ettől a pontszámtól", + "Risk scale saved": "Kockázati skála mentve", + "1 risk uses level {level}. Change it first.": "1 kockázat a(z) {level}. szintet használja. Előbb módosítsa.", + "{count} risks use level {level}. Change them first.": "{count} kockázat a(z) {level}. szintet használja. Előbb módosítsa őket.", + "Every level needs a label, and high must start above medium.": "Minden szintnek kell címke, és a magasnak a közepes fölött kell kezdődnie.", + "The project this risk belongs to": "A projekt, amelyhez a kockázat tartozik", + "The cause and the effect, in Markdown": "Az ok és az okozat, Markdownban", + "Category": "Kategória", + "A free grouping, such as planning, budget or supplier": "Szabad csoportosítás, pl. ütemezés, költségvetés vagy beszállító", + "How likely it is, on the admin's risk scale": "Mennyire valószínű, a rendszergazda kockázati skáláján", + "How hard it would hit, on the admin's risk scale": "Mennyire súlyosan érintene, a rendszergazda kockázati skáláján", + "Likelihood times impact, calculated by the server": "Valószínűség szorozva a hatással, a kiszolgáló számolja", + "Where the risk stands": "Hol tart a kockázat", + "The person who watches this risk": "A személy, aki figyeli a kockázatot", + "How the project answers the risk": "Hogyan válaszol a projekt a kockázatra", + "What is done to prevent it or soften it, in Markdown": "Mit tesznek a megelőzésére vagy enyhítésére, Markdownban", + "When to look at this risk again": "Mikor kell újra megvizsgálni a kockázatot", + "Very low": "Nagyon alacsony", + "Very high": "Nagyon magas", + "All projects": "Minden projekt" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/hu.json b/l10n/hu.json index 20f2a2c2..5112c0c0 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -308,7 +308,116 @@ "Board": "Tábla", "List": "Lista", "Column": "Oszlop", - "Board or list": "Tábla vagy lista" + "Board or list": "Tábla vagy lista", + "Log": "Napló", + "Add entry": "Bejegyzés hozzáadása", + "Show": "Megjelenítés", + "No actions yet": "Még nincsenek teendők", + "Closed": "Lezárva", + "Entry actions": "Bejegyzés műveletei", + "Add action": "Teendő hozzáadása", + "by {author}": "szerző: {author}", + "saved {time}": "mentve: {time}", + "Attendees: {names}": "Résztvevők: {names}", + "Add the first entry": "Az első bejegyzés hozzáadása", + "Issues": "Problémák", + "Lessons learned": "Tanulságok", + "Meetings": "Megbeszélések", + "Decisions": "Döntések", + "Actions": "Teendők", + "Issue": "Probléma", + "Lesson learned": "Tanulság", + "Meeting": "Megbeszélés", + "Decision": "Döntés", + "About this project": "A projektről", + "No description yet": "Még nincs leírás", + "Start date": "Kezdő dátum", + "Not set": "Nincs beállítva", + "End date": "Záró dátum", + "Progress": "Előrehaladás", + "No tasks yet": "Még nincsenek feladatok", + "Open the board": "Tábla megnyitása", + "People": "Személyek", + "Owner": "Tulajdonos", + "Latest log entries": "Legutóbbi bejegyzések", + "Open the log": "Napló megnyitása", + "{done} of {total} tasks done": "{total} feladatból {done} kész", + "Edit entry": "Bejegyzés szerkesztése", + "Type": "Fajta", + "Attendees": "Résztvevők", + "Issue is closed": "A probléma lezárva", + "Could not save the entry. Please try again.": "A bejegyzést nem sikerült menteni. Próbálja újra.", + "From: {title}": "Forrás: {title}", + "What needs to happen": "Mit kell tenni", + "Could not create the action. Please try again.": "A teendőt nem sikerült létrehozni. Próbálja újra.", + "The action was created but could not be linked to the entry.": "A teendő létrejött, de nem sikerült a bejegyzéshez kapcsolni.", + "Project pages": "Projektoldalak", + "Overview": "Áttekintés", + "Risks": "Kockázatok", + "Log entry": "Naplóbejegyzés", + "A short name for the entry": "A bejegyzés rövid neve", + "Project": "Projekt", + "The project this entry belongs to": "A projekt, amelyhez a bejegyzés tartozik", + "What kind of entry this is": "Milyen fajta bejegyzés ez", + "When it happened": "Mikor történt", + "What happened, in Markdown": "Mi történt, Markdownban", + "Whether an issue is still open": "Nyitott-e még a probléma", + "The people at a meeting": "A megbeszélés résztvevői", + "Tasks that came out of this entry": "A bejegyzésből született feladatok", + "Add risk": "Kockázat hozzáadása", + "Risk": "Kockázat", + "Score": "Pontszám", + "Response": "Válasz", + "Review date": "Felülvizsgálat dátuma", + "no longer on the project": "már nem tagja a projektnek", + "Edit {title}": "{title} szerkesztése", + "No risks recorded yet": "Még nincsenek rögzített kockázatok", + "Add the first risk": "Az első kockázat hozzáadása", + "Medium": "Közepes", + "Mitigating": "Enyhítés alatt", + "Occurred": "Bekövetkezett", + "Avoid": "Elkerülés", + "Reduce": "Csökkentés", + "Transfer": "Áthárítás", + "Accept": "Elfogadás", + "Highest open risks": "Legmagasabb nyitott kockázatok", + "No open risks": "Nincsenek nyitott kockázatok", + "Open the risk register": "Kockázatnyilvántartás megnyitása", + "Edit risk": "Kockázat szerkesztése", + "What could go wrong": "Mi mehet félre", + "Likelihood": "Valószínűség", + "Impact": "Hatás", + "Score {score}": "Pontszám: {score}", + "Countermeasures": "Ellenintézkedések", + "Could not save the risk. Please try again.": "A kockázatot nem sikerült menteni. Próbálja újra.", + "Risks by likelihood and impact": "Kockázatok valószínűség és hatás szerint", + "Risk scale": "Kockázati skála", + "The levels every project scores its risks on, and where low turns into medium and high": "A szintek, amelyeken minden projekt pontozza a kockázatait, és ahol az alacsony közepesbe és magasba vált", + "Number of levels": "Szintek száma", + "Likelihood labels": "Valószínűség címkéi", + "Impact labels": "Hatás címkéi", + "Level {level}": "{level}. szint", + "Medium from score": "Közepes ettől a pontszámtól", + "High from score": "Magas ettől a pontszámtól", + "Risk scale saved": "Kockázati skála mentve", + "1 risk uses level {level}. Change it first.": "1 kockázat a(z) {level}. szintet használja. Előbb módosítsa.", + "{count} risks use level {level}. Change them first.": "{count} kockázat a(z) {level}. szintet használja. Előbb módosítsa őket.", + "Every level needs a label, and high must start above medium.": "Minden szintnek kell címke, és a magasnak a közepes fölött kell kezdődnie.", + "The project this risk belongs to": "A projekt, amelyhez a kockázat tartozik", + "The cause and the effect, in Markdown": "Az ok és az okozat, Markdownban", + "Category": "Kategória", + "A free grouping, such as planning, budget or supplier": "Szabad csoportosítás, pl. ütemezés, költségvetés vagy beszállító", + "How likely it is, on the admin's risk scale": "Mennyire valószínű, a rendszergazda kockázati skáláján", + "How hard it would hit, on the admin's risk scale": "Mennyire súlyosan érintene, a rendszergazda kockázati skáláján", + "Likelihood times impact, calculated by the server": "Valószínűség szorozva a hatással, a kiszolgáló számolja", + "Where the risk stands": "Hol tart a kockázat", + "The person who watches this risk": "A személy, aki figyeli a kockázatot", + "How the project answers the risk": "Hogyan válaszol a projekt a kockázatra", + "What is done to prevent it or soften it, in Markdown": "Mit tesznek a megelőzésére vagy enyhítésére, Markdownban", + "When to look at this risk again": "Mikor kell újra megvizsgálni a kockázatot", + "Very low": "Nagyon alacsony", + "Very high": "Nagyon magas", + "All projects": "Minden projekt" }, "plurals": null } diff --git a/l10n/is.js b/l10n/is.js index e7ebca86..c6c12590 100644 --- a/l10n/is.js +++ b/l10n/is.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tafla", "List": "Listi", "Column": "Dálkur", - "Board or list": "Tafla eða listi" + "Board or list": "Tafla eða listi", + "Log": "Dagbók", + "Add entry": "Bæta við færslu", + "Show": "Sýna", + "No actions yet": "Engar aðgerðir enn", + "Closed": "Lokað", + "Entry actions": "Aðgerðir færslu", + "Add action": "Bæta við aðgerð", + "by {author}": "eftir {author}", + "saved {time}": "vistað {time}", + "Attendees: {names}": "Viðstaddir: {names}", + "Add the first entry": "Bæta við fyrstu færslunni", + "Issues": "Vandamál", + "Lessons learned": "Lærdómur", + "Meetings": "Fundir", + "Decisions": "Ákvarðanir", + "Actions": "Aðgerðir", + "Issue": "Vandamál", + "Lesson learned": "Lærdómur", + "Meeting": "Fundur", + "Decision": "Ákvörðun", + "About this project": "Um þetta verkefni", + "No description yet": "Engin lýsing enn", + "Start date": "Upphafsdagur", + "Not set": "Ekki stillt", + "End date": "Lokadagur", + "Progress": "Framvinda", + "No tasks yet": "Engin verkefni enn", + "Open the board": "Opna töfluna", + "People": "Fólk", + "Owner": "Eigandi", + "Latest log entries": "Nýjustu færslur", + "Open the log": "Opna dagbókina", + "{done} of {total} tasks done": "{done} af {total} verkefnum lokið", + "Edit entry": "Breyta færslu", + "Type": "Tegund", + "Attendees": "Viðstaddir", + "Issue is closed": "Vandamálinu er lokað", + "Could not save the entry. Please try again.": "Ekki tókst að vista færsluna. Reyndu aftur.", + "From: {title}": "Úr: {title}", + "What needs to happen": "Hvað þarf að gerast", + "Could not create the action. Please try again.": "Ekki tókst að búa til aðgerðina. Reyndu aftur.", + "The action was created but could not be linked to the entry.": "Aðgerðin var búin til en ekki tókst að tengja hana við færsluna.", + "Project pages": "Síður verkefnisins", + "Overview": "Yfirlit", + "Risks": "Áhættur", + "Log entry": "Dagbókarfærsla", + "A short name for the entry": "Stutt heiti færslunnar", + "Project": "Verkefni", + "The project this entry belongs to": "Verkefnið sem færslan tilheyrir", + "What kind of entry this is": "Hvers konar færsla þetta er", + "When it happened": "Hvenær það gerðist", + "What happened, in Markdown": "Hvað gerðist, í Markdown", + "Whether an issue is still open": "Hvort vandamál sé enn opið", + "The people at a meeting": "Fólkið á fundi", + "Tasks that came out of this entry": "Verkefni sem spruttu af þessari færslu", + "Add risk": "Bæta við áhættu", + "Risk": "Áhætta", + "Score": "Stig", + "Response": "Viðbrögð", + "Review date": "Endurskoðunardagur", + "no longer on the project": "er ekki lengur í verkefninu", + "Edit {title}": "Breyta {title}", + "No risks recorded yet": "Engar áhættur skráðar enn", + "Add the first risk": "Bæta við fyrstu áhættunni", + "Medium": "Miðlungs", + "Mitigating": "Í mildun", + "Occurred": "Kom fram", + "Avoid": "Forðast", + "Reduce": "Draga úr", + "Transfer": "Færa annað", + "Accept": "Samþykkja", + "Highest open risks": "Hæstu opnu áhættur", + "No open risks": "Engar opnar áhættur", + "Open the risk register": "Opna áhætturegistrið", + "Edit risk": "Breyta áhættu", + "What could go wrong": "Hvað getur farið úrskeiðis", + "Likelihood": "Líkur", + "Impact": "Áhrif", + "Score {score}": "Stig {score}", + "Countermeasures": "Mótvægisaðgerðir", + "Could not save the risk. Please try again.": "Ekki tókst að vista áhættuna. Reyndu aftur.", + "Risks by likelihood and impact": "Áhættur eftir líkum og áhrifum", + "Risk scale": "Áhættukvarði", + "The levels every project scores its risks on, and where low turns into medium and high": "Stigin sem hvert verkefni metur áhættur sínar á, og hvar lágt verður miðlungs og hátt", + "Number of levels": "Fjöldi stiga", + "Likelihood labels": "Heiti fyrir líkur", + "Impact labels": "Heiti fyrir áhrif", + "Level {level}": "Stig {level}", + "Medium from score": "Miðlungs frá stigum", + "High from score": "Hátt frá stigum", + "Risk scale saved": "Áhættukvarði vistaður", + "1 risk uses level {level}. Change it first.": "1 áhætta notar stig {level}. Breyttu henni fyrst.", + "{count} risks use level {level}. Change them first.": "{count} áhættur nota stig {level}. Breyttu þeim fyrst.", + "Every level needs a label, and high must start above medium.": "Hvert stig þarf heiti, og hátt verður að byrja fyrir ofan miðlungs.", + "The project this risk belongs to": "Verkefnið sem áhættan tilheyrir", + "The cause and the effect, in Markdown": "Orsök og afleiðing, í Markdown", + "Category": "Flokkur", + "A free grouping, such as planning, budget or supplier": "Frjáls flokkun, t.d. áætlun, fjárhagsáætlun eða birgir", + "How likely it is, on the admin's risk scale": "Hversu líklegt það er, á áhættukvarða kerfisstjórans", + "How hard it would hit, on the admin's risk scale": "Hversu hart það myndi koma niður, á áhættukvarða kerfisstjórans", + "Likelihood times impact, calculated by the server": "Líkur sinnum áhrif, reiknað af þjóninum", + "Where the risk stands": "Hvar áhættan stendur", + "The person who watches this risk": "Manneskjan sem fylgist með áhættunni", + "How the project answers the risk": "Hvernig verkefnið bregst við áhættunni", + "What is done to prevent it or soften it, in Markdown": "Hvað er gert til að koma í veg fyrir hana eða milda, í Markdown", + "When to look at this risk again": "Hvenær á að skoða áhættuna aftur", + "Very low": "Mjög lágt", + "Very high": "Mjög hátt", + "All projects": "Öll verkefni" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/is.json b/l10n/is.json index 110777e8..b2435693 100644 --- a/l10n/is.json +++ b/l10n/is.json @@ -308,7 +308,116 @@ "Board": "Tafla", "List": "Listi", "Column": "Dálkur", - "Board or list": "Tafla eða listi" + "Board or list": "Tafla eða listi", + "Log": "Dagbók", + "Add entry": "Bæta við færslu", + "Show": "Sýna", + "No actions yet": "Engar aðgerðir enn", + "Closed": "Lokað", + "Entry actions": "Aðgerðir færslu", + "Add action": "Bæta við aðgerð", + "by {author}": "eftir {author}", + "saved {time}": "vistað {time}", + "Attendees: {names}": "Viðstaddir: {names}", + "Add the first entry": "Bæta við fyrstu færslunni", + "Issues": "Vandamál", + "Lessons learned": "Lærdómur", + "Meetings": "Fundir", + "Decisions": "Ákvarðanir", + "Actions": "Aðgerðir", + "Issue": "Vandamál", + "Lesson learned": "Lærdómur", + "Meeting": "Fundur", + "Decision": "Ákvörðun", + "About this project": "Um þetta verkefni", + "No description yet": "Engin lýsing enn", + "Start date": "Upphafsdagur", + "Not set": "Ekki stillt", + "End date": "Lokadagur", + "Progress": "Framvinda", + "No tasks yet": "Engin verkefni enn", + "Open the board": "Opna töfluna", + "People": "Fólk", + "Owner": "Eigandi", + "Latest log entries": "Nýjustu færslur", + "Open the log": "Opna dagbókina", + "{done} of {total} tasks done": "{done} af {total} verkefnum lokið", + "Edit entry": "Breyta færslu", + "Type": "Tegund", + "Attendees": "Viðstaddir", + "Issue is closed": "Vandamálinu er lokað", + "Could not save the entry. Please try again.": "Ekki tókst að vista færsluna. Reyndu aftur.", + "From: {title}": "Úr: {title}", + "What needs to happen": "Hvað þarf að gerast", + "Could not create the action. Please try again.": "Ekki tókst að búa til aðgerðina. Reyndu aftur.", + "The action was created but could not be linked to the entry.": "Aðgerðin var búin til en ekki tókst að tengja hana við færsluna.", + "Project pages": "Síður verkefnisins", + "Overview": "Yfirlit", + "Risks": "Áhættur", + "Log entry": "Dagbókarfærsla", + "A short name for the entry": "Stutt heiti færslunnar", + "Project": "Verkefni", + "The project this entry belongs to": "Verkefnið sem færslan tilheyrir", + "What kind of entry this is": "Hvers konar færsla þetta er", + "When it happened": "Hvenær það gerðist", + "What happened, in Markdown": "Hvað gerðist, í Markdown", + "Whether an issue is still open": "Hvort vandamál sé enn opið", + "The people at a meeting": "Fólkið á fundi", + "Tasks that came out of this entry": "Verkefni sem spruttu af þessari færslu", + "Add risk": "Bæta við áhættu", + "Risk": "Áhætta", + "Score": "Stig", + "Response": "Viðbrögð", + "Review date": "Endurskoðunardagur", + "no longer on the project": "er ekki lengur í verkefninu", + "Edit {title}": "Breyta {title}", + "No risks recorded yet": "Engar áhættur skráðar enn", + "Add the first risk": "Bæta við fyrstu áhættunni", + "Medium": "Miðlungs", + "Mitigating": "Í mildun", + "Occurred": "Kom fram", + "Avoid": "Forðast", + "Reduce": "Draga úr", + "Transfer": "Færa annað", + "Accept": "Samþykkja", + "Highest open risks": "Hæstu opnu áhættur", + "No open risks": "Engar opnar áhættur", + "Open the risk register": "Opna áhætturegistrið", + "Edit risk": "Breyta áhættu", + "What could go wrong": "Hvað getur farið úrskeiðis", + "Likelihood": "Líkur", + "Impact": "Áhrif", + "Score {score}": "Stig {score}", + "Countermeasures": "Mótvægisaðgerðir", + "Could not save the risk. Please try again.": "Ekki tókst að vista áhættuna. Reyndu aftur.", + "Risks by likelihood and impact": "Áhættur eftir líkum og áhrifum", + "Risk scale": "Áhættukvarði", + "The levels every project scores its risks on, and where low turns into medium and high": "Stigin sem hvert verkefni metur áhættur sínar á, og hvar lágt verður miðlungs og hátt", + "Number of levels": "Fjöldi stiga", + "Likelihood labels": "Heiti fyrir líkur", + "Impact labels": "Heiti fyrir áhrif", + "Level {level}": "Stig {level}", + "Medium from score": "Miðlungs frá stigum", + "High from score": "Hátt frá stigum", + "Risk scale saved": "Áhættukvarði vistaður", + "1 risk uses level {level}. Change it first.": "1 áhætta notar stig {level}. Breyttu henni fyrst.", + "{count} risks use level {level}. Change them first.": "{count} áhættur nota stig {level}. Breyttu þeim fyrst.", + "Every level needs a label, and high must start above medium.": "Hvert stig þarf heiti, og hátt verður að byrja fyrir ofan miðlungs.", + "The project this risk belongs to": "Verkefnið sem áhættan tilheyrir", + "The cause and the effect, in Markdown": "Orsök og afleiðing, í Markdown", + "Category": "Flokkur", + "A free grouping, such as planning, budget or supplier": "Frjáls flokkun, t.d. áætlun, fjárhagsáætlun eða birgir", + "How likely it is, on the admin's risk scale": "Hversu líklegt það er, á áhættukvarða kerfisstjórans", + "How hard it would hit, on the admin's risk scale": "Hversu hart það myndi koma niður, á áhættukvarða kerfisstjórans", + "Likelihood times impact, calculated by the server": "Líkur sinnum áhrif, reiknað af þjóninum", + "Where the risk stands": "Hvar áhættan stendur", + "The person who watches this risk": "Manneskjan sem fylgist með áhættunni", + "How the project answers the risk": "Hvernig verkefnið bregst við áhættunni", + "What is done to prevent it or soften it, in Markdown": "Hvað er gert til að koma í veg fyrir hana eða milda, í Markdown", + "When to look at this risk again": "Hvenær á að skoða áhættuna aftur", + "Very low": "Mjög lágt", + "Very high": "Mjög hátt", + "All projects": "Öll verkefni" }, "plurals": {} } diff --git a/l10n/it.js b/l10n/it.js index a89a0e39..7a83eb8e 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Bacheca", "List": "Elenco", "Column": "Colonna", - "Board or list": "Bacheca o elenco" + "Board or list": "Bacheca o elenco", + "Log": "Registro", + "Add entry": "Aggiungi voce", + "Show": "Mostra", + "No actions yet": "Ancora nessuna azione", + "Closed": "Chiuso", + "Entry actions": "Azioni della voce", + "Add action": "Aggiungi azione", + "by {author}": "di {author}", + "saved {time}": "salvato {time}", + "Attendees: {names}": "Partecipanti: {names}", + "Add the first entry": "Aggiungi la prima voce", + "Issues": "Problemi", + "Lessons learned": "Lezioni apprese", + "Meetings": "Riunioni", + "Decisions": "Decisioni", + "Actions": "Azioni", + "Issue": "Problema", + "Lesson learned": "Lezione appresa", + "Meeting": "Riunione", + "Decision": "Decisione", + "About this project": "Informazioni sul progetto", + "No description yet": "Ancora nessuna descrizione", + "Start date": "Data di inizio", + "Not set": "Non impostata", + "End date": "Data di fine", + "Progress": "Avanzamento", + "No tasks yet": "Ancora nessuna attività", + "Open the board": "Apri la bacheca", + "People": "Persone", + "Owner": "Proprietario", + "Latest log entries": "Ultime voci", + "Open the log": "Apri il registro", + "{done} of {total} tasks done": "{done} di {total} attività completate", + "Edit entry": "Modifica voce", + "Type": "Tipo", + "Attendees": "Partecipanti", + "Issue is closed": "Il problema è chiuso", + "Could not save the entry. Please try again.": "Impossibile salvare la voce. Riprova.", + "From: {title}": "Da: {title}", + "What needs to happen": "Cosa bisogna fare", + "Could not create the action. Please try again.": "Impossibile creare l'azione. Riprova.", + "The action was created but could not be linked to the entry.": "L'azione è stata creata ma non è stato possibile collegarla alla voce.", + "Project pages": "Pagine del progetto", + "Overview": "Panoramica", + "Risks": "Rischi", + "Log entry": "Voce di registro", + "A short name for the entry": "Un nome breve per la voce", + "Project": "Progetto", + "The project this entry belongs to": "Il progetto a cui appartiene questa voce", + "What kind of entry this is": "Che tipo di voce è", + "When it happened": "Quando è successo", + "What happened, in Markdown": "Cosa è successo, in Markdown", + "Whether an issue is still open": "Se un problema è ancora aperto", + "The people at a meeting": "Le persone presenti a una riunione", + "Tasks that came out of this entry": "Attività nate da questa voce", + "Add risk": "Aggiungi rischio", + "Risk": "Rischio", + "Score": "Punteggio", + "Response": "Risposta", + "Review date": "Data di revisione", + "no longer on the project": "non fa più parte del progetto", + "Edit {title}": "Modifica {title}", + "No risks recorded yet": "Ancora nessun rischio registrato", + "Add the first risk": "Aggiungi il primo rischio", + "Medium": "Medio", + "Mitigating": "In mitigazione", + "Occurred": "Verificato", + "Avoid": "Evitare", + "Reduce": "Ridurre", + "Transfer": "Trasferire", + "Accept": "Accettare", + "Highest open risks": "Rischi aperti più alti", + "No open risks": "Nessun rischio aperto", + "Open the risk register": "Apri il registro dei rischi", + "Edit risk": "Modifica rischio", + "What could go wrong": "Cosa potrebbe andare storto", + "Likelihood": "Probabilità", + "Impact": "Impatto", + "Score {score}": "Punteggio {score}", + "Countermeasures": "Contromisure", + "Could not save the risk. Please try again.": "Impossibile salvare il rischio. Riprova.", + "Risks by likelihood and impact": "Rischi per probabilità e impatto", + "Risk scale": "Scala di rischio", + "The levels every project scores its risks on, and where low turns into medium and high": "I livelli su cui ogni progetto valuta i propri rischi, e dove basso diventa medio e alto", + "Number of levels": "Numero di livelli", + "Likelihood labels": "Etichette di probabilità", + "Impact labels": "Etichette di impatto", + "Level {level}": "Livello {level}", + "Medium from score": "Medio dal punteggio", + "High from score": "Alto dal punteggio", + "Risk scale saved": "Scala di rischio salvata", + "1 risk uses level {level}. Change it first.": "1 rischio usa il livello {level}. Modificalo prima.", + "{count} risks use level {level}. Change them first.": "{count} rischi usano il livello {level}. Modificali prima.", + "Every level needs a label, and high must start above medium.": "Ogni livello ha bisogno di un'etichetta, e alto deve iniziare sopra medio.", + "The project this risk belongs to": "Il progetto a cui appartiene questo rischio", + "The cause and the effect, in Markdown": "La causa e l'effetto, in Markdown", + "Category": "Categoria", + "A free grouping, such as planning, budget or supplier": "Un raggruppamento libero, come pianificazione, budget o fornitore", + "How likely it is, on the admin's risk scale": "Quanto è probabile, sulla scala di rischio dell'amministratore", + "How hard it would hit, on the admin's risk scale": "Quanto colpirebbe, sulla scala di rischio dell'amministratore", + "Likelihood times impact, calculated by the server": "Probabilità per impatto, calcolata dal server", + "Where the risk stands": "A che punto è il rischio", + "The person who watches this risk": "La persona che tiene d'occhio questo rischio", + "How the project answers the risk": "Come il progetto risponde al rischio", + "What is done to prevent it or soften it, in Markdown": "Cosa si fa per prevenirlo o attenuarlo, in Markdown", + "When to look at this risk again": "Quando riesaminare questo rischio", + "Very low": "Molto basso", + "Very high": "Molto alto", + "All projects": "Tutti i progetti" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/it.json b/l10n/it.json index d901ebda..d3a82ac8 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -308,7 +308,116 @@ "Board": "Bacheca", "List": "Elenco", "Column": "Colonna", - "Board or list": "Bacheca o elenco" + "Board or list": "Bacheca o elenco", + "Log": "Registro", + "Add entry": "Aggiungi voce", + "Show": "Mostra", + "No actions yet": "Ancora nessuna azione", + "Closed": "Chiuso", + "Entry actions": "Azioni della voce", + "Add action": "Aggiungi azione", + "by {author}": "di {author}", + "saved {time}": "salvato {time}", + "Attendees: {names}": "Partecipanti: {names}", + "Add the first entry": "Aggiungi la prima voce", + "Issues": "Problemi", + "Lessons learned": "Lezioni apprese", + "Meetings": "Riunioni", + "Decisions": "Decisioni", + "Actions": "Azioni", + "Issue": "Problema", + "Lesson learned": "Lezione appresa", + "Meeting": "Riunione", + "Decision": "Decisione", + "About this project": "Informazioni sul progetto", + "No description yet": "Ancora nessuna descrizione", + "Start date": "Data di inizio", + "Not set": "Non impostata", + "End date": "Data di fine", + "Progress": "Avanzamento", + "No tasks yet": "Ancora nessuna attività", + "Open the board": "Apri la bacheca", + "People": "Persone", + "Owner": "Proprietario", + "Latest log entries": "Ultime voci", + "Open the log": "Apri il registro", + "{done} of {total} tasks done": "{done} di {total} attività completate", + "Edit entry": "Modifica voce", + "Type": "Tipo", + "Attendees": "Partecipanti", + "Issue is closed": "Il problema è chiuso", + "Could not save the entry. Please try again.": "Impossibile salvare la voce. Riprova.", + "From: {title}": "Da: {title}", + "What needs to happen": "Cosa bisogna fare", + "Could not create the action. Please try again.": "Impossibile creare l'azione. Riprova.", + "The action was created but could not be linked to the entry.": "L'azione è stata creata ma non è stato possibile collegarla alla voce.", + "Project pages": "Pagine del progetto", + "Overview": "Panoramica", + "Risks": "Rischi", + "Log entry": "Voce di registro", + "A short name for the entry": "Un nome breve per la voce", + "Project": "Progetto", + "The project this entry belongs to": "Il progetto a cui appartiene questa voce", + "What kind of entry this is": "Che tipo di voce è", + "When it happened": "Quando è successo", + "What happened, in Markdown": "Cosa è successo, in Markdown", + "Whether an issue is still open": "Se un problema è ancora aperto", + "The people at a meeting": "Le persone presenti a una riunione", + "Tasks that came out of this entry": "Attività nate da questa voce", + "Add risk": "Aggiungi rischio", + "Risk": "Rischio", + "Score": "Punteggio", + "Response": "Risposta", + "Review date": "Data di revisione", + "no longer on the project": "non fa più parte del progetto", + "Edit {title}": "Modifica {title}", + "No risks recorded yet": "Ancora nessun rischio registrato", + "Add the first risk": "Aggiungi il primo rischio", + "Medium": "Medio", + "Mitigating": "In mitigazione", + "Occurred": "Verificato", + "Avoid": "Evitare", + "Reduce": "Ridurre", + "Transfer": "Trasferire", + "Accept": "Accettare", + "Highest open risks": "Rischi aperti più alti", + "No open risks": "Nessun rischio aperto", + "Open the risk register": "Apri il registro dei rischi", + "Edit risk": "Modifica rischio", + "What could go wrong": "Cosa potrebbe andare storto", + "Likelihood": "Probabilità", + "Impact": "Impatto", + "Score {score}": "Punteggio {score}", + "Countermeasures": "Contromisure", + "Could not save the risk. Please try again.": "Impossibile salvare il rischio. Riprova.", + "Risks by likelihood and impact": "Rischi per probabilità e impatto", + "Risk scale": "Scala di rischio", + "The levels every project scores its risks on, and where low turns into medium and high": "I livelli su cui ogni progetto valuta i propri rischi, e dove basso diventa medio e alto", + "Number of levels": "Numero di livelli", + "Likelihood labels": "Etichette di probabilità", + "Impact labels": "Etichette di impatto", + "Level {level}": "Livello {level}", + "Medium from score": "Medio dal punteggio", + "High from score": "Alto dal punteggio", + "Risk scale saved": "Scala di rischio salvata", + "1 risk uses level {level}. Change it first.": "1 rischio usa il livello {level}. Modificalo prima.", + "{count} risks use level {level}. Change them first.": "{count} rischi usano il livello {level}. Modificali prima.", + "Every level needs a label, and high must start above medium.": "Ogni livello ha bisogno di un'etichetta, e alto deve iniziare sopra medio.", + "The project this risk belongs to": "Il progetto a cui appartiene questo rischio", + "The cause and the effect, in Markdown": "La causa e l'effetto, in Markdown", + "Category": "Categoria", + "A free grouping, such as planning, budget or supplier": "Un raggruppamento libero, come pianificazione, budget o fornitore", + "How likely it is, on the admin's risk scale": "Quanto è probabile, sulla scala di rischio dell'amministratore", + "How hard it would hit, on the admin's risk scale": "Quanto colpirebbe, sulla scala di rischio dell'amministratore", + "Likelihood times impact, calculated by the server": "Probabilità per impatto, calcolata dal server", + "Where the risk stands": "A che punto è il rischio", + "The person who watches this risk": "La persona che tiene d'occhio questo rischio", + "How the project answers the risk": "Come il progetto risponde al rischio", + "What is done to prevent it or soften it, in Markdown": "Cosa si fa per prevenirlo o attenuarlo, in Markdown", + "When to look at this risk again": "Quando riesaminare questo rischio", + "Very low": "Molto basso", + "Very high": "Molto alto", + "All projects": "Tutti i progetti" }, "plurals": null } diff --git a/l10n/lb.js b/l10n/lb.js index 7e624e59..de373c98 100644 --- a/l10n/lb.js +++ b/l10n/lb.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Board", "List": "Lëscht", "Column": "Kolonn", - "Board or list": "Board oder Lëscht" + "Board or list": "Board oder Lëscht", + "Log": "Logbuch", + "Add entry": "Entrée derbäisetzen", + "Show": "Weisen", + "No actions yet": "Nach keng Aktiounen", + "Closed": "Zou", + "Entry actions": "Aktioune fir d'Entrée", + "Add action": "Aktioun derbäisetzen", + "by {author}": "vum {author}", + "saved {time}": "gespäichert {time}", + "Attendees: {names}": "Participanten: {names}", + "Add the first entry": "Déi éischt Entrée derbäisetzen", + "Issues": "Problemer", + "Lessons learned": "Erkenntnisser", + "Meetings": "Reuniounen", + "Decisions": "Decisiounen", + "Actions": "Aktiounen", + "Issue": "Problem", + "Lesson learned": "Erkenntnis", + "Meeting": "Reunioun", + "Decision": "Decisioun", + "About this project": "Iwwer dëse Projet", + "No description yet": "Nach keng Beschreiwung", + "Start date": "Startdatum", + "Not set": "Net festgeluecht", + "End date": "Schlussdatum", + "Progress": "Fortschrëtt", + "No tasks yet": "Nach keng Aufgaben", + "Open the board": "De Board opmaachen", + "People": "Leit", + "Owner": "Besëtzer", + "Latest log entries": "Lescht Entréeën", + "Open the log": "D'Logbuch opmaachen", + "{done} of {total} tasks done": "{done} vun {total} Aufgaben erleedegt", + "Edit entry": "Entrée änneren", + "Type": "Aart", + "Attendees": "Participanten", + "Issue is closed": "De Problem ass zou", + "Could not save the entry. Please try again.": "D'Entrée konnt net gespäichert ginn. Probéiert w.e.g. nach eng Kéier.", + "From: {title}": "Aus: {title}", + "What needs to happen": "Wat muss geschéien", + "Could not create the action. Please try again.": "D'Aktioun konnt net erstallt ginn. Probéiert w.e.g. nach eng Kéier.", + "The action was created but could not be linked to the entry.": "D'Aktioun gouf erstallt, konnt awer net mat der Entrée verlinkt ginn.", + "Project pages": "Projetssäiten", + "Overview": "Iwwersiicht", + "Risks": "Risiken", + "Log entry": "Logbuch-Entrée", + "A short name for the entry": "E kuerzen Numm fir d'Entrée", + "Project": "Projet", + "The project this entry belongs to": "De Projet, zu deem dës Entrée gehéiert", + "What kind of entry this is": "Wat fir eng Entrée dat ass", + "When it happened": "Wéini et geschitt ass", + "What happened, in Markdown": "Wat geschitt ass, am Markdown", + "Whether an issue is still open": "Ob e Problem nach op ass", + "The people at a meeting": "D'Leit bei enger Reunioun", + "Tasks that came out of this entry": "Aufgaben, déi aus dëser Entrée entstane sinn", + "Add risk": "Risiko derbäisetzen", + "Risk": "Risiko", + "Score": "Wäert", + "Response": "Reaktioun", + "Review date": "Iwwerpréiwungsdatum", + "no longer on the project": "net méi am Projet", + "Edit {title}": "{title} änneren", + "No risks recorded yet": "Nach keng Risiken erfaasst", + "Add the first risk": "Dat éischt Risiko derbäisetzen", + "Medium": "Mëttel", + "Mitigating": "Gëtt ofgeschwächt", + "Occurred": "Agetrueden", + "Avoid": "Vermeiden", + "Reduce": "Reduzéieren", + "Transfer": "Iwwerdroen", + "Accept": "Akzeptéieren", + "Highest open risks": "Héchst oppe Risiken", + "No open risks": "Keng oppe Risiken", + "Open the risk register": "Risikoregister opmaachen", + "Edit risk": "Risiko änneren", + "What could go wrong": "Wat kéint schifgoen", + "Likelihood": "Wahrscheinlechkeet", + "Impact": "Auswierkung", + "Score {score}": "Wäert {score}", + "Countermeasures": "Géigemoossnamen", + "Could not save the risk. Please try again.": "D'Risiko konnt net gespäichert ginn. Probéiert w.e.g. nach eng Kéier.", + "Risks by likelihood and impact": "Risiken no Wahrscheinlechkeet an Auswierkung", + "Risk scale": "Risikoskala", + "The levels every project scores its risks on, and where low turns into medium and high": "D'Stufen, op deenen all Projet seng Risike bewäert, a wou niddreg an mëttel an héich iwwergeet", + "Number of levels": "Zuel vun de Stufen", + "Likelihood labels": "Etikette fir Wahrscheinlechkeet", + "Impact labels": "Etikette fir Auswierkung", + "Level {level}": "Stuf {level}", + "Medium from score": "Mëttel ab Wäert", + "High from score": "Héich ab Wäert", + "Risk scale saved": "Risikoskala gespäichert", + "1 risk uses level {level}. Change it first.": "1 Risiko benotzt Stuf {level}. Ännert et fir d'éischt.", + "{count} risks use level {level}. Change them first.": "{count} Risike benotze Stuf {level}. Ännert se fir d'éischt.", + "Every level needs a label, and high must start above medium.": "All Stuf brauch eng Etikett, an héich muss iwwer mëttel ufänken.", + "The project this risk belongs to": "De Projet, zu deem dëst Risiko gehéiert", + "The cause and the effect, in Markdown": "Ursaach a Wierkung, am Markdown", + "Category": "Kategorie", + "A free grouping, such as planning, budget or supplier": "Eng fräi Gruppéierung, wéi Planung, Budget oder Liwwerant", + "How likely it is, on the admin's risk scale": "Wéi wahrscheinlech et ass, op der Risikoskala vum Administrator", + "How hard it would hit, on the admin's risk scale": "Wéi haart et géif treffen, op der Risikoskala vum Administrator", + "Likelihood times impact, calculated by the server": "Wahrscheinlechkeet mol Auswierkung, vum Server berechent", + "Where the risk stands": "Wou d'Risiko steet", + "The person who watches this risk": "D'Persoun, déi dëst Risiko am A behält", + "How the project answers the risk": "Wéi de Projet op d'Risiko reagéiert", + "What is done to prevent it or soften it, in Markdown": "Wat gemaach gëtt, fir et ze verhënneren oder ofzeschwächen, am Markdown", + "When to look at this risk again": "Wéini d'Risiko nees gepréift gëtt", + "Very low": "Ganz niddreg", + "Very high": "Ganz héich", + "All projects": "All Projeten" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lb.json b/l10n/lb.json index f4308da8..ddfe5cf4 100644 --- a/l10n/lb.json +++ b/l10n/lb.json @@ -308,7 +308,116 @@ "Board": "Board", "List": "Lëscht", "Column": "Kolonn", - "Board or list": "Board oder Lëscht" + "Board or list": "Board oder Lëscht", + "Log": "Logbuch", + "Add entry": "Entrée derbäisetzen", + "Show": "Weisen", + "No actions yet": "Nach keng Aktiounen", + "Closed": "Zou", + "Entry actions": "Aktioune fir d'Entrée", + "Add action": "Aktioun derbäisetzen", + "by {author}": "vum {author}", + "saved {time}": "gespäichert {time}", + "Attendees: {names}": "Participanten: {names}", + "Add the first entry": "Déi éischt Entrée derbäisetzen", + "Issues": "Problemer", + "Lessons learned": "Erkenntnisser", + "Meetings": "Reuniounen", + "Decisions": "Decisiounen", + "Actions": "Aktiounen", + "Issue": "Problem", + "Lesson learned": "Erkenntnis", + "Meeting": "Reunioun", + "Decision": "Decisioun", + "About this project": "Iwwer dëse Projet", + "No description yet": "Nach keng Beschreiwung", + "Start date": "Startdatum", + "Not set": "Net festgeluecht", + "End date": "Schlussdatum", + "Progress": "Fortschrëtt", + "No tasks yet": "Nach keng Aufgaben", + "Open the board": "De Board opmaachen", + "People": "Leit", + "Owner": "Besëtzer", + "Latest log entries": "Lescht Entréeën", + "Open the log": "D'Logbuch opmaachen", + "{done} of {total} tasks done": "{done} vun {total} Aufgaben erleedegt", + "Edit entry": "Entrée änneren", + "Type": "Aart", + "Attendees": "Participanten", + "Issue is closed": "De Problem ass zou", + "Could not save the entry. Please try again.": "D'Entrée konnt net gespäichert ginn. Probéiert w.e.g. nach eng Kéier.", + "From: {title}": "Aus: {title}", + "What needs to happen": "Wat muss geschéien", + "Could not create the action. Please try again.": "D'Aktioun konnt net erstallt ginn. Probéiert w.e.g. nach eng Kéier.", + "The action was created but could not be linked to the entry.": "D'Aktioun gouf erstallt, konnt awer net mat der Entrée verlinkt ginn.", + "Project pages": "Projetssäiten", + "Overview": "Iwwersiicht", + "Risks": "Risiken", + "Log entry": "Logbuch-Entrée", + "A short name for the entry": "E kuerzen Numm fir d'Entrée", + "Project": "Projet", + "The project this entry belongs to": "De Projet, zu deem dës Entrée gehéiert", + "What kind of entry this is": "Wat fir eng Entrée dat ass", + "When it happened": "Wéini et geschitt ass", + "What happened, in Markdown": "Wat geschitt ass, am Markdown", + "Whether an issue is still open": "Ob e Problem nach op ass", + "The people at a meeting": "D'Leit bei enger Reunioun", + "Tasks that came out of this entry": "Aufgaben, déi aus dëser Entrée entstane sinn", + "Add risk": "Risiko derbäisetzen", + "Risk": "Risiko", + "Score": "Wäert", + "Response": "Reaktioun", + "Review date": "Iwwerpréiwungsdatum", + "no longer on the project": "net méi am Projet", + "Edit {title}": "{title} änneren", + "No risks recorded yet": "Nach keng Risiken erfaasst", + "Add the first risk": "Dat éischt Risiko derbäisetzen", + "Medium": "Mëttel", + "Mitigating": "Gëtt ofgeschwächt", + "Occurred": "Agetrueden", + "Avoid": "Vermeiden", + "Reduce": "Reduzéieren", + "Transfer": "Iwwerdroen", + "Accept": "Akzeptéieren", + "Highest open risks": "Héchst oppe Risiken", + "No open risks": "Keng oppe Risiken", + "Open the risk register": "Risikoregister opmaachen", + "Edit risk": "Risiko änneren", + "What could go wrong": "Wat kéint schifgoen", + "Likelihood": "Wahrscheinlechkeet", + "Impact": "Auswierkung", + "Score {score}": "Wäert {score}", + "Countermeasures": "Géigemoossnamen", + "Could not save the risk. Please try again.": "D'Risiko konnt net gespäichert ginn. Probéiert w.e.g. nach eng Kéier.", + "Risks by likelihood and impact": "Risiken no Wahrscheinlechkeet an Auswierkung", + "Risk scale": "Risikoskala", + "The levels every project scores its risks on, and where low turns into medium and high": "D'Stufen, op deenen all Projet seng Risike bewäert, a wou niddreg an mëttel an héich iwwergeet", + "Number of levels": "Zuel vun de Stufen", + "Likelihood labels": "Etikette fir Wahrscheinlechkeet", + "Impact labels": "Etikette fir Auswierkung", + "Level {level}": "Stuf {level}", + "Medium from score": "Mëttel ab Wäert", + "High from score": "Héich ab Wäert", + "Risk scale saved": "Risikoskala gespäichert", + "1 risk uses level {level}. Change it first.": "1 Risiko benotzt Stuf {level}. Ännert et fir d'éischt.", + "{count} risks use level {level}. Change them first.": "{count} Risike benotze Stuf {level}. Ännert se fir d'éischt.", + "Every level needs a label, and high must start above medium.": "All Stuf brauch eng Etikett, an héich muss iwwer mëttel ufänken.", + "The project this risk belongs to": "De Projet, zu deem dëst Risiko gehéiert", + "The cause and the effect, in Markdown": "Ursaach a Wierkung, am Markdown", + "Category": "Kategorie", + "A free grouping, such as planning, budget or supplier": "Eng fräi Gruppéierung, wéi Planung, Budget oder Liwwerant", + "How likely it is, on the admin's risk scale": "Wéi wahrscheinlech et ass, op der Risikoskala vum Administrator", + "How hard it would hit, on the admin's risk scale": "Wéi haart et géif treffen, op der Risikoskala vum Administrator", + "Likelihood times impact, calculated by the server": "Wahrscheinlechkeet mol Auswierkung, vum Server berechent", + "Where the risk stands": "Wou d'Risiko steet", + "The person who watches this risk": "D'Persoun, déi dëst Risiko am A behält", + "How the project answers the risk": "Wéi de Projet op d'Risiko reagéiert", + "What is done to prevent it or soften it, in Markdown": "Wat gemaach gëtt, fir et ze verhënneren oder ofzeschwächen, am Markdown", + "When to look at this risk again": "Wéini d'Risiko nees gepréift gëtt", + "Very low": "Ganz niddreg", + "Very high": "Ganz héich", + "All projects": "All Projeten" }, "plurals": null } diff --git a/l10n/lt.js b/l10n/lt.js index 5f9e958b..4c674839 100644 --- a/l10n/lt.js +++ b/l10n/lt.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Lenta", "List": "Sąrašas", "Column": "Stulpelis", - "Board or list": "Lenta arba sąrašas" + "Board or list": "Lenta arba sąrašas", + "Log": "Žurnalas", + "Add entry": "Pridėti įrašą", + "Show": "Rodyti", + "No actions yet": "Veiksmų dar nėra", + "Closed": "Uždaryta", + "Entry actions": "Įrašo veiksmai", + "Add action": "Pridėti veiksmą", + "by {author}": "autorius {author}", + "saved {time}": "išsaugota {time}", + "Attendees: {names}": "Dalyviai: {names}", + "Add the first entry": "Pridėti pirmą įrašą", + "Issues": "Problemos", + "Lessons learned": "Pamokos", + "Meetings": "Susitikimai", + "Decisions": "Sprendimai", + "Actions": "Veiksmai", + "Issue": "Problema", + "Lesson learned": "Pamoka", + "Meeting": "Susitikimas", + "Decision": "Sprendimas", + "About this project": "Apie šį projektą", + "No description yet": "Aprašymo dar nėra", + "Start date": "Pradžios data", + "Not set": "Nenustatyta", + "End date": "Pabaigos data", + "Progress": "Pažanga", + "No tasks yet": "Užduočių dar nėra", + "Open the board": "Atverti lentą", + "People": "Žmonės", + "Owner": "Savininkas", + "Latest log entries": "Naujausi įrašai", + "Open the log": "Atverti žurnalą", + "{done} of {total} tasks done": "Atlikta {done} iš {total} užduočių", + "Edit entry": "Redaguoti įrašą", + "Type": "Rūšis", + "Attendees": "Dalyviai", + "Issue is closed": "Problema uždaryta", + "Could not save the entry. Please try again.": "Nepavyko išsaugoti įrašo. Bandykite dar kartą.", + "From: {title}": "Iš: {title}", + "What needs to happen": "Ką reikia padaryti", + "Could not create the action. Please try again.": "Nepavyko sukurti veiksmo. Bandykite dar kartą.", + "The action was created but could not be linked to the entry.": "Veiksmas sukurtas, bet jo nepavyko susieti su įrašu.", + "Project pages": "Projekto puslapiai", + "Overview": "Apžvalga", + "Risks": "Rizikos", + "Log entry": "Žurnalo įrašas", + "A short name for the entry": "Trumpas įrašo pavadinimas", + "Project": "Projektas", + "The project this entry belongs to": "Projektas, kuriam priklauso šis įrašas", + "What kind of entry this is": "Kokios rūšies tai įrašas", + "When it happened": "Kada tai įvyko", + "What happened, in Markdown": "Kas įvyko, Markdown formatu", + "Whether an issue is still open": "Ar problema vis dar atvira", + "The people at a meeting": "Susitikimo dalyviai", + "Tasks that came out of this entry": "Iš šio įrašo kilusios užduotys", + "Add risk": "Pridėti riziką", + "Risk": "Rizika", + "Score": "Balas", + "Response": "Atsakas", + "Review date": "Peržiūros data", + "no longer on the project": "nebėra projekte", + "Edit {title}": "Redaguoti {title}", + "No risks recorded yet": "Rizikų dar neužregistruota", + "Add the first risk": "Pridėti pirmą riziką", + "Medium": "Vidutinė", + "Mitigating": "Mažinama", + "Occurred": "Įvyko", + "Avoid": "Vengti", + "Reduce": "Mažinti", + "Transfer": "Perduoti", + "Accept": "Priimti", + "Highest open risks": "Didžiausios atviros rizikos", + "No open risks": "Atvirų rizikų nėra", + "Open the risk register": "Atverti rizikų registrą", + "Edit risk": "Redaguoti riziką", + "What could go wrong": "Kas gali nepavykti", + "Likelihood": "Tikimybė", + "Impact": "Poveikis", + "Score {score}": "Balas {score}", + "Countermeasures": "Atsakomosios priemonės", + "Could not save the risk. Please try again.": "Nepavyko išsaugoti rizikos. Bandykite dar kartą.", + "Risks by likelihood and impact": "Rizikos pagal tikimybę ir poveikį", + "Risk scale": "Rizikos skalė", + "The levels every project scores its risks on, and where low turns into medium and high": "Lygiai, kuriais kiekvienas projektas vertina rizikas, ir kur žema pereina į vidutinę ir aukštą", + "Number of levels": "Lygių skaičius", + "Likelihood labels": "Tikimybės pavadinimai", + "Impact labels": "Poveikio pavadinimai", + "Level {level}": "{level} lygis", + "Medium from score": "Vidutinė nuo balo", + "High from score": "Aukšta nuo balo", + "Risk scale saved": "Rizikos skalė išsaugota", + "1 risk uses level {level}. Change it first.": "1 rizika naudoja {level} lygį. Pirmiausia ją pakeiskite.", + "{count} risks use level {level}. Change them first.": "{count} rizikos naudoja {level} lygį. Pirmiausia jas pakeiskite.", + "Every level needs a label, and high must start above medium.": "Kiekvienam lygiui reikia pavadinimo, o aukšta turi prasidėti virš vidutinės.", + "The project this risk belongs to": "Projektas, kuriam priklauso ši rizika", + "The cause and the effect, in Markdown": "Priežastis ir pasekmė, Markdown formatu", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Laisvas grupavimas, pvz., planavimas, biudžetas ar tiekėjas", + "How likely it is, on the admin's risk scale": "Kiek tai tikėtina, pagal administratoriaus rizikos skalę", + "How hard it would hit, on the admin's risk scale": "Kaip smarkiai tai paveiktų, pagal administratoriaus rizikos skalę", + "Likelihood times impact, calculated by the server": "Tikimybė padauginta iš poveikio, apskaičiuoja serveris", + "Where the risk stands": "Kokia rizikos padėtis", + "The person who watches this risk": "Asmuo, kuris stebi šią riziką", + "How the project answers the risk": "Kaip projektas reaguoja į riziką", + "What is done to prevent it or soften it, in Markdown": "Kas daroma jai išvengti ar sušvelninti, Markdown formatu", + "When to look at this risk again": "Kada vėl peržiūrėti šią riziką", + "Very low": "Labai žema", + "Very high": "Labai aukšta", + "All projects": "Visi projektai" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lt.json b/l10n/lt.json index cf0b2d2c..db5074ec 100644 --- a/l10n/lt.json +++ b/l10n/lt.json @@ -308,7 +308,116 @@ "Board": "Lenta", "List": "Sąrašas", "Column": "Stulpelis", - "Board or list": "Lenta arba sąrašas" + "Board or list": "Lenta arba sąrašas", + "Log": "Žurnalas", + "Add entry": "Pridėti įrašą", + "Show": "Rodyti", + "No actions yet": "Veiksmų dar nėra", + "Closed": "Uždaryta", + "Entry actions": "Įrašo veiksmai", + "Add action": "Pridėti veiksmą", + "by {author}": "autorius {author}", + "saved {time}": "išsaugota {time}", + "Attendees: {names}": "Dalyviai: {names}", + "Add the first entry": "Pridėti pirmą įrašą", + "Issues": "Problemos", + "Lessons learned": "Pamokos", + "Meetings": "Susitikimai", + "Decisions": "Sprendimai", + "Actions": "Veiksmai", + "Issue": "Problema", + "Lesson learned": "Pamoka", + "Meeting": "Susitikimas", + "Decision": "Sprendimas", + "About this project": "Apie šį projektą", + "No description yet": "Aprašymo dar nėra", + "Start date": "Pradžios data", + "Not set": "Nenustatyta", + "End date": "Pabaigos data", + "Progress": "Pažanga", + "No tasks yet": "Užduočių dar nėra", + "Open the board": "Atverti lentą", + "People": "Žmonės", + "Owner": "Savininkas", + "Latest log entries": "Naujausi įrašai", + "Open the log": "Atverti žurnalą", + "{done} of {total} tasks done": "Atlikta {done} iš {total} užduočių", + "Edit entry": "Redaguoti įrašą", + "Type": "Rūšis", + "Attendees": "Dalyviai", + "Issue is closed": "Problema uždaryta", + "Could not save the entry. Please try again.": "Nepavyko išsaugoti įrašo. Bandykite dar kartą.", + "From: {title}": "Iš: {title}", + "What needs to happen": "Ką reikia padaryti", + "Could not create the action. Please try again.": "Nepavyko sukurti veiksmo. Bandykite dar kartą.", + "The action was created but could not be linked to the entry.": "Veiksmas sukurtas, bet jo nepavyko susieti su įrašu.", + "Project pages": "Projekto puslapiai", + "Overview": "Apžvalga", + "Risks": "Rizikos", + "Log entry": "Žurnalo įrašas", + "A short name for the entry": "Trumpas įrašo pavadinimas", + "Project": "Projektas", + "The project this entry belongs to": "Projektas, kuriam priklauso šis įrašas", + "What kind of entry this is": "Kokios rūšies tai įrašas", + "When it happened": "Kada tai įvyko", + "What happened, in Markdown": "Kas įvyko, Markdown formatu", + "Whether an issue is still open": "Ar problema vis dar atvira", + "The people at a meeting": "Susitikimo dalyviai", + "Tasks that came out of this entry": "Iš šio įrašo kilusios užduotys", + "Add risk": "Pridėti riziką", + "Risk": "Rizika", + "Score": "Balas", + "Response": "Atsakas", + "Review date": "Peržiūros data", + "no longer on the project": "nebėra projekte", + "Edit {title}": "Redaguoti {title}", + "No risks recorded yet": "Rizikų dar neužregistruota", + "Add the first risk": "Pridėti pirmą riziką", + "Medium": "Vidutinė", + "Mitigating": "Mažinama", + "Occurred": "Įvyko", + "Avoid": "Vengti", + "Reduce": "Mažinti", + "Transfer": "Perduoti", + "Accept": "Priimti", + "Highest open risks": "Didžiausios atviros rizikos", + "No open risks": "Atvirų rizikų nėra", + "Open the risk register": "Atverti rizikų registrą", + "Edit risk": "Redaguoti riziką", + "What could go wrong": "Kas gali nepavykti", + "Likelihood": "Tikimybė", + "Impact": "Poveikis", + "Score {score}": "Balas {score}", + "Countermeasures": "Atsakomosios priemonės", + "Could not save the risk. Please try again.": "Nepavyko išsaugoti rizikos. Bandykite dar kartą.", + "Risks by likelihood and impact": "Rizikos pagal tikimybę ir poveikį", + "Risk scale": "Rizikos skalė", + "The levels every project scores its risks on, and where low turns into medium and high": "Lygiai, kuriais kiekvienas projektas vertina rizikas, ir kur žema pereina į vidutinę ir aukštą", + "Number of levels": "Lygių skaičius", + "Likelihood labels": "Tikimybės pavadinimai", + "Impact labels": "Poveikio pavadinimai", + "Level {level}": "{level} lygis", + "Medium from score": "Vidutinė nuo balo", + "High from score": "Aukšta nuo balo", + "Risk scale saved": "Rizikos skalė išsaugota", + "1 risk uses level {level}. Change it first.": "1 rizika naudoja {level} lygį. Pirmiausia ją pakeiskite.", + "{count} risks use level {level}. Change them first.": "{count} rizikos naudoja {level} lygį. Pirmiausia jas pakeiskite.", + "Every level needs a label, and high must start above medium.": "Kiekvienam lygiui reikia pavadinimo, o aukšta turi prasidėti virš vidutinės.", + "The project this risk belongs to": "Projektas, kuriam priklauso ši rizika", + "The cause and the effect, in Markdown": "Priežastis ir pasekmė, Markdown formatu", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Laisvas grupavimas, pvz., planavimas, biudžetas ar tiekėjas", + "How likely it is, on the admin's risk scale": "Kiek tai tikėtina, pagal administratoriaus rizikos skalę", + "How hard it would hit, on the admin's risk scale": "Kaip smarkiai tai paveiktų, pagal administratoriaus rizikos skalę", + "Likelihood times impact, calculated by the server": "Tikimybė padauginta iš poveikio, apskaičiuoja serveris", + "Where the risk stands": "Kokia rizikos padėtis", + "The person who watches this risk": "Asmuo, kuris stebi šią riziką", + "How the project answers the risk": "Kaip projektas reaguoja į riziką", + "What is done to prevent it or soften it, in Markdown": "Kas daroma jai išvengti ar sušvelninti, Markdown formatu", + "When to look at this risk again": "Kada vėl peržiūrėti šią riziką", + "Very low": "Labai žema", + "Very high": "Labai aukšta", + "All projects": "Visi projektai" }, "plurals": null } diff --git a/l10n/lv.js b/l10n/lv.js index 5e961d44..78c24df4 100644 --- a/l10n/lv.js +++ b/l10n/lv.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Dēlis", "List": "Saraksts", "Column": "Kolonna", - "Board or list": "Dēlis vai saraksts" + "Board or list": "Dēlis vai saraksts", + "Log": "Žurnāls", + "Add entry": "Pievienot ierakstu", + "Show": "Rādīt", + "No actions yet": "Vēl nav darbību", + "Closed": "Slēgts", + "Entry actions": "Ieraksta darbības", + "Add action": "Pievienot darbību", + "by {author}": "autors {author}", + "saved {time}": "saglabāts {time}", + "Attendees: {names}": "Dalībnieki: {names}", + "Add the first entry": "Pievienot pirmo ierakstu", + "Issues": "Problēmas", + "Lessons learned": "Gūtās atziņas", + "Meetings": "Sanāksmes", + "Decisions": "Lēmumi", + "Actions": "Darbības", + "Issue": "Problēma", + "Lesson learned": "Gūtā atziņa", + "Meeting": "Sanāksme", + "Decision": "Lēmums", + "About this project": "Par šo projektu", + "No description yet": "Vēl nav apraksta", + "Start date": "Sākuma datums", + "Not set": "Nav iestatīts", + "End date": "Beigu datums", + "Progress": "Progress", + "No tasks yet": "Vēl nav uzdevumu", + "Open the board": "Atvērt dēli", + "People": "Cilvēki", + "Owner": "Īpašnieks", + "Latest log entries": "Jaunākie ieraksti", + "Open the log": "Atvērt žurnālu", + "{done} of {total} tasks done": "Izpildīti {done} no {total} uzdevumiem", + "Edit entry": "Rediģēt ierakstu", + "Type": "Veids", + "Attendees": "Dalībnieki", + "Issue is closed": "Problēma ir slēgta", + "Could not save the entry. Please try again.": "Ierakstu neizdevās saglabāt. Mēģiniet vēlreiz.", + "From: {title}": "No: {title}", + "What needs to happen": "Kas jāizdara", + "Could not create the action. Please try again.": "Darbību neizdevās izveidot. Mēģiniet vēlreiz.", + "The action was created but could not be linked to the entry.": "Darbība tika izveidota, bet to neizdevās sasaistīt ar ierakstu.", + "Project pages": "Projekta lapas", + "Overview": "Pārskats", + "Risks": "Riski", + "Log entry": "Žurnāla ieraksts", + "A short name for the entry": "Īss ieraksta nosaukums", + "Project": "Projekts", + "The project this entry belongs to": "Projekts, kuram pieder šis ieraksts", + "What kind of entry this is": "Kāda veida ieraksts tas ir", + "When it happened": "Kad tas notika", + "What happened, in Markdown": "Kas notika, Markdown formātā", + "Whether an issue is still open": "Vai problēma joprojām ir atvērta", + "The people at a meeting": "Cilvēki sanāksmē", + "Tasks that came out of this entry": "Uzdevumi, kas radušies no šī ieraksta", + "Add risk": "Pievienot risku", + "Risk": "Risks", + "Score": "Punkti", + "Response": "Rīcība", + "Review date": "Pārskatīšanas datums", + "no longer on the project": "vairs nav projektā", + "Edit {title}": "Rediģēt {title}", + "No risks recorded yet": "Vēl nav reģistrētu risku", + "Add the first risk": "Pievienot pirmo risku", + "Medium": "Vidējs", + "Mitigating": "Tiek mazināts", + "Occurred": "Iestājies", + "Avoid": "Izvairīties", + "Reduce": "Samazināt", + "Transfer": "Nodot", + "Accept": "Pieņemt", + "Highest open risks": "Augstākie atvērtie riski", + "No open risks": "Nav atvērtu risku", + "Open the risk register": "Atvērt risku reģistru", + "Edit risk": "Rediģēt risku", + "What could go wrong": "Kas var noiet greizi", + "Likelihood": "Varbūtība", + "Impact": "Ietekme", + "Score {score}": "Punkti {score}", + "Countermeasures": "Pretpasākumi", + "Could not save the risk. Please try again.": "Risku neizdevās saglabāt. Mēģiniet vēlreiz.", + "Risks by likelihood and impact": "Riski pēc varbūtības un ietekmes", + "Risk scale": "Risku skala", + "The levels every project scores its risks on, and where low turns into medium and high": "Līmeņi, kuros katrs projekts vērtē savus riskus, un kur zems pāriet vidējā un augstā", + "Number of levels": "Līmeņu skaits", + "Likelihood labels": "Varbūtības nosaukumi", + "Impact labels": "Ietekmes nosaukumi", + "Level {level}": "Līmenis {level}", + "Medium from score": "Vidējs no punktiem", + "High from score": "Augsts no punktiem", + "Risk scale saved": "Risku skala saglabāta", + "1 risk uses level {level}. Change it first.": "1 risks izmanto līmeni {level}. Vispirms to mainiet.", + "{count} risks use level {level}. Change them first.": "{count} riski izmanto līmeni {level}. Vispirms tos mainiet.", + "Every level needs a label, and high must start above medium.": "Katram līmenim vajag nosaukumu, un augstam jāsākas virs vidējā.", + "The project this risk belongs to": "Projekts, kuram pieder šis risks", + "The cause and the effect, in Markdown": "Cēlonis un sekas, Markdown formātā", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Brīvs grupējums, piemēram, plānošana, budžets vai piegādātājs", + "How likely it is, on the admin's risk scale": "Cik tas ir varbūtīgs, administratora risku skalā", + "How hard it would hit, on the admin's risk scale": "Cik smagi tas skartu, administratora risku skalā", + "Likelihood times impact, calculated by the server": "Varbūtība reiz ietekme, aprēķina serveris", + "Where the risk stands": "Kur risks atrodas", + "The person who watches this risk": "Persona, kas uzrauga šo risku", + "How the project answers the risk": "Kā projekts reaģē uz risku", + "What is done to prevent it or soften it, in Markdown": "Kas tiek darīts, lai to novērstu vai mazinātu, Markdown formātā", + "When to look at this risk again": "Kad risku pārskatīt atkal", + "Very low": "Ļoti zems", + "Very high": "Ļoti augsts", + "All projects": "Visi projekti" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lv.json b/l10n/lv.json index daa30d40..1c789ad1 100644 --- a/l10n/lv.json +++ b/l10n/lv.json @@ -308,7 +308,116 @@ "Board": "Dēlis", "List": "Saraksts", "Column": "Kolonna", - "Board or list": "Dēlis vai saraksts" + "Board or list": "Dēlis vai saraksts", + "Log": "Žurnāls", + "Add entry": "Pievienot ierakstu", + "Show": "Rādīt", + "No actions yet": "Vēl nav darbību", + "Closed": "Slēgts", + "Entry actions": "Ieraksta darbības", + "Add action": "Pievienot darbību", + "by {author}": "autors {author}", + "saved {time}": "saglabāts {time}", + "Attendees: {names}": "Dalībnieki: {names}", + "Add the first entry": "Pievienot pirmo ierakstu", + "Issues": "Problēmas", + "Lessons learned": "Gūtās atziņas", + "Meetings": "Sanāksmes", + "Decisions": "Lēmumi", + "Actions": "Darbības", + "Issue": "Problēma", + "Lesson learned": "Gūtā atziņa", + "Meeting": "Sanāksme", + "Decision": "Lēmums", + "About this project": "Par šo projektu", + "No description yet": "Vēl nav apraksta", + "Start date": "Sākuma datums", + "Not set": "Nav iestatīts", + "End date": "Beigu datums", + "Progress": "Progress", + "No tasks yet": "Vēl nav uzdevumu", + "Open the board": "Atvērt dēli", + "People": "Cilvēki", + "Owner": "Īpašnieks", + "Latest log entries": "Jaunākie ieraksti", + "Open the log": "Atvērt žurnālu", + "{done} of {total} tasks done": "Izpildīti {done} no {total} uzdevumiem", + "Edit entry": "Rediģēt ierakstu", + "Type": "Veids", + "Attendees": "Dalībnieki", + "Issue is closed": "Problēma ir slēgta", + "Could not save the entry. Please try again.": "Ierakstu neizdevās saglabāt. Mēģiniet vēlreiz.", + "From: {title}": "No: {title}", + "What needs to happen": "Kas jāizdara", + "Could not create the action. Please try again.": "Darbību neizdevās izveidot. Mēģiniet vēlreiz.", + "The action was created but could not be linked to the entry.": "Darbība tika izveidota, bet to neizdevās sasaistīt ar ierakstu.", + "Project pages": "Projekta lapas", + "Overview": "Pārskats", + "Risks": "Riski", + "Log entry": "Žurnāla ieraksts", + "A short name for the entry": "Īss ieraksta nosaukums", + "Project": "Projekts", + "The project this entry belongs to": "Projekts, kuram pieder šis ieraksts", + "What kind of entry this is": "Kāda veida ieraksts tas ir", + "When it happened": "Kad tas notika", + "What happened, in Markdown": "Kas notika, Markdown formātā", + "Whether an issue is still open": "Vai problēma joprojām ir atvērta", + "The people at a meeting": "Cilvēki sanāksmē", + "Tasks that came out of this entry": "Uzdevumi, kas radušies no šī ieraksta", + "Add risk": "Pievienot risku", + "Risk": "Risks", + "Score": "Punkti", + "Response": "Rīcība", + "Review date": "Pārskatīšanas datums", + "no longer on the project": "vairs nav projektā", + "Edit {title}": "Rediģēt {title}", + "No risks recorded yet": "Vēl nav reģistrētu risku", + "Add the first risk": "Pievienot pirmo risku", + "Medium": "Vidējs", + "Mitigating": "Tiek mazināts", + "Occurred": "Iestājies", + "Avoid": "Izvairīties", + "Reduce": "Samazināt", + "Transfer": "Nodot", + "Accept": "Pieņemt", + "Highest open risks": "Augstākie atvērtie riski", + "No open risks": "Nav atvērtu risku", + "Open the risk register": "Atvērt risku reģistru", + "Edit risk": "Rediģēt risku", + "What could go wrong": "Kas var noiet greizi", + "Likelihood": "Varbūtība", + "Impact": "Ietekme", + "Score {score}": "Punkti {score}", + "Countermeasures": "Pretpasākumi", + "Could not save the risk. Please try again.": "Risku neizdevās saglabāt. Mēģiniet vēlreiz.", + "Risks by likelihood and impact": "Riski pēc varbūtības un ietekmes", + "Risk scale": "Risku skala", + "The levels every project scores its risks on, and where low turns into medium and high": "Līmeņi, kuros katrs projekts vērtē savus riskus, un kur zems pāriet vidējā un augstā", + "Number of levels": "Līmeņu skaits", + "Likelihood labels": "Varbūtības nosaukumi", + "Impact labels": "Ietekmes nosaukumi", + "Level {level}": "Līmenis {level}", + "Medium from score": "Vidējs no punktiem", + "High from score": "Augsts no punktiem", + "Risk scale saved": "Risku skala saglabāta", + "1 risk uses level {level}. Change it first.": "1 risks izmanto līmeni {level}. Vispirms to mainiet.", + "{count} risks use level {level}. Change them first.": "{count} riski izmanto līmeni {level}. Vispirms tos mainiet.", + "Every level needs a label, and high must start above medium.": "Katram līmenim vajag nosaukumu, un augstam jāsākas virs vidējā.", + "The project this risk belongs to": "Projekts, kuram pieder šis risks", + "The cause and the effect, in Markdown": "Cēlonis un sekas, Markdown formātā", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Brīvs grupējums, piemēram, plānošana, budžets vai piegādātājs", + "How likely it is, on the admin's risk scale": "Cik tas ir varbūtīgs, administratora risku skalā", + "How hard it would hit, on the admin's risk scale": "Cik smagi tas skartu, administratora risku skalā", + "Likelihood times impact, calculated by the server": "Varbūtība reiz ietekme, aprēķina serveris", + "Where the risk stands": "Kur risks atrodas", + "The person who watches this risk": "Persona, kas uzrauga šo risku", + "How the project answers the risk": "Kā projekts reaģē uz risku", + "What is done to prevent it or soften it, in Markdown": "Kas tiek darīts, lai to novērstu vai mazinātu, Markdown formātā", + "When to look at this risk again": "Kad risku pārskatīt atkal", + "Very low": "Ļoti zems", + "Very high": "Ļoti augsts", + "All projects": "Visi projekti" }, "plurals": null } diff --git a/l10n/mk.js b/l10n/mk.js index 2e9e5217..b43f150b 100644 --- a/l10n/mk.js +++ b/l10n/mk.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Табла", "List": "Листа", "Column": "Колона", - "Board or list": "Табла или листа" + "Board or list": "Табла или листа", + "Log": "Дневник", + "Add entry": "Додај запис", + "Show": "Прикажи", + "No actions yet": "Сѐ уште нема дејства", + "Closed": "Затворено", + "Entry actions": "Дејства на записот", + "Add action": "Додај дејство", + "by {author}": "од {author}", + "saved {time}": "зачувано {time}", + "Attendees: {names}": "Присутни: {names}", + "Add the first entry": "Додај го првиот запис", + "Issues": "Проблеми", + "Lessons learned": "Научени лекции", + "Meetings": "Состаноци", + "Decisions": "Одлуки", + "Actions": "Дејства", + "Issue": "Проблем", + "Lesson learned": "Научена лекција", + "Meeting": "Состанок", + "Decision": "Одлука", + "About this project": "За овој проект", + "No description yet": "Сѐ уште нема опис", + "Start date": "Датум на почеток", + "Not set": "Не е поставено", + "End date": "Датум на крај", + "Progress": "Напредок", + "No tasks yet": "Сѐ уште нема задачи", + "Open the board": "Отвори ја таблата", + "People": "Луѓе", + "Owner": "Сопственик", + "Latest log entries": "Најнови записи", + "Open the log": "Отвори го дневникот", + "{done} of {total} tasks done": "Завршени {done} од {total} задачи", + "Edit entry": "Уреди запис", + "Type": "Вид", + "Attendees": "Присутни", + "Issue is closed": "Проблемот е затворен", + "Could not save the entry. Please try again.": "Записот не може да се зачува. Обидете се повторно.", + "From: {title}": "Од: {title}", + "What needs to happen": "Што треба да се направи", + "Could not create the action. Please try again.": "Дејството не може да се создаде. Обидете се повторно.", + "The action was created but could not be linked to the entry.": "Дејството е создадено, но не може да се поврзе со записот.", + "Project pages": "Страници на проектот", + "Overview": "Преглед", + "Risks": "Ризици", + "Log entry": "Запис во дневник", + "A short name for the entry": "Краток назив за записот", + "Project": "Проект", + "The project this entry belongs to": "Проектот на кој му припаѓа записот", + "What kind of entry this is": "Каков вид запис е ова", + "When it happened": "Кога се случило", + "What happened, in Markdown": "Што се случило, во Markdown", + "Whether an issue is still open": "Дали проблемот е сѐ уште отворен", + "The people at a meeting": "Луѓето на состанок", + "Tasks that came out of this entry": "Задачи што произлегоа од овој запис", + "Add risk": "Додај ризик", + "Risk": "Ризик", + "Score": "Оценка", + "Response": "Одговор", + "Review date": "Датум на преглед", + "no longer on the project": "повеќе не е во проектот", + "Edit {title}": "Уреди {title}", + "No risks recorded yet": "Сѐ уште нема запишани ризици", + "Add the first risk": "Додај го првиот ризик", + "Medium": "Средно", + "Mitigating": "Се ублажува", + "Occurred": "Настапи", + "Avoid": "Избегни", + "Reduce": "Намали", + "Transfer": "Префрли", + "Accept": "Прифати", + "Highest open risks": "Највисоки отворени ризици", + "No open risks": "Нема отворени ризици", + "Open the risk register": "Отвори го регистарот на ризици", + "Edit risk": "Уреди ризик", + "What could go wrong": "Што може да тргне наопаку", + "Likelihood": "Веројатност", + "Impact": "Влијание", + "Score {score}": "Оценка {score}", + "Countermeasures": "Противмерки", + "Could not save the risk. Please try again.": "Ризикот не може да се зачува. Обидете се повторно.", + "Risks by likelihood and impact": "Ризици според веројатност и влијание", + "Risk scale": "Скала на ризик", + "The levels every project scores its risks on, and where low turns into medium and high": "Нивоата на кои секој проект ги оценува своите ризици, и каде ниско преминува во средно и високо", + "Number of levels": "Број на нивоа", + "Likelihood labels": "Ознаки за веројатност", + "Impact labels": "Ознаки за влијание", + "Level {level}": "Ниво {level}", + "Medium from score": "Средно од оценка", + "High from score": "Високо од оценка", + "Risk scale saved": "Скалата на ризик е зачувана", + "1 risk uses level {level}. Change it first.": "1 ризик го користи нивото {level}. Прво сменете го.", + "{count} risks use level {level}. Change them first.": "Ризици што го користат нивото {level}: {count}. Прво сменете ги.", + "Every level needs a label, and high must start above medium.": "Секое ниво треба ознака, а високо мора да почне над средно.", + "The project this risk belongs to": "Проектот на кој му припаѓа ризикот", + "The cause and the effect, in Markdown": "Причината и последицата, во Markdown", + "Category": "Категорија", + "A free grouping, such as planning, budget or supplier": "Слободно групирање, на пр. планирање, буџет или добавувач", + "How likely it is, on the admin's risk scale": "Колку е веројатно, на скалата на ризик на администраторот", + "How hard it would hit, on the admin's risk scale": "Колку силно би удрило, на скалата на ризик на администраторот", + "Likelihood times impact, calculated by the server": "Веројатност по влијание, пресметано од серверот", + "Where the risk stands": "Каде е ризикот", + "The person who watches this risk": "Лицето што го следи ризикот", + "How the project answers the risk": "Како проектот одговара на ризикот", + "What is done to prevent it or soften it, in Markdown": "Што се прави за да се спречи или ублажи, во Markdown", + "When to look at this risk again": "Кога повторно да се прегледа ризикот", + "Very low": "Многу ниско", + "Very high": "Многу високо", + "All projects": "Сите проекти" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/mk.json b/l10n/mk.json index 9d0f71a8..b16687fb 100644 --- a/l10n/mk.json +++ b/l10n/mk.json @@ -308,7 +308,116 @@ "Board": "Табла", "List": "Листа", "Column": "Колона", - "Board or list": "Табла или листа" + "Board or list": "Табла или листа", + "Log": "Дневник", + "Add entry": "Додај запис", + "Show": "Прикажи", + "No actions yet": "Сѐ уште нема дејства", + "Closed": "Затворено", + "Entry actions": "Дејства на записот", + "Add action": "Додај дејство", + "by {author}": "од {author}", + "saved {time}": "зачувано {time}", + "Attendees: {names}": "Присутни: {names}", + "Add the first entry": "Додај го првиот запис", + "Issues": "Проблеми", + "Lessons learned": "Научени лекции", + "Meetings": "Состаноци", + "Decisions": "Одлуки", + "Actions": "Дејства", + "Issue": "Проблем", + "Lesson learned": "Научена лекција", + "Meeting": "Состанок", + "Decision": "Одлука", + "About this project": "За овој проект", + "No description yet": "Сѐ уште нема опис", + "Start date": "Датум на почеток", + "Not set": "Не е поставено", + "End date": "Датум на крај", + "Progress": "Напредок", + "No tasks yet": "Сѐ уште нема задачи", + "Open the board": "Отвори ја таблата", + "People": "Луѓе", + "Owner": "Сопственик", + "Latest log entries": "Најнови записи", + "Open the log": "Отвори го дневникот", + "{done} of {total} tasks done": "Завршени {done} од {total} задачи", + "Edit entry": "Уреди запис", + "Type": "Вид", + "Attendees": "Присутни", + "Issue is closed": "Проблемот е затворен", + "Could not save the entry. Please try again.": "Записот не може да се зачува. Обидете се повторно.", + "From: {title}": "Од: {title}", + "What needs to happen": "Што треба да се направи", + "Could not create the action. Please try again.": "Дејството не може да се создаде. Обидете се повторно.", + "The action was created but could not be linked to the entry.": "Дејството е создадено, но не може да се поврзе со записот.", + "Project pages": "Страници на проектот", + "Overview": "Преглед", + "Risks": "Ризици", + "Log entry": "Запис во дневник", + "A short name for the entry": "Краток назив за записот", + "Project": "Проект", + "The project this entry belongs to": "Проектот на кој му припаѓа записот", + "What kind of entry this is": "Каков вид запис е ова", + "When it happened": "Кога се случило", + "What happened, in Markdown": "Што се случило, во Markdown", + "Whether an issue is still open": "Дали проблемот е сѐ уште отворен", + "The people at a meeting": "Луѓето на состанок", + "Tasks that came out of this entry": "Задачи што произлегоа од овој запис", + "Add risk": "Додај ризик", + "Risk": "Ризик", + "Score": "Оценка", + "Response": "Одговор", + "Review date": "Датум на преглед", + "no longer on the project": "повеќе не е во проектот", + "Edit {title}": "Уреди {title}", + "No risks recorded yet": "Сѐ уште нема запишани ризици", + "Add the first risk": "Додај го првиот ризик", + "Medium": "Средно", + "Mitigating": "Се ублажува", + "Occurred": "Настапи", + "Avoid": "Избегни", + "Reduce": "Намали", + "Transfer": "Префрли", + "Accept": "Прифати", + "Highest open risks": "Највисоки отворени ризици", + "No open risks": "Нема отворени ризици", + "Open the risk register": "Отвори го регистарот на ризици", + "Edit risk": "Уреди ризик", + "What could go wrong": "Што може да тргне наопаку", + "Likelihood": "Веројатност", + "Impact": "Влијание", + "Score {score}": "Оценка {score}", + "Countermeasures": "Противмерки", + "Could not save the risk. Please try again.": "Ризикот не може да се зачува. Обидете се повторно.", + "Risks by likelihood and impact": "Ризици според веројатност и влијание", + "Risk scale": "Скала на ризик", + "The levels every project scores its risks on, and where low turns into medium and high": "Нивоата на кои секој проект ги оценува своите ризици, и каде ниско преминува во средно и високо", + "Number of levels": "Број на нивоа", + "Likelihood labels": "Ознаки за веројатност", + "Impact labels": "Ознаки за влијание", + "Level {level}": "Ниво {level}", + "Medium from score": "Средно од оценка", + "High from score": "Високо од оценка", + "Risk scale saved": "Скалата на ризик е зачувана", + "1 risk uses level {level}. Change it first.": "1 ризик го користи нивото {level}. Прво сменете го.", + "{count} risks use level {level}. Change them first.": "Ризици што го користат нивото {level}: {count}. Прво сменете ги.", + "Every level needs a label, and high must start above medium.": "Секое ниво треба ознака, а високо мора да почне над средно.", + "The project this risk belongs to": "Проектот на кој му припаѓа ризикот", + "The cause and the effect, in Markdown": "Причината и последицата, во Markdown", + "Category": "Категорија", + "A free grouping, such as planning, budget or supplier": "Слободно групирање, на пр. планирање, буџет или добавувач", + "How likely it is, on the admin's risk scale": "Колку е веројатно, на скалата на ризик на администраторот", + "How hard it would hit, on the admin's risk scale": "Колку силно би удрило, на скалата на ризик на администраторот", + "Likelihood times impact, calculated by the server": "Веројатност по влијание, пресметано од серверот", + "Where the risk stands": "Каде е ризикот", + "The person who watches this risk": "Лицето што го следи ризикот", + "How the project answers the risk": "Како проектот одговара на ризикот", + "What is done to prevent it or soften it, in Markdown": "Што се прави за да се спречи или ублажи, во Markdown", + "When to look at this risk again": "Кога повторно да се прегледа ризикот", + "Very low": "Многу ниско", + "Very high": "Многу високо", + "All projects": "Сите проекти" }, "plurals": null } diff --git a/l10n/mt.js b/l10n/mt.js index ddb2f30a..ca8e9c77 100644 --- a/l10n/mt.js +++ b/l10n/mt.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Bord", "List": "Lista", "Column": "Kolonna", - "Board or list": "Bord jew lista" + "Board or list": "Bord jew lista", + "Log": "Log", + "Add entry": "Żid daħla", + "Show": "Uri", + "No actions yet": "Għad m'hemm l-ebda azzjoni", + "Closed": "Magħluq", + "Entry actions": "Azzjonijiet tad-daħla", + "Add action": "Żid azzjoni", + "by {author}": "minn {author}", + "saved {time}": "issejvjat {time}", + "Attendees: {names}": "Preżenti: {names}", + "Add the first entry": "Żid l-ewwel daħla", + "Issues": "Kwistjonijiet", + "Lessons learned": "Lezzjonijiet mitgħallma", + "Meetings": "Laqgħat", + "Decisions": "Deċiżjonijiet", + "Actions": "Azzjonijiet", + "Issue": "Kwistjoni", + "Lesson learned": "Lezzjoni mitgħallma", + "Meeting": "Laqgħa", + "Decision": "Deċiżjoni", + "About this project": "Dwar dan il-proġett", + "No description yet": "Għad m'hemmx deskrizzjoni", + "Start date": "Data tal-bidu", + "Not set": "Mhux issettjat", + "End date": "Data tat-tmiem", + "Progress": "Progress", + "No tasks yet": "Għad m'hemmx kompiti", + "Open the board": "Iftaħ il-bord", + "People": "Nies", + "Owner": "Sid", + "Latest log entries": "L-aħħar daħliet", + "Open the log": "Iftaħ il-log", + "{done} of {total} tasks done": "{done} minn {total} kompiti lesti", + "Edit entry": "Editja d-daħla", + "Type": "Tip", + "Attendees": "Preżenti", + "Issue is closed": "Il-kwistjoni hija magħluqa", + "Could not save the entry. Please try again.": "Id-daħla ma setgħetx tiġi ssejvjata. Erġa' pprova.", + "From: {title}": "Minn: {title}", + "What needs to happen": "X'għandu jsir", + "Could not create the action. Please try again.": "L-azzjoni ma setgħetx tinħoloq. Erġa' pprova.", + "The action was created but could not be linked to the entry.": "L-azzjoni nħolqot iżda ma setgħetx tiġi marbuta mad-daħla.", + "Project pages": "Paġni tal-proġett", + "Overview": "Ħarsa ġenerali", + "Risks": "Riskji", + "Log entry": "Daħla tal-log", + "A short name for the entry": "Isem qasir għad-daħla", + "Project": "Proġett", + "The project this entry belongs to": "Il-proġett li għalih tappartjeni din id-daħla", + "What kind of entry this is": "X'tip ta' daħla hija", + "When it happened": "Meta ġara", + "What happened, in Markdown": "X'ġara, f'Markdown", + "Whether an issue is still open": "Jekk kwistjoni għadhiex miftuħa", + "The people at a meeting": "In-nies f'laqgħa", + "Tasks that came out of this entry": "Kompiti li ħarġu minn din id-daħla", + "Add risk": "Żid riskju", + "Risk": "Riskju", + "Score": "Punteġġ", + "Response": "Rispons", + "Review date": "Data tar-reviżjoni", + "no longer on the project": "m'għadux fil-proġett", + "Edit {title}": "Editja {title}", + "No risks recorded yet": "Għad ma ġie rreġistrat l-ebda riskju", + "Add the first risk": "Żid l-ewwel riskju", + "Medium": "Medju", + "Mitigating": "Qed jittaffa", + "Occurred": "Seħħ", + "Avoid": "Evita", + "Reduce": "Naqqas", + "Transfer": "Ittrasferixxi", + "Accept": "Aċċetta", + "Highest open risks": "L-ogħla riskji miftuħa", + "No open risks": "L-ebda riskju miftuħ", + "Open the risk register": "Iftaħ ir-reġistru tar-riskji", + "Edit risk": "Editja r-riskju", + "What could go wrong": "X'jista' jmur ħażin", + "Likelihood": "Probabbiltà", + "Impact": "Impatt", + "Score {score}": "Punteġġ {score}", + "Countermeasures": "Kontromiżuri", + "Could not save the risk. Please try again.": "Ir-riskju ma setax jiġi ssejvjat. Erġa' pprova.", + "Risks by likelihood and impact": "Riskji skont il-probabbiltà u l-impatt", + "Risk scale": "Skala tar-riskju", + "The levels every project scores its risks on, and where low turns into medium and high": "Il-livelli li fuqhom kull proġett jagħti punteġġ lir-riskji tiegħu, u fejn baxx isir medju u għoli", + "Number of levels": "Numru ta' livelli", + "Likelihood labels": "Tikketti tal-probabbiltà", + "Impact labels": "Tikketti tal-impatt", + "Level {level}": "Livell {level}", + "Medium from score": "Medju mill-punteġġ", + "High from score": "Għoli mill-punteġġ", + "Risk scale saved": "L-iskala tar-riskju ġiet issejvjata", + "1 risk uses level {level}. Change it first.": "Riskju 1 juża l-livell {level}. Ibdlu l-ewwel.", + "{count} risks use level {level}. Change them first.": "{count} riskji jużaw il-livell {level}. Ibdilhom l-ewwel.", + "Every level needs a label, and high must start above medium.": "Kull livell jeħtieġ tikketta, u għoli għandu jibda 'l fuq minn medju.", + "The project this risk belongs to": "Il-proġett li għalih jappartjeni dan ir-riskju", + "The cause and the effect, in Markdown": "Il-kawża u l-effett, f'Markdown", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Raggruppament liberu, bħal ippjanar, baġit jew fornitur", + "How likely it is, on the admin's risk scale": "Kemm hu probabbli, fuq l-iskala tar-riskju tal-amministratur", + "How hard it would hit, on the admin's risk scale": "Kemm jolqot ħażin, fuq l-iskala tar-riskju tal-amministratur", + "Likelihood times impact, calculated by the server": "Il-probabbiltà multiplikata bl-impatt, ikkalkulata mis-server", + "Where the risk stands": "Fejn jinsab ir-riskju", + "The person who watches this risk": "Il-persuna li ssegwi dan ir-riskju", + "How the project answers the risk": "Kif il-proġett jirrispondi għar-riskju", + "What is done to prevent it or soften it, in Markdown": "X'qed isir biex jiġi evitat jew imtaffi, f'Markdown", + "When to look at this risk again": "Meta għandu jerġa' jiġi rivedut dan ir-riskju", + "Very low": "Baxx ħafna", + "Very high": "Għoli ħafna", + "All projects": "Il-proġetti kollha" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/mt.json b/l10n/mt.json index 13a001bd..72283f29 100644 --- a/l10n/mt.json +++ b/l10n/mt.json @@ -308,7 +308,116 @@ "Board": "Bord", "List": "Lista", "Column": "Kolonna", - "Board or list": "Bord jew lista" + "Board or list": "Bord jew lista", + "Log": "Log", + "Add entry": "Żid daħla", + "Show": "Uri", + "No actions yet": "Għad m'hemm l-ebda azzjoni", + "Closed": "Magħluq", + "Entry actions": "Azzjonijiet tad-daħla", + "Add action": "Żid azzjoni", + "by {author}": "minn {author}", + "saved {time}": "issejvjat {time}", + "Attendees: {names}": "Preżenti: {names}", + "Add the first entry": "Żid l-ewwel daħla", + "Issues": "Kwistjonijiet", + "Lessons learned": "Lezzjonijiet mitgħallma", + "Meetings": "Laqgħat", + "Decisions": "Deċiżjonijiet", + "Actions": "Azzjonijiet", + "Issue": "Kwistjoni", + "Lesson learned": "Lezzjoni mitgħallma", + "Meeting": "Laqgħa", + "Decision": "Deċiżjoni", + "About this project": "Dwar dan il-proġett", + "No description yet": "Għad m'hemmx deskrizzjoni", + "Start date": "Data tal-bidu", + "Not set": "Mhux issettjat", + "End date": "Data tat-tmiem", + "Progress": "Progress", + "No tasks yet": "Għad m'hemmx kompiti", + "Open the board": "Iftaħ il-bord", + "People": "Nies", + "Owner": "Sid", + "Latest log entries": "L-aħħar daħliet", + "Open the log": "Iftaħ il-log", + "{done} of {total} tasks done": "{done} minn {total} kompiti lesti", + "Edit entry": "Editja d-daħla", + "Type": "Tip", + "Attendees": "Preżenti", + "Issue is closed": "Il-kwistjoni hija magħluqa", + "Could not save the entry. Please try again.": "Id-daħla ma setgħetx tiġi ssejvjata. Erġa' pprova.", + "From: {title}": "Minn: {title}", + "What needs to happen": "X'għandu jsir", + "Could not create the action. Please try again.": "L-azzjoni ma setgħetx tinħoloq. Erġa' pprova.", + "The action was created but could not be linked to the entry.": "L-azzjoni nħolqot iżda ma setgħetx tiġi marbuta mad-daħla.", + "Project pages": "Paġni tal-proġett", + "Overview": "Ħarsa ġenerali", + "Risks": "Riskji", + "Log entry": "Daħla tal-log", + "A short name for the entry": "Isem qasir għad-daħla", + "Project": "Proġett", + "The project this entry belongs to": "Il-proġett li għalih tappartjeni din id-daħla", + "What kind of entry this is": "X'tip ta' daħla hija", + "When it happened": "Meta ġara", + "What happened, in Markdown": "X'ġara, f'Markdown", + "Whether an issue is still open": "Jekk kwistjoni għadhiex miftuħa", + "The people at a meeting": "In-nies f'laqgħa", + "Tasks that came out of this entry": "Kompiti li ħarġu minn din id-daħla", + "Add risk": "Żid riskju", + "Risk": "Riskju", + "Score": "Punteġġ", + "Response": "Rispons", + "Review date": "Data tar-reviżjoni", + "no longer on the project": "m'għadux fil-proġett", + "Edit {title}": "Editja {title}", + "No risks recorded yet": "Għad ma ġie rreġistrat l-ebda riskju", + "Add the first risk": "Żid l-ewwel riskju", + "Medium": "Medju", + "Mitigating": "Qed jittaffa", + "Occurred": "Seħħ", + "Avoid": "Evita", + "Reduce": "Naqqas", + "Transfer": "Ittrasferixxi", + "Accept": "Aċċetta", + "Highest open risks": "L-ogħla riskji miftuħa", + "No open risks": "L-ebda riskju miftuħ", + "Open the risk register": "Iftaħ ir-reġistru tar-riskji", + "Edit risk": "Editja r-riskju", + "What could go wrong": "X'jista' jmur ħażin", + "Likelihood": "Probabbiltà", + "Impact": "Impatt", + "Score {score}": "Punteġġ {score}", + "Countermeasures": "Kontromiżuri", + "Could not save the risk. Please try again.": "Ir-riskju ma setax jiġi ssejvjat. Erġa' pprova.", + "Risks by likelihood and impact": "Riskji skont il-probabbiltà u l-impatt", + "Risk scale": "Skala tar-riskju", + "The levels every project scores its risks on, and where low turns into medium and high": "Il-livelli li fuqhom kull proġett jagħti punteġġ lir-riskji tiegħu, u fejn baxx isir medju u għoli", + "Number of levels": "Numru ta' livelli", + "Likelihood labels": "Tikketti tal-probabbiltà", + "Impact labels": "Tikketti tal-impatt", + "Level {level}": "Livell {level}", + "Medium from score": "Medju mill-punteġġ", + "High from score": "Għoli mill-punteġġ", + "Risk scale saved": "L-iskala tar-riskju ġiet issejvjata", + "1 risk uses level {level}. Change it first.": "Riskju 1 juża l-livell {level}. Ibdlu l-ewwel.", + "{count} risks use level {level}. Change them first.": "{count} riskji jużaw il-livell {level}. Ibdilhom l-ewwel.", + "Every level needs a label, and high must start above medium.": "Kull livell jeħtieġ tikketta, u għoli għandu jibda 'l fuq minn medju.", + "The project this risk belongs to": "Il-proġett li għalih jappartjeni dan ir-riskju", + "The cause and the effect, in Markdown": "Il-kawża u l-effett, f'Markdown", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Raggruppament liberu, bħal ippjanar, baġit jew fornitur", + "How likely it is, on the admin's risk scale": "Kemm hu probabbli, fuq l-iskala tar-riskju tal-amministratur", + "How hard it would hit, on the admin's risk scale": "Kemm jolqot ħażin, fuq l-iskala tar-riskju tal-amministratur", + "Likelihood times impact, calculated by the server": "Il-probabbiltà multiplikata bl-impatt, ikkalkulata mis-server", + "Where the risk stands": "Fejn jinsab ir-riskju", + "The person who watches this risk": "Il-persuna li ssegwi dan ir-riskju", + "How the project answers the risk": "Kif il-proġett jirrispondi għar-riskju", + "What is done to prevent it or soften it, in Markdown": "X'qed isir biex jiġi evitat jew imtaffi, f'Markdown", + "When to look at this risk again": "Meta għandu jerġa' jiġi rivedut dan ir-riskju", + "Very low": "Baxx ħafna", + "Very high": "Għoli ħafna", + "All projects": "Il-proġetti kollha" }, "plurals": {} } diff --git a/l10n/nb.js b/l10n/nb.js index b377e419..fd041c3e 100644 --- a/l10n/nb.js +++ b/l10n/nb.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tavle", "List": "Liste", "Column": "Kolonne", - "Board or list": "Tavle eller liste" + "Board or list": "Tavle eller liste", + "Log": "Logg", + "Add entry": "Legg til oppføring", + "Show": "Vis", + "No actions yet": "Ingen tiltak ennå", + "Closed": "Lukket", + "Entry actions": "Handlinger for oppføringen", + "Add action": "Legg til tiltak", + "by {author}": "av {author}", + "saved {time}": "lagret {time}", + "Attendees: {names}": "Deltakere: {names}", + "Add the first entry": "Legg til den første oppføringen", + "Issues": "Problemer", + "Lessons learned": "Lærdommer", + "Meetings": "Møter", + "Decisions": "Beslutninger", + "Actions": "Tiltak", + "Issue": "Problem", + "Lesson learned": "Lærdom", + "Meeting": "Møte", + "Decision": "Beslutning", + "About this project": "Om dette prosjektet", + "No description yet": "Ingen beskrivelse ennå", + "Start date": "Startdato", + "Not set": "Ikke angitt", + "End date": "Sluttdato", + "Progress": "Fremdrift", + "No tasks yet": "Ingen oppgaver ennå", + "Open the board": "Åpne tavlen", + "People": "Personer", + "Owner": "Eier", + "Latest log entries": "Siste oppføringer", + "Open the log": "Åpne loggen", + "{done} of {total} tasks done": "{done} av {total} oppgaver fullført", + "Edit entry": "Rediger oppføring", + "Type": "Type", + "Attendees": "Deltakere", + "Issue is closed": "Problemet er lukket", + "Could not save the entry. Please try again.": "Oppføringen kunne ikke lagres. Prøv igjen.", + "From: {title}": "Fra: {title}", + "What needs to happen": "Hva som må gjøres", + "Could not create the action. Please try again.": "Tiltaket kunne ikke opprettes. Prøv igjen.", + "The action was created but could not be linked to the entry.": "Tiltaket ble opprettet, men kunne ikke knyttes til oppføringen.", + "Project pages": "Prosjektsider", + "Overview": "Oversikt", + "Risks": "Risikoer", + "Log entry": "Loggoppføring", + "A short name for the entry": "Et kort navn på oppføringen", + "Project": "Prosjekt", + "The project this entry belongs to": "Prosjektet oppføringen hører til", + "What kind of entry this is": "Hva slags oppføring dette er", + "When it happened": "Når det skjedde", + "What happened, in Markdown": "Hva som skjedde, i Markdown", + "Whether an issue is still open": "Om et problem fortsatt er åpent", + "The people at a meeting": "Personene på et møte", + "Tasks that came out of this entry": "Oppgaver som kom ut av denne oppføringen", + "Add risk": "Legg til risiko", + "Risk": "Risiko", + "Score": "Poeng", + "Response": "Håndtering", + "Review date": "Gjennomgangsdato", + "no longer on the project": "er ikke lenger med i prosjektet", + "Edit {title}": "Rediger {title}", + "No risks recorded yet": "Ingen risikoer registrert ennå", + "Add the first risk": "Legg til den første risikoen", + "Medium": "Middels", + "Mitigating": "Reduseres", + "Occurred": "Inntruffet", + "Avoid": "Unngå", + "Reduce": "Redusere", + "Transfer": "Overføre", + "Accept": "Akseptere", + "Highest open risks": "Høyeste åpne risikoer", + "No open risks": "Ingen åpne risikoer", + "Open the risk register": "Åpne risikoregisteret", + "Edit risk": "Rediger risiko", + "What could go wrong": "Hva som kan gå galt", + "Likelihood": "Sannsynlighet", + "Impact": "Konsekvens", + "Score {score}": "Poeng {score}", + "Countermeasures": "Tiltak", + "Could not save the risk. Please try again.": "Risikoen kunne ikke lagres. Prøv igjen.", + "Risks by likelihood and impact": "Risikoer etter sannsynlighet og konsekvens", + "Risk scale": "Risikoskala", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivåene hvert prosjekt vurderer risikoene sine på, og hvor lav går over i middels og høy", + "Number of levels": "Antall nivåer", + "Likelihood labels": "Etiketter for sannsynlighet", + "Impact labels": "Etiketter for konsekvens", + "Level {level}": "Nivå {level}", + "Medium from score": "Middels fra poeng", + "High from score": "Høy fra poeng", + "Risk scale saved": "Risikoskala lagret", + "1 risk uses level {level}. Change it first.": "1 risiko bruker nivå {level}. Endre den først.", + "{count} risks use level {level}. Change them first.": "{count} risikoer bruker nivå {level}. Endre dem først.", + "Every level needs a label, and high must start above medium.": "Hvert nivå trenger en etikett, og høy må starte over middels.", + "The project this risk belongs to": "Prosjektet risikoen hører til", + "The cause and the effect, in Markdown": "Årsak og virkning, i Markdown", + "Category": "Kategori", + "A free grouping, such as planning, budget or supplier": "En fri gruppering, for eksempel planlegging, budsjett eller leverandør", + "How likely it is, on the admin's risk scale": "Hvor sannsynlig det er, på administratorens risikoskala", + "How hard it would hit, on the admin's risk scale": "Hvor hardt det ville ramme, på administratorens risikoskala", + "Likelihood times impact, calculated by the server": "Sannsynlighet ganger konsekvens, beregnet av serveren", + "Where the risk stands": "Hvor risikoen står", + "The person who watches this risk": "Personen som følger med på risikoen", + "How the project answers the risk": "Hvordan prosjektet håndterer risikoen", + "What is done to prevent it or soften it, in Markdown": "Hva som gjøres for å hindre eller dempe den, i Markdown", + "When to look at this risk again": "Når risikoen skal vurderes igjen", + "Very low": "Svært lav", + "Very high": "Svært høy", + "All projects": "Alle prosjekter" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nb.json b/l10n/nb.json index dda9f668..efc01838 100644 --- a/l10n/nb.json +++ b/l10n/nb.json @@ -308,7 +308,116 @@ "Board": "Tavle", "List": "Liste", "Column": "Kolonne", - "Board or list": "Tavle eller liste" + "Board or list": "Tavle eller liste", + "Log": "Logg", + "Add entry": "Legg til oppføring", + "Show": "Vis", + "No actions yet": "Ingen tiltak ennå", + "Closed": "Lukket", + "Entry actions": "Handlinger for oppføringen", + "Add action": "Legg til tiltak", + "by {author}": "av {author}", + "saved {time}": "lagret {time}", + "Attendees: {names}": "Deltakere: {names}", + "Add the first entry": "Legg til den første oppføringen", + "Issues": "Problemer", + "Lessons learned": "Lærdommer", + "Meetings": "Møter", + "Decisions": "Beslutninger", + "Actions": "Tiltak", + "Issue": "Problem", + "Lesson learned": "Lærdom", + "Meeting": "Møte", + "Decision": "Beslutning", + "About this project": "Om dette prosjektet", + "No description yet": "Ingen beskrivelse ennå", + "Start date": "Startdato", + "Not set": "Ikke angitt", + "End date": "Sluttdato", + "Progress": "Fremdrift", + "No tasks yet": "Ingen oppgaver ennå", + "Open the board": "Åpne tavlen", + "People": "Personer", + "Owner": "Eier", + "Latest log entries": "Siste oppføringer", + "Open the log": "Åpne loggen", + "{done} of {total} tasks done": "{done} av {total} oppgaver fullført", + "Edit entry": "Rediger oppføring", + "Type": "Type", + "Attendees": "Deltakere", + "Issue is closed": "Problemet er lukket", + "Could not save the entry. Please try again.": "Oppføringen kunne ikke lagres. Prøv igjen.", + "From: {title}": "Fra: {title}", + "What needs to happen": "Hva som må gjøres", + "Could not create the action. Please try again.": "Tiltaket kunne ikke opprettes. Prøv igjen.", + "The action was created but could not be linked to the entry.": "Tiltaket ble opprettet, men kunne ikke knyttes til oppføringen.", + "Project pages": "Prosjektsider", + "Overview": "Oversikt", + "Risks": "Risikoer", + "Log entry": "Loggoppføring", + "A short name for the entry": "Et kort navn på oppføringen", + "Project": "Prosjekt", + "The project this entry belongs to": "Prosjektet oppføringen hører til", + "What kind of entry this is": "Hva slags oppføring dette er", + "When it happened": "Når det skjedde", + "What happened, in Markdown": "Hva som skjedde, i Markdown", + "Whether an issue is still open": "Om et problem fortsatt er åpent", + "The people at a meeting": "Personene på et møte", + "Tasks that came out of this entry": "Oppgaver som kom ut av denne oppføringen", + "Add risk": "Legg til risiko", + "Risk": "Risiko", + "Score": "Poeng", + "Response": "Håndtering", + "Review date": "Gjennomgangsdato", + "no longer on the project": "er ikke lenger med i prosjektet", + "Edit {title}": "Rediger {title}", + "No risks recorded yet": "Ingen risikoer registrert ennå", + "Add the first risk": "Legg til den første risikoen", + "Medium": "Middels", + "Mitigating": "Reduseres", + "Occurred": "Inntruffet", + "Avoid": "Unngå", + "Reduce": "Redusere", + "Transfer": "Overføre", + "Accept": "Akseptere", + "Highest open risks": "Høyeste åpne risikoer", + "No open risks": "Ingen åpne risikoer", + "Open the risk register": "Åpne risikoregisteret", + "Edit risk": "Rediger risiko", + "What could go wrong": "Hva som kan gå galt", + "Likelihood": "Sannsynlighet", + "Impact": "Konsekvens", + "Score {score}": "Poeng {score}", + "Countermeasures": "Tiltak", + "Could not save the risk. Please try again.": "Risikoen kunne ikke lagres. Prøv igjen.", + "Risks by likelihood and impact": "Risikoer etter sannsynlighet og konsekvens", + "Risk scale": "Risikoskala", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivåene hvert prosjekt vurderer risikoene sine på, og hvor lav går over i middels og høy", + "Number of levels": "Antall nivåer", + "Likelihood labels": "Etiketter for sannsynlighet", + "Impact labels": "Etiketter for konsekvens", + "Level {level}": "Nivå {level}", + "Medium from score": "Middels fra poeng", + "High from score": "Høy fra poeng", + "Risk scale saved": "Risikoskala lagret", + "1 risk uses level {level}. Change it first.": "1 risiko bruker nivå {level}. Endre den først.", + "{count} risks use level {level}. Change them first.": "{count} risikoer bruker nivå {level}. Endre dem først.", + "Every level needs a label, and high must start above medium.": "Hvert nivå trenger en etikett, og høy må starte over middels.", + "The project this risk belongs to": "Prosjektet risikoen hører til", + "The cause and the effect, in Markdown": "Årsak og virkning, i Markdown", + "Category": "Kategori", + "A free grouping, such as planning, budget or supplier": "En fri gruppering, for eksempel planlegging, budsjett eller leverandør", + "How likely it is, on the admin's risk scale": "Hvor sannsynlig det er, på administratorens risikoskala", + "How hard it would hit, on the admin's risk scale": "Hvor hardt det ville ramme, på administratorens risikoskala", + "Likelihood times impact, calculated by the server": "Sannsynlighet ganger konsekvens, beregnet av serveren", + "Where the risk stands": "Hvor risikoen står", + "The person who watches this risk": "Personen som følger med på risikoen", + "How the project answers the risk": "Hvordan prosjektet håndterer risikoen", + "What is done to prevent it or soften it, in Markdown": "Hva som gjøres for å hindre eller dempe den, i Markdown", + "When to look at this risk again": "Når risikoen skal vurderes igjen", + "Very low": "Svært lav", + "Very high": "Svært høy", + "All projects": "Alle prosjekter" }, "plurals": {} } diff --git a/l10n/nl.js b/l10n/nl.js index 5a1c1ee2..98bca9de 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -330,7 +330,116 @@ OC.L10N.register( "Board": "Bord", "List": "Lijst", "Column": "Kolom", - "Board or list": "Bord of lijst" + "Board or list": "Bord of lijst", + "Log": "Logboek", + "Add entry": "Notitie toevoegen", + "Show": "Tonen", + "No actions yet": "Nog geen acties", + "Closed": "Gesloten", + "Entry actions": "Notitieacties", + "Add action": "Actie toevoegen", + "by {author}": "door {author}", + "saved {time}": "opgeslagen {time}", + "Attendees: {names}": "Aanwezigen: {names}", + "Add the first entry": "Eerste notitie toevoegen", + "Issues": "Issues", + "Lessons learned": "Leerpunten", + "Meetings": "Overleggen", + "Decisions": "Besluiten", + "Actions": "Acties", + "Issue": "Issue", + "Lesson learned": "Leerpunt", + "Meeting": "Overleg", + "Decision": "Besluit", + "About this project": "Over dit project", + "No description yet": "Nog geen beschrijving", + "Start date": "Startdatum", + "Not set": "Niet ingesteld", + "End date": "Einddatum", + "Progress": "Voortgang", + "No tasks yet": "Nog geen taken", + "Open the board": "Bord openen", + "People": "Mensen", + "Owner": "Eigenaar", + "Latest log entries": "Laatste notities", + "Open the log": "Logboek openen", + "{done} of {total} tasks done": "{done} van {total} taken klaar", + "Edit entry": "Notitie bewerken", + "Type": "Soort", + "Attendees": "Aanwezigen", + "Issue is closed": "Issue is gesloten", + "Could not save the entry. Please try again.": "De notitie kon niet worden opgeslagen. Probeer het opnieuw.", + "From: {title}": "Uit: {title}", + "What needs to happen": "Wat er moet gebeuren", + "Could not create the action. Please try again.": "De actie kon niet worden aangemaakt. Probeer het opnieuw.", + "The action was created but could not be linked to the entry.": "De actie is aangemaakt, maar kon niet aan de notitie worden gekoppeld.", + "Project pages": "Projectpagina's", + "Overview": "Overzicht", + "Risks": "Risico's", + "Log entry": "Logboeknotitie", + "A short name for the entry": "Een korte naam voor de notitie", + "Project": "Project", + "The project this entry belongs to": "Het project waar deze notitie bij hoort", + "What kind of entry this is": "Wat voor notitie dit is", + "When it happened": "Wanneer het gebeurde", + "What happened, in Markdown": "Wat er gebeurde, in Markdown", + "Whether an issue is still open": "Of een issue nog openstaat", + "The people at a meeting": "De mensen bij een overleg", + "Tasks that came out of this entry": "Taken die uit deze notitie voortkwamen", + "Add risk": "Risico toevoegen", + "Risk": "Risico", + "Score": "Score", + "Response": "Reactie", + "Review date": "Evaluatiedatum", + "no longer on the project": "niet meer in het project", + "Edit {title}": "{title} bewerken", + "No risks recorded yet": "Nog geen risico's vastgelegd", + "Add the first risk": "Eerste risico toevoegen", + "Medium": "Gemiddeld", + "Mitigating": "Wordt beperkt", + "Occurred": "Opgetreden", + "Avoid": "Vermijden", + "Reduce": "Beperken", + "Transfer": "Overdragen", + "Accept": "Accepteren", + "Highest open risks": "Hoogste open risico's", + "No open risks": "Geen open risico's", + "Open the risk register": "Risicoregister openen", + "Edit risk": "Risico bewerken", + "What could go wrong": "Wat er mis kan gaan", + "Likelihood": "Kans", + "Impact": "Impact", + "Score {score}": "Score {score}", + "Countermeasures": "Maatregelen", + "Could not save the risk. Please try again.": "Het risico kon niet worden opgeslagen. Probeer het opnieuw.", + "Risks by likelihood and impact": "Risico's naar kans en impact", + "Risk scale": "Risicoschaal", + "The levels every project scores its risks on, and where low turns into medium and high": "De niveaus waarop elk project zijn risico's scoort, en waar laag overgaat in gemiddeld en hoog", + "Number of levels": "Aantal niveaus", + "Likelihood labels": "Labels voor kans", + "Impact labels": "Labels voor impact", + "Level {level}": "Niveau {level}", + "Medium from score": "Gemiddeld vanaf score", + "High from score": "Hoog vanaf score", + "Risk scale saved": "Risicoschaal opgeslagen", + "1 risk uses level {level}. Change it first.": "1 risico gebruikt niveau {level}. Pas dat eerst aan.", + "{count} risks use level {level}. Change them first.": "{count} risico's gebruiken niveau {level}. Pas die eerst aan.", + "Every level needs a label, and high must start above medium.": "Elk niveau heeft een label nodig, en hoog moet boven gemiddeld beginnen.", + "The project this risk belongs to": "Het project waar dit risico bij hoort", + "The cause and the effect, in Markdown": "De oorzaak en het gevolg, in Markdown", + "Category": "Categorie", + "A free grouping, such as planning, budget or supplier": "Een vrije indeling, zoals planning, budget of leverancier", + "How likely it is, on the admin's risk scale": "Hoe waarschijnlijk het is, op de risicoschaal van de beheerder", + "How hard it would hit, on the admin's risk scale": "Hoe hard het zou aankomen, op de risicoschaal van de beheerder", + "Likelihood times impact, calculated by the server": "Kans maal impact, berekend door de server", + "Where the risk stands": "Hoe het risico ervoor staat", + "The person who watches this risk": "De persoon die dit risico in de gaten houdt", + "How the project answers the risk": "Hoe het project op het risico reageert", + "What is done to prevent it or soften it, in Markdown": "Wat er gedaan wordt om het te voorkomen of te verzachten, in Markdown", + "When to look at this risk again": "Wanneer dit risico opnieuw bekeken wordt", + "Very low": "Zeer laag", + "Very high": "Zeer hoog", + "All projects": "Alle projecten" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nl.json b/l10n/nl.json index 80ad24aa..95f3ab2e 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -329,7 +329,116 @@ "Board": "Bord", "List": "Lijst", "Column": "Kolom", - "Board or list": "Bord of lijst" + "Board or list": "Bord of lijst", + "Log": "Logboek", + "Add entry": "Notitie toevoegen", + "Show": "Tonen", + "No actions yet": "Nog geen acties", + "Closed": "Gesloten", + "Entry actions": "Notitieacties", + "Add action": "Actie toevoegen", + "by {author}": "door {author}", + "saved {time}": "opgeslagen {time}", + "Attendees: {names}": "Aanwezigen: {names}", + "Add the first entry": "Eerste notitie toevoegen", + "Issues": "Issues", + "Lessons learned": "Leerpunten", + "Meetings": "Overleggen", + "Decisions": "Besluiten", + "Actions": "Acties", + "Issue": "Issue", + "Lesson learned": "Leerpunt", + "Meeting": "Overleg", + "Decision": "Besluit", + "About this project": "Over dit project", + "No description yet": "Nog geen beschrijving", + "Start date": "Startdatum", + "Not set": "Niet ingesteld", + "End date": "Einddatum", + "Progress": "Voortgang", + "No tasks yet": "Nog geen taken", + "Open the board": "Bord openen", + "People": "Mensen", + "Owner": "Eigenaar", + "Latest log entries": "Laatste notities", + "Open the log": "Logboek openen", + "{done} of {total} tasks done": "{done} van {total} taken klaar", + "Edit entry": "Notitie bewerken", + "Type": "Soort", + "Attendees": "Aanwezigen", + "Issue is closed": "Issue is gesloten", + "Could not save the entry. Please try again.": "De notitie kon niet worden opgeslagen. Probeer het opnieuw.", + "From: {title}": "Uit: {title}", + "What needs to happen": "Wat er moet gebeuren", + "Could not create the action. Please try again.": "De actie kon niet worden aangemaakt. Probeer het opnieuw.", + "The action was created but could not be linked to the entry.": "De actie is aangemaakt, maar kon niet aan de notitie worden gekoppeld.", + "Project pages": "Projectpagina's", + "Overview": "Overzicht", + "Risks": "Risico's", + "Log entry": "Logboeknotitie", + "A short name for the entry": "Een korte naam voor de notitie", + "Project": "Project", + "The project this entry belongs to": "Het project waar deze notitie bij hoort", + "What kind of entry this is": "Wat voor notitie dit is", + "When it happened": "Wanneer het gebeurde", + "What happened, in Markdown": "Wat er gebeurde, in Markdown", + "Whether an issue is still open": "Of een issue nog openstaat", + "The people at a meeting": "De mensen bij een overleg", + "Tasks that came out of this entry": "Taken die uit deze notitie voortkwamen", + "Add risk": "Risico toevoegen", + "Risk": "Risico", + "Score": "Score", + "Response": "Reactie", + "Review date": "Evaluatiedatum", + "no longer on the project": "niet meer in het project", + "Edit {title}": "{title} bewerken", + "No risks recorded yet": "Nog geen risico's vastgelegd", + "Add the first risk": "Eerste risico toevoegen", + "Medium": "Gemiddeld", + "Mitigating": "Wordt beperkt", + "Occurred": "Opgetreden", + "Avoid": "Vermijden", + "Reduce": "Beperken", + "Transfer": "Overdragen", + "Accept": "Accepteren", + "Highest open risks": "Hoogste open risico's", + "No open risks": "Geen open risico's", + "Open the risk register": "Risicoregister openen", + "Edit risk": "Risico bewerken", + "What could go wrong": "Wat er mis kan gaan", + "Likelihood": "Kans", + "Impact": "Impact", + "Score {score}": "Score {score}", + "Countermeasures": "Maatregelen", + "Could not save the risk. Please try again.": "Het risico kon niet worden opgeslagen. Probeer het opnieuw.", + "Risks by likelihood and impact": "Risico's naar kans en impact", + "Risk scale": "Risicoschaal", + "The levels every project scores its risks on, and where low turns into medium and high": "De niveaus waarop elk project zijn risico's scoort, en waar laag overgaat in gemiddeld en hoog", + "Number of levels": "Aantal niveaus", + "Likelihood labels": "Labels voor kans", + "Impact labels": "Labels voor impact", + "Level {level}": "Niveau {level}", + "Medium from score": "Gemiddeld vanaf score", + "High from score": "Hoog vanaf score", + "Risk scale saved": "Risicoschaal opgeslagen", + "1 risk uses level {level}. Change it first.": "1 risico gebruikt niveau {level}. Pas dat eerst aan.", + "{count} risks use level {level}. Change them first.": "{count} risico's gebruiken niveau {level}. Pas die eerst aan.", + "Every level needs a label, and high must start above medium.": "Elk niveau heeft een label nodig, en hoog moet boven gemiddeld beginnen.", + "The project this risk belongs to": "Het project waar dit risico bij hoort", + "The cause and the effect, in Markdown": "De oorzaak en het gevolg, in Markdown", + "Category": "Categorie", + "A free grouping, such as planning, budget or supplier": "Een vrije indeling, zoals planning, budget of leverancier", + "How likely it is, on the admin's risk scale": "Hoe waarschijnlijk het is, op de risicoschaal van de beheerder", + "How hard it would hit, on the admin's risk scale": "Hoe hard het zou aankomen, op de risicoschaal van de beheerder", + "Likelihood times impact, calculated by the server": "Kans maal impact, berekend door de server", + "Where the risk stands": "Hoe het risico ervoor staat", + "The person who watches this risk": "De persoon die dit risico in de gaten houdt", + "How the project answers the risk": "Hoe het project op het risico reageert", + "What is done to prevent it or soften it, in Markdown": "Wat er gedaan wordt om het te voorkomen of te verzachten, in Markdown", + "When to look at this risk again": "Wanneer dit risico opnieuw bekeken wordt", + "Very low": "Zeer laag", + "Very high": "Zeer hoog", + "All projects": "Alle projecten" }, "plurals": {}, "pluralForm": "nplurals=2; plural=(n != 1);" diff --git a/l10n/pl.js b/l10n/pl.js index d4be0f2f..d4c7e3e4 100644 --- a/l10n/pl.js +++ b/l10n/pl.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tablica", "List": "Lista", "Column": "Kolumna", - "Board or list": "Tablica lub lista" + "Board or list": "Tablica lub lista", + "Log": "Dziennik", + "Add entry": "Dodaj wpis", + "Show": "Pokaż", + "No actions yet": "Brak działań", + "Closed": "Zamknięty", + "Entry actions": "Akcje wpisu", + "Add action": "Dodaj działanie", + "by {author}": "autor: {author}", + "saved {time}": "zapisano {time}", + "Attendees: {names}": "Uczestnicy: {names}", + "Add the first entry": "Dodaj pierwszy wpis", + "Issues": "Problemy", + "Lessons learned": "Wnioski", + "Meetings": "Spotkania", + "Decisions": "Decyzje", + "Actions": "Działania", + "Issue": "Problem", + "Lesson learned": "Wniosek", + "Meeting": "Spotkanie", + "Decision": "Decyzja", + "About this project": "O projekcie", + "No description yet": "Brak opisu", + "Start date": "Data rozpoczęcia", + "Not set": "Nie ustawiono", + "End date": "Data zakończenia", + "Progress": "Postęp", + "No tasks yet": "Brak zadań", + "Open the board": "Otwórz tablicę", + "People": "Osoby", + "Owner": "Właściciel", + "Latest log entries": "Najnowsze wpisy", + "Open the log": "Otwórz dziennik", + "{done} of {total} tasks done": "Ukończono {done} z {total} zadań", + "Edit entry": "Edytuj wpis", + "Type": "Rodzaj", + "Attendees": "Uczestnicy", + "Issue is closed": "Problem jest zamknięty", + "Could not save the entry. Please try again.": "Nie udało się zapisać wpisu. Spróbuj ponownie.", + "From: {title}": "Z: {title}", + "What needs to happen": "Co trzeba zrobić", + "Could not create the action. Please try again.": "Nie udało się utworzyć działania. Spróbuj ponownie.", + "The action was created but could not be linked to the entry.": "Działanie utworzono, ale nie udało się go powiązać z wpisem.", + "Project pages": "Strony projektu", + "Overview": "Przegląd", + "Risks": "Ryzyka", + "Log entry": "Wpis dziennika", + "A short name for the entry": "Krótka nazwa wpisu", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, do którego należy ten wpis", + "What kind of entry this is": "Jakiego rodzaju jest to wpis", + "When it happened": "Kiedy to się stało", + "What happened, in Markdown": "Co się stało, w Markdownie", + "Whether an issue is still open": "Czy problem jest nadal otwarty", + "The people at a meeting": "Osoby na spotkaniu", + "Tasks that came out of this entry": "Zadania wynikające z tego wpisu", + "Add risk": "Dodaj ryzyko", + "Risk": "Ryzyko", + "Score": "Wynik", + "Response": "Reakcja", + "Review date": "Data przeglądu", + "no longer on the project": "nie należy już do projektu", + "Edit {title}": "Edytuj {title}", + "No risks recorded yet": "Nie zapisano jeszcze żadnych ryzyk", + "Add the first risk": "Dodaj pierwsze ryzyko", + "Medium": "Średnie", + "Mitigating": "Łagodzone", + "Occurred": "Wystąpiło", + "Avoid": "Unikać", + "Reduce": "Ograniczać", + "Transfer": "Przenieść", + "Accept": "Zaakceptować", + "Highest open risks": "Najwyższe otwarte ryzyka", + "No open risks": "Brak otwartych ryzyk", + "Open the risk register": "Otwórz rejestr ryzyk", + "Edit risk": "Edytuj ryzyko", + "What could go wrong": "Co może pójść źle", + "Likelihood": "Prawdopodobieństwo", + "Impact": "Wpływ", + "Score {score}": "Wynik {score}", + "Countermeasures": "Środki zaradcze", + "Could not save the risk. Please try again.": "Nie udało się zapisać ryzyka. Spróbuj ponownie.", + "Risks by likelihood and impact": "Ryzyka według prawdopodobieństwa i wpływu", + "Risk scale": "Skala ryzyka", + "The levels every project scores its risks on, and where low turns into medium and high": "Poziomy, na których każdy projekt ocenia swoje ryzyka, i gdzie niskie przechodzi w średnie i wysokie", + "Number of levels": "Liczba poziomów", + "Likelihood labels": "Etykiety prawdopodobieństwa", + "Impact labels": "Etykiety wpływu", + "Level {level}": "Poziom {level}", + "Medium from score": "Średnie od wyniku", + "High from score": "Wysokie od wyniku", + "Risk scale saved": "Zapisano skalę ryzyka", + "1 risk uses level {level}. Change it first.": "1 ryzyko używa poziomu {level}. Najpierw je zmień.", + "{count} risks use level {level}. Change them first.": "Ryzyka używające poziomu {level}: {count}. Najpierw je zmień.", + "Every level needs a label, and high must start above medium.": "Każdy poziom potrzebuje etykiety, a wysokie musi zaczynać się powyżej średniego.", + "The project this risk belongs to": "Projekt, do którego należy to ryzyko", + "The cause and the effect, in Markdown": "Przyczyna i skutek, w Markdownie", + "Category": "Kategoria", + "A free grouping, such as planning, budget or supplier": "Dowolne grupowanie, np. harmonogram, budżet lub dostawca", + "How likely it is, on the admin's risk scale": "Jak bardzo jest prawdopodobne, w skali ryzyka administratora", + "How hard it would hit, on the admin's risk scale": "Jak mocno by uderzyło, w skali ryzyka administratora", + "Likelihood times impact, calculated by the server": "Prawdopodobieństwo razy wpływ, obliczane przez serwer", + "Where the risk stands": "Na jakim etapie jest ryzyko", + "The person who watches this risk": "Osoba, która pilnuje tego ryzyka", + "How the project answers the risk": "Jak projekt reaguje na ryzyko", + "What is done to prevent it or soften it, in Markdown": "Co się robi, aby mu zapobiec lub je złagodzić, w Markdownie", + "When to look at this risk again": "Kiedy ponownie przejrzeć to ryzyko", + "Very low": "Bardzo niskie", + "Very high": "Bardzo wysokie", + "All projects": "Wszystkie projekty" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/pl.json b/l10n/pl.json index cd096eb5..a694782e 100644 --- a/l10n/pl.json +++ b/l10n/pl.json @@ -308,7 +308,116 @@ "Board": "Tablica", "List": "Lista", "Column": "Kolumna", - "Board or list": "Tablica lub lista" + "Board or list": "Tablica lub lista", + "Log": "Dziennik", + "Add entry": "Dodaj wpis", + "Show": "Pokaż", + "No actions yet": "Brak działań", + "Closed": "Zamknięty", + "Entry actions": "Akcje wpisu", + "Add action": "Dodaj działanie", + "by {author}": "autor: {author}", + "saved {time}": "zapisano {time}", + "Attendees: {names}": "Uczestnicy: {names}", + "Add the first entry": "Dodaj pierwszy wpis", + "Issues": "Problemy", + "Lessons learned": "Wnioski", + "Meetings": "Spotkania", + "Decisions": "Decyzje", + "Actions": "Działania", + "Issue": "Problem", + "Lesson learned": "Wniosek", + "Meeting": "Spotkanie", + "Decision": "Decyzja", + "About this project": "O projekcie", + "No description yet": "Brak opisu", + "Start date": "Data rozpoczęcia", + "Not set": "Nie ustawiono", + "End date": "Data zakończenia", + "Progress": "Postęp", + "No tasks yet": "Brak zadań", + "Open the board": "Otwórz tablicę", + "People": "Osoby", + "Owner": "Właściciel", + "Latest log entries": "Najnowsze wpisy", + "Open the log": "Otwórz dziennik", + "{done} of {total} tasks done": "Ukończono {done} z {total} zadań", + "Edit entry": "Edytuj wpis", + "Type": "Rodzaj", + "Attendees": "Uczestnicy", + "Issue is closed": "Problem jest zamknięty", + "Could not save the entry. Please try again.": "Nie udało się zapisać wpisu. Spróbuj ponownie.", + "From: {title}": "Z: {title}", + "What needs to happen": "Co trzeba zrobić", + "Could not create the action. Please try again.": "Nie udało się utworzyć działania. Spróbuj ponownie.", + "The action was created but could not be linked to the entry.": "Działanie utworzono, ale nie udało się go powiązać z wpisem.", + "Project pages": "Strony projektu", + "Overview": "Przegląd", + "Risks": "Ryzyka", + "Log entry": "Wpis dziennika", + "A short name for the entry": "Krótka nazwa wpisu", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, do którego należy ten wpis", + "What kind of entry this is": "Jakiego rodzaju jest to wpis", + "When it happened": "Kiedy to się stało", + "What happened, in Markdown": "Co się stało, w Markdownie", + "Whether an issue is still open": "Czy problem jest nadal otwarty", + "The people at a meeting": "Osoby na spotkaniu", + "Tasks that came out of this entry": "Zadania wynikające z tego wpisu", + "Add risk": "Dodaj ryzyko", + "Risk": "Ryzyko", + "Score": "Wynik", + "Response": "Reakcja", + "Review date": "Data przeglądu", + "no longer on the project": "nie należy już do projektu", + "Edit {title}": "Edytuj {title}", + "No risks recorded yet": "Nie zapisano jeszcze żadnych ryzyk", + "Add the first risk": "Dodaj pierwsze ryzyko", + "Medium": "Średnie", + "Mitigating": "Łagodzone", + "Occurred": "Wystąpiło", + "Avoid": "Unikać", + "Reduce": "Ograniczać", + "Transfer": "Przenieść", + "Accept": "Zaakceptować", + "Highest open risks": "Najwyższe otwarte ryzyka", + "No open risks": "Brak otwartych ryzyk", + "Open the risk register": "Otwórz rejestr ryzyk", + "Edit risk": "Edytuj ryzyko", + "What could go wrong": "Co może pójść źle", + "Likelihood": "Prawdopodobieństwo", + "Impact": "Wpływ", + "Score {score}": "Wynik {score}", + "Countermeasures": "Środki zaradcze", + "Could not save the risk. Please try again.": "Nie udało się zapisać ryzyka. Spróbuj ponownie.", + "Risks by likelihood and impact": "Ryzyka według prawdopodobieństwa i wpływu", + "Risk scale": "Skala ryzyka", + "The levels every project scores its risks on, and where low turns into medium and high": "Poziomy, na których każdy projekt ocenia swoje ryzyka, i gdzie niskie przechodzi w średnie i wysokie", + "Number of levels": "Liczba poziomów", + "Likelihood labels": "Etykiety prawdopodobieństwa", + "Impact labels": "Etykiety wpływu", + "Level {level}": "Poziom {level}", + "Medium from score": "Średnie od wyniku", + "High from score": "Wysokie od wyniku", + "Risk scale saved": "Zapisano skalę ryzyka", + "1 risk uses level {level}. Change it first.": "1 ryzyko używa poziomu {level}. Najpierw je zmień.", + "{count} risks use level {level}. Change them first.": "Ryzyka używające poziomu {level}: {count}. Najpierw je zmień.", + "Every level needs a label, and high must start above medium.": "Każdy poziom potrzebuje etykiety, a wysokie musi zaczynać się powyżej średniego.", + "The project this risk belongs to": "Projekt, do którego należy to ryzyko", + "The cause and the effect, in Markdown": "Przyczyna i skutek, w Markdownie", + "Category": "Kategoria", + "A free grouping, such as planning, budget or supplier": "Dowolne grupowanie, np. harmonogram, budżet lub dostawca", + "How likely it is, on the admin's risk scale": "Jak bardzo jest prawdopodobne, w skali ryzyka administratora", + "How hard it would hit, on the admin's risk scale": "Jak mocno by uderzyło, w skali ryzyka administratora", + "Likelihood times impact, calculated by the server": "Prawdopodobieństwo razy wpływ, obliczane przez serwer", + "Where the risk stands": "Na jakim etapie jest ryzyko", + "The person who watches this risk": "Osoba, która pilnuje tego ryzyka", + "How the project answers the risk": "Jak projekt reaguje na ryzyko", + "What is done to prevent it or soften it, in Markdown": "Co się robi, aby mu zapobiec lub je złagodzić, w Markdownie", + "When to look at this risk again": "Kiedy ponownie przejrzeć to ryzyko", + "Very low": "Bardzo niskie", + "Very high": "Bardzo wysokie", + "All projects": "Wszystkie projekty" }, "plurals": {} } diff --git a/l10n/pt.js b/l10n/pt.js index 8615ca86..3ddd4262 100644 --- a/l10n/pt.js +++ b/l10n/pt.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Quadro", "List": "Lista", "Column": "Coluna", - "Board or list": "Quadro ou lista" + "Board or list": "Quadro ou lista", + "Log": "Registo", + "Add entry": "Adicionar entrada", + "Show": "Mostrar", + "No actions yet": "Ainda não há ações", + "Closed": "Fechado", + "Entry actions": "Ações da entrada", + "Add action": "Adicionar ação", + "by {author}": "por {author}", + "saved {time}": "guardado {time}", + "Attendees: {names}": "Participantes: {names}", + "Add the first entry": "Adicionar a primeira entrada", + "Issues": "Problemas", + "Lessons learned": "Lições aprendidas", + "Meetings": "Reuniões", + "Decisions": "Decisões", + "Actions": "Ações", + "Issue": "Problema", + "Lesson learned": "Lição aprendida", + "Meeting": "Reunião", + "Decision": "Decisão", + "About this project": "Sobre este projeto", + "No description yet": "Ainda sem descrição", + "Start date": "Data de início", + "Not set": "Não definida", + "End date": "Data de fim", + "Progress": "Progresso", + "No tasks yet": "Ainda não há tarefas", + "Open the board": "Abrir o quadro", + "People": "Pessoas", + "Owner": "Proprietário", + "Latest log entries": "Entradas mais recentes", + "Open the log": "Abrir o registo", + "{done} of {total} tasks done": "{done} de {total} tarefas concluídas", + "Edit entry": "Editar entrada", + "Type": "Tipo", + "Attendees": "Participantes", + "Issue is closed": "O problema está fechado", + "Could not save the entry. Please try again.": "Não foi possível guardar a entrada. Tente novamente.", + "From: {title}": "De: {title}", + "What needs to happen": "O que tem de acontecer", + "Could not create the action. Please try again.": "Não foi possível criar a ação. Tente novamente.", + "The action was created but could not be linked to the entry.": "A ação foi criada, mas não foi possível ligá-la à entrada.", + "Project pages": "Páginas do projeto", + "Overview": "Visão geral", + "Risks": "Riscos", + "Log entry": "Entrada de registo", + "A short name for the entry": "Um nome curto para a entrada", + "Project": "Projeto", + "The project this entry belongs to": "O projeto a que esta entrada pertence", + "What kind of entry this is": "Que tipo de entrada é", + "When it happened": "Quando aconteceu", + "What happened, in Markdown": "O que aconteceu, em Markdown", + "Whether an issue is still open": "Se um problema ainda está aberto", + "The people at a meeting": "As pessoas numa reunião", + "Tasks that came out of this entry": "Tarefas que resultaram desta entrada", + "Add risk": "Adicionar risco", + "Risk": "Risco", + "Score": "Pontuação", + "Response": "Resposta", + "Review date": "Data de revisão", + "no longer on the project": "já não está no projeto", + "Edit {title}": "Editar {title}", + "No risks recorded yet": "Ainda não há riscos registados", + "Add the first risk": "Adicionar o primeiro risco", + "Medium": "Médio", + "Mitigating": "Em mitigação", + "Occurred": "Ocorreu", + "Avoid": "Evitar", + "Reduce": "Reduzir", + "Transfer": "Transferir", + "Accept": "Aceitar", + "Highest open risks": "Riscos abertos mais altos", + "No open risks": "Não há riscos abertos", + "Open the risk register": "Abrir o registo de riscos", + "Edit risk": "Editar risco", + "What could go wrong": "O que pode correr mal", + "Likelihood": "Probabilidade", + "Impact": "Impacto", + "Score {score}": "Pontuação {score}", + "Countermeasures": "Contramedidas", + "Could not save the risk. Please try again.": "Não foi possível guardar o risco. Tente novamente.", + "Risks by likelihood and impact": "Riscos por probabilidade e impacto", + "Risk scale": "Escala de risco", + "The levels every project scores its risks on, and where low turns into medium and high": "Os níveis em que cada projeto pontua os seus riscos, e onde baixo passa a médio e alto", + "Number of levels": "Número de níveis", + "Likelihood labels": "Etiquetas de probabilidade", + "Impact labels": "Etiquetas de impacto", + "Level {level}": "Nível {level}", + "Medium from score": "Médio a partir da pontuação", + "High from score": "Alto a partir da pontuação", + "Risk scale saved": "Escala de risco guardada", + "1 risk uses level {level}. Change it first.": "1 risco usa o nível {level}. Altere-o primeiro.", + "{count} risks use level {level}. Change them first.": "{count} riscos usam o nível {level}. Altere-os primeiro.", + "Every level needs a label, and high must start above medium.": "Cada nível precisa de uma etiqueta, e alto tem de começar acima de médio.", + "The project this risk belongs to": "O projeto a que este risco pertence", + "The cause and the effect, in Markdown": "A causa e o efeito, em Markdown", + "Category": "Categoria", + "A free grouping, such as planning, budget or supplier": "Um agrupamento livre, como planeamento, orçamento ou fornecedor", + "How likely it is, on the admin's risk scale": "Quão provável é, na escala de risco do administrador", + "How hard it would hit, on the admin's risk scale": "Quão forte atingiria, na escala de risco do administrador", + "Likelihood times impact, calculated by the server": "Probabilidade vezes impacto, calculado pelo servidor", + "Where the risk stands": "Em que ponto está o risco", + "The person who watches this risk": "A pessoa que acompanha este risco", + "How the project answers the risk": "Como o projeto responde ao risco", + "What is done to prevent it or soften it, in Markdown": "O que se faz para o evitar ou atenuar, em Markdown", + "When to look at this risk again": "Quando rever este risco de novo", + "Very low": "Muito baixo", + "Very high": "Muito alto", + "All projects": "Todos os projetos" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/pt.json b/l10n/pt.json index f9a4468e..3a22fb0f 100644 --- a/l10n/pt.json +++ b/l10n/pt.json @@ -308,7 +308,116 @@ "Board": "Quadro", "List": "Lista", "Column": "Coluna", - "Board or list": "Quadro ou lista" + "Board or list": "Quadro ou lista", + "Log": "Registo", + "Add entry": "Adicionar entrada", + "Show": "Mostrar", + "No actions yet": "Ainda não há ações", + "Closed": "Fechado", + "Entry actions": "Ações da entrada", + "Add action": "Adicionar ação", + "by {author}": "por {author}", + "saved {time}": "guardado {time}", + "Attendees: {names}": "Participantes: {names}", + "Add the first entry": "Adicionar a primeira entrada", + "Issues": "Problemas", + "Lessons learned": "Lições aprendidas", + "Meetings": "Reuniões", + "Decisions": "Decisões", + "Actions": "Ações", + "Issue": "Problema", + "Lesson learned": "Lição aprendida", + "Meeting": "Reunião", + "Decision": "Decisão", + "About this project": "Sobre este projeto", + "No description yet": "Ainda sem descrição", + "Start date": "Data de início", + "Not set": "Não definida", + "End date": "Data de fim", + "Progress": "Progresso", + "No tasks yet": "Ainda não há tarefas", + "Open the board": "Abrir o quadro", + "People": "Pessoas", + "Owner": "Proprietário", + "Latest log entries": "Entradas mais recentes", + "Open the log": "Abrir o registo", + "{done} of {total} tasks done": "{done} de {total} tarefas concluídas", + "Edit entry": "Editar entrada", + "Type": "Tipo", + "Attendees": "Participantes", + "Issue is closed": "O problema está fechado", + "Could not save the entry. Please try again.": "Não foi possível guardar a entrada. Tente novamente.", + "From: {title}": "De: {title}", + "What needs to happen": "O que tem de acontecer", + "Could not create the action. Please try again.": "Não foi possível criar a ação. Tente novamente.", + "The action was created but could not be linked to the entry.": "A ação foi criada, mas não foi possível ligá-la à entrada.", + "Project pages": "Páginas do projeto", + "Overview": "Visão geral", + "Risks": "Riscos", + "Log entry": "Entrada de registo", + "A short name for the entry": "Um nome curto para a entrada", + "Project": "Projeto", + "The project this entry belongs to": "O projeto a que esta entrada pertence", + "What kind of entry this is": "Que tipo de entrada é", + "When it happened": "Quando aconteceu", + "What happened, in Markdown": "O que aconteceu, em Markdown", + "Whether an issue is still open": "Se um problema ainda está aberto", + "The people at a meeting": "As pessoas numa reunião", + "Tasks that came out of this entry": "Tarefas que resultaram desta entrada", + "Add risk": "Adicionar risco", + "Risk": "Risco", + "Score": "Pontuação", + "Response": "Resposta", + "Review date": "Data de revisão", + "no longer on the project": "já não está no projeto", + "Edit {title}": "Editar {title}", + "No risks recorded yet": "Ainda não há riscos registados", + "Add the first risk": "Adicionar o primeiro risco", + "Medium": "Médio", + "Mitigating": "Em mitigação", + "Occurred": "Ocorreu", + "Avoid": "Evitar", + "Reduce": "Reduzir", + "Transfer": "Transferir", + "Accept": "Aceitar", + "Highest open risks": "Riscos abertos mais altos", + "No open risks": "Não há riscos abertos", + "Open the risk register": "Abrir o registo de riscos", + "Edit risk": "Editar risco", + "What could go wrong": "O que pode correr mal", + "Likelihood": "Probabilidade", + "Impact": "Impacto", + "Score {score}": "Pontuação {score}", + "Countermeasures": "Contramedidas", + "Could not save the risk. Please try again.": "Não foi possível guardar o risco. Tente novamente.", + "Risks by likelihood and impact": "Riscos por probabilidade e impacto", + "Risk scale": "Escala de risco", + "The levels every project scores its risks on, and where low turns into medium and high": "Os níveis em que cada projeto pontua os seus riscos, e onde baixo passa a médio e alto", + "Number of levels": "Número de níveis", + "Likelihood labels": "Etiquetas de probabilidade", + "Impact labels": "Etiquetas de impacto", + "Level {level}": "Nível {level}", + "Medium from score": "Médio a partir da pontuação", + "High from score": "Alto a partir da pontuação", + "Risk scale saved": "Escala de risco guardada", + "1 risk uses level {level}. Change it first.": "1 risco usa o nível {level}. Altere-o primeiro.", + "{count} risks use level {level}. Change them first.": "{count} riscos usam o nível {level}. Altere-os primeiro.", + "Every level needs a label, and high must start above medium.": "Cada nível precisa de uma etiqueta, e alto tem de começar acima de médio.", + "The project this risk belongs to": "O projeto a que este risco pertence", + "The cause and the effect, in Markdown": "A causa e o efeito, em Markdown", + "Category": "Categoria", + "A free grouping, such as planning, budget or supplier": "Um agrupamento livre, como planeamento, orçamento ou fornecedor", + "How likely it is, on the admin's risk scale": "Quão provável é, na escala de risco do administrador", + "How hard it would hit, on the admin's risk scale": "Quão forte atingiria, na escala de risco do administrador", + "Likelihood times impact, calculated by the server": "Probabilidade vezes impacto, calculado pelo servidor", + "Where the risk stands": "Em que ponto está o risco", + "The person who watches this risk": "A pessoa que acompanha este risco", + "How the project answers the risk": "Como o projeto responde ao risco", + "What is done to prevent it or soften it, in Markdown": "O que se faz para o evitar ou atenuar, em Markdown", + "When to look at this risk again": "Quando rever este risco de novo", + "Very low": "Muito baixo", + "Very high": "Muito alto", + "All projects": "Todos os projetos" }, "plurals": {} } diff --git a/l10n/rm.js b/l10n/rm.js index db64974c..2d1761ca 100644 --- a/l10n/rm.js +++ b/l10n/rm.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tavla", "List": "Glista", "Column": "Colonna", - "Board or list": "Tavla u glista" + "Board or list": "Tavla u glista", + "Log": "Protocol", + "Add entry": "Agiuntar ina inscripziun", + "Show": "Mussar", + "No actions yet": "Anc naginas acziuns", + "Closed": "Serrà", + "Entry actions": "Acziuns da l'inscripziun", + "Add action": "Agiuntar ina acziun", + "by {author}": "da {author}", + "saved {time}": "memorisà {time}", + "Attendees: {names}": "Participants: {names}", + "Add the first entry": "Agiuntar l'emprima inscripziun", + "Issues": "Problems", + "Lessons learned": "Enconuschientschas", + "Meetings": "Sesidas", + "Decisions": "Decisiuns", + "Actions": "Acziuns", + "Issue": "Problem", + "Lesson learned": "Enconuschientscha", + "Meeting": "Sesida", + "Decision": "Decisiun", + "About this project": "Davart quest project", + "No description yet": "Anc nagina descripziun", + "Start date": "Data da cumenzament", + "Not set": "Betg definì", + "End date": "Data da fin", + "Progress": "Progress", + "No tasks yet": "Anc naginas incumbensas", + "Open the board": "Avrir la tavla", + "People": "Persunas", + "Owner": "Possessur", + "Latest log entries": "Ultimas inscripziuns", + "Open the log": "Avrir il protocol", + "{done} of {total} tasks done": "{done} da {total} incumbensas terminadas", + "Edit entry": "Modifitgar l'inscripziun", + "Type": "Gener", + "Attendees": "Participants", + "Issue is closed": "Il problem è serrà", + "Could not save the entry. Please try again.": "L'inscripziun n'ha betg pudì vegnir memorisada. Empruvai anc ina giada.", + "From: {title}": "Da: {title}", + "What needs to happen": "Tge che sto succeder", + "Could not create the action. Please try again.": "L'acziun n'ha betg pudì vegnir creada. Empruvai anc ina giada.", + "The action was created but could not be linked to the entry.": "L'acziun è vegnida creada, ma n'ha betg pudì vegnir colliada cun l'inscripziun.", + "Project pages": "Paginas dal project", + "Overview": "Survista", + "Risks": "Ristgs", + "Log entry": "Inscripziun dal protocol", + "A short name for the entry": "In num curt per l'inscripziun", + "Project": "Project", + "The project this entry belongs to": "Il project al qual l'inscripziun appartegna", + "What kind of entry this is": "Tge gener d'inscripziun che quai è", + "When it happened": "Cura che quai è capità", + "What happened, in Markdown": "Tge ch'è capità, en Markdown", + "Whether an issue is still open": "Sch'in problem è anc avert", + "The people at a meeting": "Las persunas ad ina sesida", + "Tasks that came out of this entry": "Incumbensas ch'èn resultadas da questa inscripziun", + "Add risk": "Agiuntar in ristg", + "Risk": "Ristg", + "Score": "Puncts", + "Response": "Reacziun", + "Review date": "Data da revisiun", + "no longer on the project": "betg pli en il project", + "Edit {title}": "Modifitgar {title}", + "No risks recorded yet": "Anc nagins ristgs registrads", + "Add the first risk": "Agiuntar l'emprim ristg", + "Medium": "Mesaun", + "Mitigating": "Vegn mitigà", + "Occurred": "Succedì", + "Avoid": "Evitar", + "Reduce": "Reducir", + "Transfer": "Transferir", + "Accept": "Acceptar", + "Highest open risks": "Ils pli auts ristgs averts", + "No open risks": "Nagins ristgs averts", + "Open the risk register": "Avrir il register da ristgs", + "Edit risk": "Modifitgar il ristg", + "What could go wrong": "Tge che pudess ir mal", + "Likelihood": "Probabilitad", + "Impact": "Effect", + "Score {score}": "Puncts {score}", + "Countermeasures": "Contramesiras", + "Could not save the risk. Please try again.": "Il ristg n'ha betg pudì vegnir memorisà. Empruvai anc ina giada.", + "Risks by likelihood and impact": "Ristgs tenor probabilitad ed effect", + "Risk scale": "Scala da ristg", + "The levels every project scores its risks on, and where low turns into medium and high": "Ils nivels sin ils quals mintga project valitescha ses ristgs, e nua che bass daventa mesaun ed aut", + "Number of levels": "Dumber da nivels", + "Likelihood labels": "Etichettas da probabilitad", + "Impact labels": "Etichettas d'effect", + "Level {level}": "Nivel {level}", + "Medium from score": "Mesaun a partir dals puncts", + "High from score": "Aut a partir dals puncts", + "Risk scale saved": "Scala da ristg memorisada", + "1 risk uses level {level}. Change it first.": "1 ristg dovra il nivel {level}. Midai el l'emprim.", + "{count} risks use level {level}. Change them first.": "{count} ristgs dovran il nivel {level}. Midai els l'emprim.", + "Every level needs a label, and high must start above medium.": "Mintga nivel dovra ina etichetta, ed aut sto cumenzar sur mesaun.", + "The project this risk belongs to": "Il project al qual quest ristg appartegna", + "The cause and the effect, in Markdown": "La chaschun e l'effect, en Markdown", + "Category": "Categoria", + "A free grouping, such as planning, budget or supplier": "Ina gruppaziun libra, sco planisaziun, budget u furnitur", + "How likely it is, on the admin's risk scale": "Co probabel ch'el è, sin la scala da ristg da l'administratur", + "How hard it would hit, on the admin's risk scale": "Co ferm ch'el pertutgass, sin la scala da ristg da l'administratur", + "Likelihood times impact, calculated by the server": "Probabilitad giada effect, calculà dal server", + "Where the risk stands": "Nua ch'il ristg stat", + "The person who watches this risk": "La persuna che surveglia quest ristg", + "How the project answers the risk": "Co ch'il project reagescha sin il ristg", + "What is done to prevent it or soften it, in Markdown": "Tge che vegn fatg per al impedir u mitigiar, en Markdown", + "When to look at this risk again": "Cura revair danovamain quest ristg", + "Very low": "Fitg bass", + "Very high": "Fitg aut", + "All projects": "Tut ils projects" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/rm.json b/l10n/rm.json index 6a57f259..fd370fc6 100644 --- a/l10n/rm.json +++ b/l10n/rm.json @@ -308,7 +308,116 @@ "Board": "Tavla", "List": "Glista", "Column": "Colonna", - "Board or list": "Tavla u glista" + "Board or list": "Tavla u glista", + "Log": "Protocol", + "Add entry": "Agiuntar ina inscripziun", + "Show": "Mussar", + "No actions yet": "Anc naginas acziuns", + "Closed": "Serrà", + "Entry actions": "Acziuns da l'inscripziun", + "Add action": "Agiuntar ina acziun", + "by {author}": "da {author}", + "saved {time}": "memorisà {time}", + "Attendees: {names}": "Participants: {names}", + "Add the first entry": "Agiuntar l'emprima inscripziun", + "Issues": "Problems", + "Lessons learned": "Enconuschientschas", + "Meetings": "Sesidas", + "Decisions": "Decisiuns", + "Actions": "Acziuns", + "Issue": "Problem", + "Lesson learned": "Enconuschientscha", + "Meeting": "Sesida", + "Decision": "Decisiun", + "About this project": "Davart quest project", + "No description yet": "Anc nagina descripziun", + "Start date": "Data da cumenzament", + "Not set": "Betg definì", + "End date": "Data da fin", + "Progress": "Progress", + "No tasks yet": "Anc naginas incumbensas", + "Open the board": "Avrir la tavla", + "People": "Persunas", + "Owner": "Possessur", + "Latest log entries": "Ultimas inscripziuns", + "Open the log": "Avrir il protocol", + "{done} of {total} tasks done": "{done} da {total} incumbensas terminadas", + "Edit entry": "Modifitgar l'inscripziun", + "Type": "Gener", + "Attendees": "Participants", + "Issue is closed": "Il problem è serrà", + "Could not save the entry. Please try again.": "L'inscripziun n'ha betg pudì vegnir memorisada. Empruvai anc ina giada.", + "From: {title}": "Da: {title}", + "What needs to happen": "Tge che sto succeder", + "Could not create the action. Please try again.": "L'acziun n'ha betg pudì vegnir creada. Empruvai anc ina giada.", + "The action was created but could not be linked to the entry.": "L'acziun è vegnida creada, ma n'ha betg pudì vegnir colliada cun l'inscripziun.", + "Project pages": "Paginas dal project", + "Overview": "Survista", + "Risks": "Ristgs", + "Log entry": "Inscripziun dal protocol", + "A short name for the entry": "In num curt per l'inscripziun", + "Project": "Project", + "The project this entry belongs to": "Il project al qual l'inscripziun appartegna", + "What kind of entry this is": "Tge gener d'inscripziun che quai è", + "When it happened": "Cura che quai è capità", + "What happened, in Markdown": "Tge ch'è capità, en Markdown", + "Whether an issue is still open": "Sch'in problem è anc avert", + "The people at a meeting": "Las persunas ad ina sesida", + "Tasks that came out of this entry": "Incumbensas ch'èn resultadas da questa inscripziun", + "Add risk": "Agiuntar in ristg", + "Risk": "Ristg", + "Score": "Puncts", + "Response": "Reacziun", + "Review date": "Data da revisiun", + "no longer on the project": "betg pli en il project", + "Edit {title}": "Modifitgar {title}", + "No risks recorded yet": "Anc nagins ristgs registrads", + "Add the first risk": "Agiuntar l'emprim ristg", + "Medium": "Mesaun", + "Mitigating": "Vegn mitigà", + "Occurred": "Succedì", + "Avoid": "Evitar", + "Reduce": "Reducir", + "Transfer": "Transferir", + "Accept": "Acceptar", + "Highest open risks": "Ils pli auts ristgs averts", + "No open risks": "Nagins ristgs averts", + "Open the risk register": "Avrir il register da ristgs", + "Edit risk": "Modifitgar il ristg", + "What could go wrong": "Tge che pudess ir mal", + "Likelihood": "Probabilitad", + "Impact": "Effect", + "Score {score}": "Puncts {score}", + "Countermeasures": "Contramesiras", + "Could not save the risk. Please try again.": "Il ristg n'ha betg pudì vegnir memorisà. Empruvai anc ina giada.", + "Risks by likelihood and impact": "Ristgs tenor probabilitad ed effect", + "Risk scale": "Scala da ristg", + "The levels every project scores its risks on, and where low turns into medium and high": "Ils nivels sin ils quals mintga project valitescha ses ristgs, e nua che bass daventa mesaun ed aut", + "Number of levels": "Dumber da nivels", + "Likelihood labels": "Etichettas da probabilitad", + "Impact labels": "Etichettas d'effect", + "Level {level}": "Nivel {level}", + "Medium from score": "Mesaun a partir dals puncts", + "High from score": "Aut a partir dals puncts", + "Risk scale saved": "Scala da ristg memorisada", + "1 risk uses level {level}. Change it first.": "1 ristg dovra il nivel {level}. Midai el l'emprim.", + "{count} risks use level {level}. Change them first.": "{count} ristgs dovran il nivel {level}. Midai els l'emprim.", + "Every level needs a label, and high must start above medium.": "Mintga nivel dovra ina etichetta, ed aut sto cumenzar sur mesaun.", + "The project this risk belongs to": "Il project al qual quest ristg appartegna", + "The cause and the effect, in Markdown": "La chaschun e l'effect, en Markdown", + "Category": "Categoria", + "A free grouping, such as planning, budget or supplier": "Ina gruppaziun libra, sco planisaziun, budget u furnitur", + "How likely it is, on the admin's risk scale": "Co probabel ch'el è, sin la scala da ristg da l'administratur", + "How hard it would hit, on the admin's risk scale": "Co ferm ch'el pertutgass, sin la scala da ristg da l'administratur", + "Likelihood times impact, calculated by the server": "Probabilitad giada effect, calculà dal server", + "Where the risk stands": "Nua ch'il ristg stat", + "The person who watches this risk": "La persuna che surveglia quest ristg", + "How the project answers the risk": "Co ch'il project reagescha sin il ristg", + "What is done to prevent it or soften it, in Markdown": "Tge che vegn fatg per al impedir u mitigiar, en Markdown", + "When to look at this risk again": "Cura revair danovamain quest ristg", + "Very low": "Fitg bass", + "Very high": "Fitg aut", + "All projects": "Tut ils projects" }, "plurals": null } diff --git a/l10n/ro.js b/l10n/ro.js index 1dca3c01..f513ce8b 100644 --- a/l10n/ro.js +++ b/l10n/ro.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tablă", "List": "Listă", "Column": "Coloană", - "Board or list": "Tablă sau listă" + "Board or list": "Tablă sau listă", + "Log": "Jurnal", + "Add entry": "Adaugă o intrare", + "Show": "Afișează", + "No actions yet": "Încă nu există acțiuni", + "Closed": "Închis", + "Entry actions": "Acțiuni pentru intrare", + "Add action": "Adaugă o acțiune", + "by {author}": "de {author}", + "saved {time}": "salvat {time}", + "Attendees: {names}": "Participanți: {names}", + "Add the first entry": "Adaugă prima intrare", + "Issues": "Probleme", + "Lessons learned": "Lecții învățate", + "Meetings": "Întâlniri", + "Decisions": "Decizii", + "Actions": "Acțiuni", + "Issue": "Problemă", + "Lesson learned": "Lecție învățată", + "Meeting": "Întâlnire", + "Decision": "Decizie", + "About this project": "Despre acest proiect", + "No description yet": "Încă nu există descriere", + "Start date": "Data de început", + "Not set": "Nesetat", + "End date": "Data de sfârșit", + "Progress": "Progres", + "No tasks yet": "Încă nu există sarcini", + "Open the board": "Deschide tabla", + "People": "Persoane", + "Owner": "Proprietar", + "Latest log entries": "Cele mai recente intrări", + "Open the log": "Deschide jurnalul", + "{done} of {total} tasks done": "{done} din {total} sarcini finalizate", + "Edit entry": "Editează intrarea", + "Type": "Tip", + "Attendees": "Participanți", + "Issue is closed": "Problema este închisă", + "Could not save the entry. Please try again.": "Intrarea nu a putut fi salvată. Încercați din nou.", + "From: {title}": "Din: {title}", + "What needs to happen": "Ce trebuie să se întâmple", + "Could not create the action. Please try again.": "Acțiunea nu a putut fi creată. Încercați din nou.", + "The action was created but could not be linked to the entry.": "Acțiunea a fost creată, dar nu a putut fi legată de intrare.", + "Project pages": "Paginile proiectului", + "Overview": "Prezentare generală", + "Risks": "Riscuri", + "Log entry": "Intrare în jurnal", + "A short name for the entry": "Un nume scurt pentru intrare", + "Project": "Proiect", + "The project this entry belongs to": "Proiectul căruia îi aparține această intrare", + "What kind of entry this is": "Ce tip de intrare este", + "When it happened": "Când s-a întâmplat", + "What happened, in Markdown": "Ce s-a întâmplat, în Markdown", + "Whether an issue is still open": "Dacă o problemă este încă deschisă", + "The people at a meeting": "Persoanele de la o întâlnire", + "Tasks that came out of this entry": "Sarcini rezultate din această intrare", + "Add risk": "Adaugă un risc", + "Risk": "Risc", + "Score": "Scor", + "Response": "Răspuns", + "Review date": "Data revizuirii", + "no longer on the project": "nu mai face parte din proiect", + "Edit {title}": "Editează {title}", + "No risks recorded yet": "Încă nu există riscuri înregistrate", + "Add the first risk": "Adaugă primul risc", + "Medium": "Mediu", + "Mitigating": "În atenuare", + "Occurred": "S-a produs", + "Avoid": "Evitare", + "Reduce": "Reducere", + "Transfer": "Transfer", + "Accept": "Acceptare", + "Highest open risks": "Cele mai mari riscuri deschise", + "No open risks": "Niciun risc deschis", + "Open the risk register": "Deschide registrul de riscuri", + "Edit risk": "Editează riscul", + "What could go wrong": "Ce ar putea merge prost", + "Likelihood": "Probabilitate", + "Impact": "Impact", + "Score {score}": "Scor {score}", + "Countermeasures": "Contramăsuri", + "Could not save the risk. Please try again.": "Riscul nu a putut fi salvat. Încercați din nou.", + "Risks by likelihood and impact": "Riscuri după probabilitate și impact", + "Risk scale": "Scala de risc", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivelurile pe care fiecare proiect își punctează riscurile și unde scăzut trece în mediu și ridicat", + "Number of levels": "Numărul de niveluri", + "Likelihood labels": "Etichete pentru probabilitate", + "Impact labels": "Etichete pentru impact", + "Level {level}": "Nivelul {level}", + "Medium from score": "Mediu de la scorul", + "High from score": "Ridicat de la scorul", + "Risk scale saved": "Scala de risc a fost salvată", + "1 risk uses level {level}. Change it first.": "1 risc folosește nivelul {level}. Modificați-l mai întâi.", + "{count} risks use level {level}. Change them first.": "{count} riscuri folosesc nivelul {level}. Modificați-le mai întâi.", + "Every level needs a label, and high must start above medium.": "Fiecare nivel are nevoie de o etichetă, iar ridicat trebuie să înceapă peste mediu.", + "The project this risk belongs to": "Proiectul căruia îi aparține acest risc", + "The cause and the effect, in Markdown": "Cauza și efectul, în Markdown", + "Category": "Categorie", + "A free grouping, such as planning, budget or supplier": "O grupare liberă, cum ar fi planificare, buget sau furnizor", + "How likely it is, on the admin's risk scale": "Cât de probabil este, pe scala de risc a administratorului", + "How hard it would hit, on the admin's risk scale": "Cât de tare ar lovi, pe scala de risc a administratorului", + "Likelihood times impact, calculated by the server": "Probabilitate ori impact, calculat de server", + "Where the risk stands": "Unde se află riscul", + "The person who watches this risk": "Persoana care urmărește acest risc", + "How the project answers the risk": "Cum răspunde proiectul la risc", + "What is done to prevent it or soften it, in Markdown": "Ce se face pentru a-l preveni sau atenua, în Markdown", + "When to look at this risk again": "Când se revizuiește din nou acest risc", + "Very low": "Foarte scăzut", + "Very high": "Foarte ridicat", + "All projects": "Toate proiectele" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ro.json b/l10n/ro.json index a29472b2..cbd1f40a 100644 --- a/l10n/ro.json +++ b/l10n/ro.json @@ -308,7 +308,116 @@ "Board": "Tablă", "List": "Listă", "Column": "Coloană", - "Board or list": "Tablă sau listă" + "Board or list": "Tablă sau listă", + "Log": "Jurnal", + "Add entry": "Adaugă o intrare", + "Show": "Afișează", + "No actions yet": "Încă nu există acțiuni", + "Closed": "Închis", + "Entry actions": "Acțiuni pentru intrare", + "Add action": "Adaugă o acțiune", + "by {author}": "de {author}", + "saved {time}": "salvat {time}", + "Attendees: {names}": "Participanți: {names}", + "Add the first entry": "Adaugă prima intrare", + "Issues": "Probleme", + "Lessons learned": "Lecții învățate", + "Meetings": "Întâlniri", + "Decisions": "Decizii", + "Actions": "Acțiuni", + "Issue": "Problemă", + "Lesson learned": "Lecție învățată", + "Meeting": "Întâlnire", + "Decision": "Decizie", + "About this project": "Despre acest proiect", + "No description yet": "Încă nu există descriere", + "Start date": "Data de început", + "Not set": "Nesetat", + "End date": "Data de sfârșit", + "Progress": "Progres", + "No tasks yet": "Încă nu există sarcini", + "Open the board": "Deschide tabla", + "People": "Persoane", + "Owner": "Proprietar", + "Latest log entries": "Cele mai recente intrări", + "Open the log": "Deschide jurnalul", + "{done} of {total} tasks done": "{done} din {total} sarcini finalizate", + "Edit entry": "Editează intrarea", + "Type": "Tip", + "Attendees": "Participanți", + "Issue is closed": "Problema este închisă", + "Could not save the entry. Please try again.": "Intrarea nu a putut fi salvată. Încercați din nou.", + "From: {title}": "Din: {title}", + "What needs to happen": "Ce trebuie să se întâmple", + "Could not create the action. Please try again.": "Acțiunea nu a putut fi creată. Încercați din nou.", + "The action was created but could not be linked to the entry.": "Acțiunea a fost creată, dar nu a putut fi legată de intrare.", + "Project pages": "Paginile proiectului", + "Overview": "Prezentare generală", + "Risks": "Riscuri", + "Log entry": "Intrare în jurnal", + "A short name for the entry": "Un nume scurt pentru intrare", + "Project": "Proiect", + "The project this entry belongs to": "Proiectul căruia îi aparține această intrare", + "What kind of entry this is": "Ce tip de intrare este", + "When it happened": "Când s-a întâmplat", + "What happened, in Markdown": "Ce s-a întâmplat, în Markdown", + "Whether an issue is still open": "Dacă o problemă este încă deschisă", + "The people at a meeting": "Persoanele de la o întâlnire", + "Tasks that came out of this entry": "Sarcini rezultate din această intrare", + "Add risk": "Adaugă un risc", + "Risk": "Risc", + "Score": "Scor", + "Response": "Răspuns", + "Review date": "Data revizuirii", + "no longer on the project": "nu mai face parte din proiect", + "Edit {title}": "Editează {title}", + "No risks recorded yet": "Încă nu există riscuri înregistrate", + "Add the first risk": "Adaugă primul risc", + "Medium": "Mediu", + "Mitigating": "În atenuare", + "Occurred": "S-a produs", + "Avoid": "Evitare", + "Reduce": "Reducere", + "Transfer": "Transfer", + "Accept": "Acceptare", + "Highest open risks": "Cele mai mari riscuri deschise", + "No open risks": "Niciun risc deschis", + "Open the risk register": "Deschide registrul de riscuri", + "Edit risk": "Editează riscul", + "What could go wrong": "Ce ar putea merge prost", + "Likelihood": "Probabilitate", + "Impact": "Impact", + "Score {score}": "Scor {score}", + "Countermeasures": "Contramăsuri", + "Could not save the risk. Please try again.": "Riscul nu a putut fi salvat. Încercați din nou.", + "Risks by likelihood and impact": "Riscuri după probabilitate și impact", + "Risk scale": "Scala de risc", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivelurile pe care fiecare proiect își punctează riscurile și unde scăzut trece în mediu și ridicat", + "Number of levels": "Numărul de niveluri", + "Likelihood labels": "Etichete pentru probabilitate", + "Impact labels": "Etichete pentru impact", + "Level {level}": "Nivelul {level}", + "Medium from score": "Mediu de la scorul", + "High from score": "Ridicat de la scorul", + "Risk scale saved": "Scala de risc a fost salvată", + "1 risk uses level {level}. Change it first.": "1 risc folosește nivelul {level}. Modificați-l mai întâi.", + "{count} risks use level {level}. Change them first.": "{count} riscuri folosesc nivelul {level}. Modificați-le mai întâi.", + "Every level needs a label, and high must start above medium.": "Fiecare nivel are nevoie de o etichetă, iar ridicat trebuie să înceapă peste mediu.", + "The project this risk belongs to": "Proiectul căruia îi aparține acest risc", + "The cause and the effect, in Markdown": "Cauza și efectul, în Markdown", + "Category": "Categorie", + "A free grouping, such as planning, budget or supplier": "O grupare liberă, cum ar fi planificare, buget sau furnizor", + "How likely it is, on the admin's risk scale": "Cât de probabil este, pe scala de risc a administratorului", + "How hard it would hit, on the admin's risk scale": "Cât de tare ar lovi, pe scala de risc a administratorului", + "Likelihood times impact, calculated by the server": "Probabilitate ori impact, calculat de server", + "Where the risk stands": "Unde se află riscul", + "The person who watches this risk": "Persoana care urmărește acest risc", + "How the project answers the risk": "Cum răspunde proiectul la risc", + "What is done to prevent it or soften it, in Markdown": "Ce se face pentru a-l preveni sau atenua, în Markdown", + "When to look at this risk again": "Când se revizuiește din nou acest risc", + "Very low": "Foarte scăzut", + "Very high": "Foarte ridicat", + "All projects": "Toate proiectele" }, "plurals": {} } diff --git a/l10n/ru.js b/l10n/ru.js index 928809ef..ac7dc30e 100644 --- a/l10n/ru.js +++ b/l10n/ru.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Доска", "List": "Список", "Column": "Столбец", - "Board or list": "Доска или список" + "Board or list": "Доска или список", + "Log": "Журнал", + "Add entry": "Добавить запись", + "Show": "Показать", + "No actions yet": "Действий пока нет", + "Closed": "Закрыто", + "Entry actions": "Действия с записью", + "Add action": "Добавить действие", + "by {author}": "автор {author}", + "saved {time}": "сохранено {time}", + "Attendees: {names}": "Присутствующие: {names}", + "Add the first entry": "Добавить первую запись", + "Issues": "Проблемы", + "Lessons learned": "Извлечённые уроки", + "Meetings": "Встречи", + "Decisions": "Решения", + "Actions": "Действия", + "Issue": "Проблема", + "Lesson learned": "Извлечённый урок", + "Meeting": "Встреча", + "Decision": "Решение", + "About this project": "Об этом проекте", + "No description yet": "Описания пока нет", + "Start date": "Дата начала", + "Not set": "Не задано", + "End date": "Дата окончания", + "Progress": "Ход работ", + "No tasks yet": "Задач пока нет", + "Open the board": "Открыть доску", + "People": "Люди", + "Owner": "Владелец", + "Latest log entries": "Последние записи", + "Open the log": "Открыть журнал", + "{done} of {total} tasks done": "Выполнено {done} из {total} задач", + "Edit entry": "Редактировать запись", + "Type": "Вид", + "Attendees": "Присутствующие", + "Issue is closed": "Проблема закрыта", + "Could not save the entry. Please try again.": "Не удалось сохранить запись. Попробуйте ещё раз.", + "From: {title}": "Из: {title}", + "What needs to happen": "Что нужно сделать", + "Could not create the action. Please try again.": "Не удалось создать действие. Попробуйте ещё раз.", + "The action was created but could not be linked to the entry.": "Действие создано, но его не удалось связать с записью.", + "Project pages": "Страницы проекта", + "Overview": "Обзор", + "Risks": "Риски", + "Log entry": "Запись журнала", + "A short name for the entry": "Краткое название записи", + "Project": "Проект", + "The project this entry belongs to": "Проект, к которому относится эта запись", + "What kind of entry this is": "Какой это вид записи", + "When it happened": "Когда это произошло", + "What happened, in Markdown": "Что произошло, в Markdown", + "Whether an issue is still open": "Открыта ли ещё проблема", + "The people at a meeting": "Люди на встрече", + "Tasks that came out of this entry": "Задачи, возникшие из этой записи", + "Add risk": "Добавить риск", + "Risk": "Риск", + "Score": "Оценка", + "Response": "Реагирование", + "Review date": "Дата пересмотра", + "no longer on the project": "больше не в проекте", + "Edit {title}": "Редактировать {title}", + "No risks recorded yet": "Рисков пока не записано", + "Add the first risk": "Добавить первый риск", + "Medium": "Средний", + "Mitigating": "Смягчается", + "Occurred": "Наступил", + "Avoid": "Избежать", + "Reduce": "Снизить", + "Transfer": "Передать", + "Accept": "Принять", + "Highest open risks": "Самые высокие открытые риски", + "No open risks": "Нет открытых рисков", + "Open the risk register": "Открыть реестр рисков", + "Edit risk": "Редактировать риск", + "What could go wrong": "Что может пойти не так", + "Likelihood": "Вероятность", + "Impact": "Влияние", + "Score {score}": "Оценка {score}", + "Countermeasures": "Контрмеры", + "Could not save the risk. Please try again.": "Не удалось сохранить риск. Попробуйте ещё раз.", + "Risks by likelihood and impact": "Риски по вероятности и влиянию", + "Risk scale": "Шкала рисков", + "The levels every project scores its risks on, and where low turns into medium and high": "Уровни, по которым каждый проект оценивает свои риски, и где низкий переходит в средний и высокий", + "Number of levels": "Число уровней", + "Likelihood labels": "Названия вероятности", + "Impact labels": "Названия влияния", + "Level {level}": "Уровень {level}", + "Medium from score": "Средний от оценки", + "High from score": "Высокий от оценки", + "Risk scale saved": "Шкала рисков сохранена", + "1 risk uses level {level}. Change it first.": "1 риск использует уровень {level}. Сначала измените его.", + "{count} risks use level {level}. Change them first.": "Рисков на уровне {level}: {count}. Сначала измените их.", + "Every level needs a label, and high must start above medium.": "Каждому уровню нужно название, а высокий должен начинаться выше среднего.", + "The project this risk belongs to": "Проект, к которому относится этот риск", + "The cause and the effect, in Markdown": "Причина и следствие, в Markdown", + "Category": "Категория", + "A free grouping, such as planning, budget or supplier": "Свободная группировка, например планирование, бюджет или поставщик", + "How likely it is, on the admin's risk scale": "Насколько это вероятно, по шкале рисков администратора", + "How hard it would hit, on the admin's risk scale": "Насколько сильно это ударит, по шкале рисков администратора", + "Likelihood times impact, calculated by the server": "Вероятность, умноженная на влияние, считает сервер", + "Where the risk stands": "На каком этапе риск", + "The person who watches this risk": "Человек, который следит за этим риском", + "How the project answers the risk": "Как проект реагирует на риск", + "What is done to prevent it or soften it, in Markdown": "Что делается, чтобы его предотвратить или смягчить, в Markdown", + "When to look at this risk again": "Когда снова пересмотреть этот риск", + "Very low": "Очень низкий", + "Very high": "Очень высокий", + "All projects": "Все проекты" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ru.json b/l10n/ru.json index f115af33..76c57e0e 100644 --- a/l10n/ru.json +++ b/l10n/ru.json @@ -308,7 +308,116 @@ "Board": "Доска", "List": "Список", "Column": "Столбец", - "Board or list": "Доска или список" + "Board or list": "Доска или список", + "Log": "Журнал", + "Add entry": "Добавить запись", + "Show": "Показать", + "No actions yet": "Действий пока нет", + "Closed": "Закрыто", + "Entry actions": "Действия с записью", + "Add action": "Добавить действие", + "by {author}": "автор {author}", + "saved {time}": "сохранено {time}", + "Attendees: {names}": "Присутствующие: {names}", + "Add the first entry": "Добавить первую запись", + "Issues": "Проблемы", + "Lessons learned": "Извлечённые уроки", + "Meetings": "Встречи", + "Decisions": "Решения", + "Actions": "Действия", + "Issue": "Проблема", + "Lesson learned": "Извлечённый урок", + "Meeting": "Встреча", + "Decision": "Решение", + "About this project": "Об этом проекте", + "No description yet": "Описания пока нет", + "Start date": "Дата начала", + "Not set": "Не задано", + "End date": "Дата окончания", + "Progress": "Ход работ", + "No tasks yet": "Задач пока нет", + "Open the board": "Открыть доску", + "People": "Люди", + "Owner": "Владелец", + "Latest log entries": "Последние записи", + "Open the log": "Открыть журнал", + "{done} of {total} tasks done": "Выполнено {done} из {total} задач", + "Edit entry": "Редактировать запись", + "Type": "Вид", + "Attendees": "Присутствующие", + "Issue is closed": "Проблема закрыта", + "Could not save the entry. Please try again.": "Не удалось сохранить запись. Попробуйте ещё раз.", + "From: {title}": "Из: {title}", + "What needs to happen": "Что нужно сделать", + "Could not create the action. Please try again.": "Не удалось создать действие. Попробуйте ещё раз.", + "The action was created but could not be linked to the entry.": "Действие создано, но его не удалось связать с записью.", + "Project pages": "Страницы проекта", + "Overview": "Обзор", + "Risks": "Риски", + "Log entry": "Запись журнала", + "A short name for the entry": "Краткое название записи", + "Project": "Проект", + "The project this entry belongs to": "Проект, к которому относится эта запись", + "What kind of entry this is": "Какой это вид записи", + "When it happened": "Когда это произошло", + "What happened, in Markdown": "Что произошло, в Markdown", + "Whether an issue is still open": "Открыта ли ещё проблема", + "The people at a meeting": "Люди на встрече", + "Tasks that came out of this entry": "Задачи, возникшие из этой записи", + "Add risk": "Добавить риск", + "Risk": "Риск", + "Score": "Оценка", + "Response": "Реагирование", + "Review date": "Дата пересмотра", + "no longer on the project": "больше не в проекте", + "Edit {title}": "Редактировать {title}", + "No risks recorded yet": "Рисков пока не записано", + "Add the first risk": "Добавить первый риск", + "Medium": "Средний", + "Mitigating": "Смягчается", + "Occurred": "Наступил", + "Avoid": "Избежать", + "Reduce": "Снизить", + "Transfer": "Передать", + "Accept": "Принять", + "Highest open risks": "Самые высокие открытые риски", + "No open risks": "Нет открытых рисков", + "Open the risk register": "Открыть реестр рисков", + "Edit risk": "Редактировать риск", + "What could go wrong": "Что может пойти не так", + "Likelihood": "Вероятность", + "Impact": "Влияние", + "Score {score}": "Оценка {score}", + "Countermeasures": "Контрмеры", + "Could not save the risk. Please try again.": "Не удалось сохранить риск. Попробуйте ещё раз.", + "Risks by likelihood and impact": "Риски по вероятности и влиянию", + "Risk scale": "Шкала рисков", + "The levels every project scores its risks on, and where low turns into medium and high": "Уровни, по которым каждый проект оценивает свои риски, и где низкий переходит в средний и высокий", + "Number of levels": "Число уровней", + "Likelihood labels": "Названия вероятности", + "Impact labels": "Названия влияния", + "Level {level}": "Уровень {level}", + "Medium from score": "Средний от оценки", + "High from score": "Высокий от оценки", + "Risk scale saved": "Шкала рисков сохранена", + "1 risk uses level {level}. Change it first.": "1 риск использует уровень {level}. Сначала измените его.", + "{count} risks use level {level}. Change them first.": "Рисков на уровне {level}: {count}. Сначала измените их.", + "Every level needs a label, and high must start above medium.": "Каждому уровню нужно название, а высокий должен начинаться выше среднего.", + "The project this risk belongs to": "Проект, к которому относится этот риск", + "The cause and the effect, in Markdown": "Причина и следствие, в Markdown", + "Category": "Категория", + "A free grouping, such as planning, budget or supplier": "Свободная группировка, например планирование, бюджет или поставщик", + "How likely it is, on the admin's risk scale": "Насколько это вероятно, по шкале рисков администратора", + "How hard it would hit, on the admin's risk scale": "Насколько сильно это ударит, по шкале рисков администратора", + "Likelihood times impact, calculated by the server": "Вероятность, умноженная на влияние, считает сервер", + "Where the risk stands": "На каком этапе риск", + "The person who watches this risk": "Человек, который следит за этим риском", + "How the project answers the risk": "Как проект реагирует на риск", + "What is done to prevent it or soften it, in Markdown": "Что делается, чтобы его предотвратить или смягчить, в Markdown", + "When to look at this risk again": "Когда снова пересмотреть этот риск", + "Very low": "Очень низкий", + "Very high": "Очень высокий", + "All projects": "Все проекты" }, "plurals": {} } diff --git a/l10n/sk.js b/l10n/sk.js index e93b537a..fa766914 100644 --- a/l10n/sk.js +++ b/l10n/sk.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Nástenka", "List": "Zoznam", "Column": "Stĺpec", - "Board or list": "Nástenka alebo zoznam" + "Board or list": "Nástenka alebo zoznam", + "Log": "Denník", + "Add entry": "Pridať záznam", + "Show": "Zobraziť", + "No actions yet": "Zatiaľ žiadne akcie", + "Closed": "Uzavreté", + "Entry actions": "Akcie záznamu", + "Add action": "Pridať akciu", + "by {author}": "autor {author}", + "saved {time}": "uložené {time}", + "Attendees: {names}": "Účastníci: {names}", + "Add the first entry": "Pridať prvý záznam", + "Issues": "Problémy", + "Lessons learned": "Poučenia", + "Meetings": "Stretnutia", + "Decisions": "Rozhodnutia", + "Actions": "Akcie", + "Issue": "Problém", + "Lesson learned": "Poučenie", + "Meeting": "Stretnutie", + "Decision": "Rozhodnutie", + "About this project": "O tomto projekte", + "No description yet": "Zatiaľ bez popisu", + "Start date": "Dátum začiatku", + "Not set": "Nenastavené", + "End date": "Dátum ukončenia", + "Progress": "Postup", + "No tasks yet": "Zatiaľ žiadne úlohy", + "Open the board": "Otvoriť nástenku", + "People": "Ľudia", + "Owner": "Vlastník", + "Latest log entries": "Najnovšie záznamy", + "Open the log": "Otvoriť denník", + "{done} of {total} tasks done": "Hotovo {done} z {total} úloh", + "Edit entry": "Upraviť záznam", + "Type": "Druh", + "Attendees": "Účastníci", + "Issue is closed": "Problém je uzavretý", + "Could not save the entry. Please try again.": "Záznam sa nepodarilo uložiť. Skúste to znova.", + "From: {title}": "Z: {title}", + "What needs to happen": "Čo treba urobiť", + "Could not create the action. Please try again.": "Akciu sa nepodarilo vytvoriť. Skúste to znova.", + "The action was created but could not be linked to the entry.": "Akcia bola vytvorená, ale nepodarilo sa ju prepojiť so záznamom.", + "Project pages": "Stránky projektu", + "Overview": "Prehľad", + "Risks": "Riziká", + "Log entry": "Záznam denníka", + "A short name for the entry": "Krátky názov záznamu", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, ku ktorému záznam patrí", + "What kind of entry this is": "Aký je to druh záznamu", + "When it happened": "Kedy sa to stalo", + "What happened, in Markdown": "Čo sa stalo, v Markdowne", + "Whether an issue is still open": "Či je problém stále otvorený", + "The people at a meeting": "Ľudia na stretnutí", + "Tasks that came out of this entry": "Úlohy, ktoré vzišli z tohto záznamu", + "Add risk": "Pridať riziko", + "Risk": "Riziko", + "Score": "Skóre", + "Response": "Reakcia", + "Review date": "Dátum revízie", + "no longer on the project": "už nie je v projekte", + "Edit {title}": "Upraviť {title}", + "No risks recorded yet": "Zatiaľ žiadne zaznamenané riziká", + "Add the first risk": "Pridať prvé riziko", + "Medium": "Stredné", + "Mitigating": "Zmierňuje sa", + "Occurred": "Nastalo", + "Avoid": "Vyhnúť sa", + "Reduce": "Znížiť", + "Transfer": "Preniesť", + "Accept": "Prijať", + "Highest open risks": "Najvyššie otvorené riziká", + "No open risks": "Žiadne otvorené riziká", + "Open the risk register": "Otvoriť register rizík", + "Edit risk": "Upraviť riziko", + "What could go wrong": "Čo sa môže pokaziť", + "Likelihood": "Pravdepodobnosť", + "Impact": "Dopad", + "Score {score}": "Skóre {score}", + "Countermeasures": "Protiopatrenia", + "Could not save the risk. Please try again.": "Riziko sa nepodarilo uložiť. Skúste to znova.", + "Risks by likelihood and impact": "Riziká podľa pravdepodobnosti a dopadu", + "Risk scale": "Škála rizík", + "The levels every project scores its risks on, and where low turns into medium and high": "Úrovne, na ktorých každý projekt hodnotí svoje riziká, a kde nízke prechádza do stredného a vysokého", + "Number of levels": "Počet úrovní", + "Likelihood labels": "Označenia pravdepodobnosti", + "Impact labels": "Označenia dopadu", + "Level {level}": "Úroveň {level}", + "Medium from score": "Stredné od skóre", + "High from score": "Vysoké od skóre", + "Risk scale saved": "Škála rizík uložená", + "1 risk uses level {level}. Change it first.": "1 riziko používa úroveň {level}. Najprv ho zmeňte.", + "{count} risks use level {level}. Change them first.": "{count} rizík používa úroveň {level}. Najprv ich zmeňte.", + "Every level needs a label, and high must start above medium.": "Každá úroveň potrebuje označenie a vysoké musí začínať nad stredným.", + "The project this risk belongs to": "Projekt, ku ktorému riziko patrí", + "The cause and the effect, in Markdown": "Príčina a následok, v Markdowne", + "Category": "Kategória", + "A free grouping, such as planning, budget or supplier": "Voľné zoskupenie, napr. plánovanie, rozpočet alebo dodávateľ", + "How likely it is, on the admin's risk scale": "Aké je pravdepodobné, na škále rizík správcu", + "How hard it would hit, on the admin's risk scale": "Ako tvrdo by zasiahlo, na škále rizík správcu", + "Likelihood times impact, calculated by the server": "Pravdepodobnosť krát dopad, vypočítané serverom", + "Where the risk stands": "Ako je na tom riziko", + "The person who watches this risk": "Osoba, ktorá riziko sleduje", + "How the project answers the risk": "Ako projekt na riziko reaguje", + "What is done to prevent it or soften it, in Markdown": "Čo sa robí na jeho prevenciu alebo zmiernenie, v Markdowne", + "When to look at this risk again": "Kedy riziko znova posúdiť", + "Very low": "Veľmi nízke", + "Very high": "Veľmi vysoké", + "All projects": "Všetky projekty" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sk.json b/l10n/sk.json index 04b41fe7..e1dbceed 100644 --- a/l10n/sk.json +++ b/l10n/sk.json @@ -308,7 +308,116 @@ "Board": "Nástenka", "List": "Zoznam", "Column": "Stĺpec", - "Board or list": "Nástenka alebo zoznam" + "Board or list": "Nástenka alebo zoznam", + "Log": "Denník", + "Add entry": "Pridať záznam", + "Show": "Zobraziť", + "No actions yet": "Zatiaľ žiadne akcie", + "Closed": "Uzavreté", + "Entry actions": "Akcie záznamu", + "Add action": "Pridať akciu", + "by {author}": "autor {author}", + "saved {time}": "uložené {time}", + "Attendees: {names}": "Účastníci: {names}", + "Add the first entry": "Pridať prvý záznam", + "Issues": "Problémy", + "Lessons learned": "Poučenia", + "Meetings": "Stretnutia", + "Decisions": "Rozhodnutia", + "Actions": "Akcie", + "Issue": "Problém", + "Lesson learned": "Poučenie", + "Meeting": "Stretnutie", + "Decision": "Rozhodnutie", + "About this project": "O tomto projekte", + "No description yet": "Zatiaľ bez popisu", + "Start date": "Dátum začiatku", + "Not set": "Nenastavené", + "End date": "Dátum ukončenia", + "Progress": "Postup", + "No tasks yet": "Zatiaľ žiadne úlohy", + "Open the board": "Otvoriť nástenku", + "People": "Ľudia", + "Owner": "Vlastník", + "Latest log entries": "Najnovšie záznamy", + "Open the log": "Otvoriť denník", + "{done} of {total} tasks done": "Hotovo {done} z {total} úloh", + "Edit entry": "Upraviť záznam", + "Type": "Druh", + "Attendees": "Účastníci", + "Issue is closed": "Problém je uzavretý", + "Could not save the entry. Please try again.": "Záznam sa nepodarilo uložiť. Skúste to znova.", + "From: {title}": "Z: {title}", + "What needs to happen": "Čo treba urobiť", + "Could not create the action. Please try again.": "Akciu sa nepodarilo vytvoriť. Skúste to znova.", + "The action was created but could not be linked to the entry.": "Akcia bola vytvorená, ale nepodarilo sa ju prepojiť so záznamom.", + "Project pages": "Stránky projektu", + "Overview": "Prehľad", + "Risks": "Riziká", + "Log entry": "Záznam denníka", + "A short name for the entry": "Krátky názov záznamu", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, ku ktorému záznam patrí", + "What kind of entry this is": "Aký je to druh záznamu", + "When it happened": "Kedy sa to stalo", + "What happened, in Markdown": "Čo sa stalo, v Markdowne", + "Whether an issue is still open": "Či je problém stále otvorený", + "The people at a meeting": "Ľudia na stretnutí", + "Tasks that came out of this entry": "Úlohy, ktoré vzišli z tohto záznamu", + "Add risk": "Pridať riziko", + "Risk": "Riziko", + "Score": "Skóre", + "Response": "Reakcia", + "Review date": "Dátum revízie", + "no longer on the project": "už nie je v projekte", + "Edit {title}": "Upraviť {title}", + "No risks recorded yet": "Zatiaľ žiadne zaznamenané riziká", + "Add the first risk": "Pridať prvé riziko", + "Medium": "Stredné", + "Mitigating": "Zmierňuje sa", + "Occurred": "Nastalo", + "Avoid": "Vyhnúť sa", + "Reduce": "Znížiť", + "Transfer": "Preniesť", + "Accept": "Prijať", + "Highest open risks": "Najvyššie otvorené riziká", + "No open risks": "Žiadne otvorené riziká", + "Open the risk register": "Otvoriť register rizík", + "Edit risk": "Upraviť riziko", + "What could go wrong": "Čo sa môže pokaziť", + "Likelihood": "Pravdepodobnosť", + "Impact": "Dopad", + "Score {score}": "Skóre {score}", + "Countermeasures": "Protiopatrenia", + "Could not save the risk. Please try again.": "Riziko sa nepodarilo uložiť. Skúste to znova.", + "Risks by likelihood and impact": "Riziká podľa pravdepodobnosti a dopadu", + "Risk scale": "Škála rizík", + "The levels every project scores its risks on, and where low turns into medium and high": "Úrovne, na ktorých každý projekt hodnotí svoje riziká, a kde nízke prechádza do stredného a vysokého", + "Number of levels": "Počet úrovní", + "Likelihood labels": "Označenia pravdepodobnosti", + "Impact labels": "Označenia dopadu", + "Level {level}": "Úroveň {level}", + "Medium from score": "Stredné od skóre", + "High from score": "Vysoké od skóre", + "Risk scale saved": "Škála rizík uložená", + "1 risk uses level {level}. Change it first.": "1 riziko používa úroveň {level}. Najprv ho zmeňte.", + "{count} risks use level {level}. Change them first.": "{count} rizík používa úroveň {level}. Najprv ich zmeňte.", + "Every level needs a label, and high must start above medium.": "Každá úroveň potrebuje označenie a vysoké musí začínať nad stredným.", + "The project this risk belongs to": "Projekt, ku ktorému riziko patrí", + "The cause and the effect, in Markdown": "Príčina a následok, v Markdowne", + "Category": "Kategória", + "A free grouping, such as planning, budget or supplier": "Voľné zoskupenie, napr. plánovanie, rozpočet alebo dodávateľ", + "How likely it is, on the admin's risk scale": "Aké je pravdepodobné, na škále rizík správcu", + "How hard it would hit, on the admin's risk scale": "Ako tvrdo by zasiahlo, na škále rizík správcu", + "Likelihood times impact, calculated by the server": "Pravdepodobnosť krát dopad, vypočítané serverom", + "Where the risk stands": "Ako je na tom riziko", + "The person who watches this risk": "Osoba, ktorá riziko sleduje", + "How the project answers the risk": "Ako projekt na riziko reaguje", + "What is done to prevent it or soften it, in Markdown": "Čo sa robí na jeho prevenciu alebo zmiernenie, v Markdowne", + "When to look at this risk again": "Kedy riziko znova posúdiť", + "Very low": "Veľmi nízke", + "Very high": "Veľmi vysoké", + "All projects": "Všetky projekty" }, "plurals": null } diff --git a/l10n/sl.js b/l10n/sl.js index a063ed52..22fc4e12 100644 --- a/l10n/sl.js +++ b/l10n/sl.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tabla", "List": "Seznam", "Column": "Stolpec", - "Board or list": "Tabla ali seznam" + "Board or list": "Tabla ali seznam", + "Log": "Dnevnik", + "Add entry": "Dodaj vnos", + "Show": "Prikaži", + "No actions yet": "Še ni dejanj", + "Closed": "Zaprto", + "Entry actions": "Dejanja vnosa", + "Add action": "Dodaj dejanje", + "by {author}": "avtor {author}", + "saved {time}": "shranjeno {time}", + "Attendees: {names}": "Prisotni: {names}", + "Add the first entry": "Dodaj prvi vnos", + "Issues": "Težave", + "Lessons learned": "Naučene lekcije", + "Meetings": "Sestanki", + "Decisions": "Odločitve", + "Actions": "Dejanja", + "Issue": "Težava", + "Lesson learned": "Naučena lekcija", + "Meeting": "Sestanek", + "Decision": "Odločitev", + "About this project": "O tem projektu", + "No description yet": "Še ni opisa", + "Start date": "Datum začetka", + "Not set": "Ni nastavljeno", + "End date": "Datum konca", + "Progress": "Napredek", + "No tasks yet": "Še ni opravil", + "Open the board": "Odpri tablo", + "People": "Ljudje", + "Owner": "Lastnik", + "Latest log entries": "Najnovejši vnosi", + "Open the log": "Odpri dnevnik", + "{done} of {total} tasks done": "Končanih {done} od {total} opravil", + "Edit entry": "Uredi vnos", + "Type": "Vrsta", + "Attendees": "Prisotni", + "Issue is closed": "Težava je zaprta", + "Could not save the entry. Please try again.": "Vnosa ni bilo mogoče shraniti. Poskusite znova.", + "From: {title}": "Iz: {title}", + "What needs to happen": "Kaj je treba narediti", + "Could not create the action. Please try again.": "Dejanja ni bilo mogoče ustvariti. Poskusite znova.", + "The action was created but could not be linked to the entry.": "Dejanje je ustvarjeno, vendar ga ni bilo mogoče povezati z vnosom.", + "Project pages": "Strani projekta", + "Overview": "Pregled", + "Risks": "Tveganja", + "Log entry": "Vnos v dnevnik", + "A short name for the entry": "Kratko ime vnosa", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, ki mu vnos pripada", + "What kind of entry this is": "Kakšne vrste je vnos", + "When it happened": "Kdaj se je zgodilo", + "What happened, in Markdown": "Kaj se je zgodilo, v Markdownu", + "Whether an issue is still open": "Ali je težava še odprta", + "The people at a meeting": "Ljudje na sestanku", + "Tasks that came out of this entry": "Opravila, ki izhajajo iz tega vnosa", + "Add risk": "Dodaj tveganje", + "Risk": "Tveganje", + "Score": "Ocena", + "Response": "Odziv", + "Review date": "Datum pregleda", + "no longer on the project": "ni več na projektu", + "Edit {title}": "Uredi {title}", + "No risks recorded yet": "Še ni zabeleženih tveganj", + "Add the first risk": "Dodaj prvo tveganje", + "Medium": "Srednje", + "Mitigating": "V blaženju", + "Occurred": "Nastopilo", + "Avoid": "Izogniti se", + "Reduce": "Zmanjšati", + "Transfer": "Prenesti", + "Accept": "Sprejeti", + "Highest open risks": "Najvišja odprta tveganja", + "No open risks": "Ni odprtih tveganj", + "Open the risk register": "Odpri register tveganj", + "Edit risk": "Uredi tveganje", + "What could go wrong": "Kaj lahko gre narobe", + "Likelihood": "Verjetnost", + "Impact": "Vpliv", + "Score {score}": "Ocena {score}", + "Countermeasures": "Protiukrepi", + "Could not save the risk. Please try again.": "Tveganja ni bilo mogoče shraniti. Poskusite znova.", + "Risks by likelihood and impact": "Tveganja po verjetnosti in vplivu", + "Risk scale": "Lestvica tveganj", + "The levels every project scores its risks on, and where low turns into medium and high": "Ravni, na katerih vsak projekt ocenjuje svoja tveganja, in kje nizko preide v srednje in visoko", + "Number of levels": "Število ravni", + "Likelihood labels": "Oznake verjetnosti", + "Impact labels": "Oznake vpliva", + "Level {level}": "Raven {level}", + "Medium from score": "Srednje od ocene", + "High from score": "Visoko od ocene", + "Risk scale saved": "Lestvica tveganj je shranjena", + "1 risk uses level {level}. Change it first.": "1 tveganje uporablja raven {level}. Najprej ga spremenite.", + "{count} risks use level {level}. Change them first.": "Tveganja z ravnjo {level}: {count}. Najprej jih spremenite.", + "Every level needs a label, and high must start above medium.": "Vsaka raven potrebuje oznako, visoko pa se mora začeti nad srednjim.", + "The project this risk belongs to": "Projekt, ki mu tveganje pripada", + "The cause and the effect, in Markdown": "Vzrok in posledica, v Markdownu", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Prosto združevanje, npr. načrtovanje, proračun ali dobavitelj", + "How likely it is, on the admin's risk scale": "Kako verjetno je, na lestvici tveganj skrbnika", + "How hard it would hit, on the admin's risk scale": "Kako močno bi prizadelo, na lestvici tveganj skrbnika", + "Likelihood times impact, calculated by the server": "Verjetnost krat vpliv, izračuna strežnik", + "Where the risk stands": "Kje je tveganje", + "The person who watches this risk": "Oseba, ki spremlja to tveganje", + "How the project answers the risk": "Kako se projekt odziva na tveganje", + "What is done to prevent it or soften it, in Markdown": "Kaj se naredi, da se prepreči ali omili, v Markdownu", + "When to look at this risk again": "Kdaj tveganje znova pregledati", + "Very low": "Zelo nizko", + "Very high": "Zelo visoko", + "All projects": "Vsi projekti" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sl.json b/l10n/sl.json index 5b9d8ebf..263ce903 100644 --- a/l10n/sl.json +++ b/l10n/sl.json @@ -308,7 +308,116 @@ "Board": "Tabla", "List": "Seznam", "Column": "Stolpec", - "Board or list": "Tabla ali seznam" + "Board or list": "Tabla ali seznam", + "Log": "Dnevnik", + "Add entry": "Dodaj vnos", + "Show": "Prikaži", + "No actions yet": "Še ni dejanj", + "Closed": "Zaprto", + "Entry actions": "Dejanja vnosa", + "Add action": "Dodaj dejanje", + "by {author}": "avtor {author}", + "saved {time}": "shranjeno {time}", + "Attendees: {names}": "Prisotni: {names}", + "Add the first entry": "Dodaj prvi vnos", + "Issues": "Težave", + "Lessons learned": "Naučene lekcije", + "Meetings": "Sestanki", + "Decisions": "Odločitve", + "Actions": "Dejanja", + "Issue": "Težava", + "Lesson learned": "Naučena lekcija", + "Meeting": "Sestanek", + "Decision": "Odločitev", + "About this project": "O tem projektu", + "No description yet": "Še ni opisa", + "Start date": "Datum začetka", + "Not set": "Ni nastavljeno", + "End date": "Datum konca", + "Progress": "Napredek", + "No tasks yet": "Še ni opravil", + "Open the board": "Odpri tablo", + "People": "Ljudje", + "Owner": "Lastnik", + "Latest log entries": "Najnovejši vnosi", + "Open the log": "Odpri dnevnik", + "{done} of {total} tasks done": "Končanih {done} od {total} opravil", + "Edit entry": "Uredi vnos", + "Type": "Vrsta", + "Attendees": "Prisotni", + "Issue is closed": "Težava je zaprta", + "Could not save the entry. Please try again.": "Vnosa ni bilo mogoče shraniti. Poskusite znova.", + "From: {title}": "Iz: {title}", + "What needs to happen": "Kaj je treba narediti", + "Could not create the action. Please try again.": "Dejanja ni bilo mogoče ustvariti. Poskusite znova.", + "The action was created but could not be linked to the entry.": "Dejanje je ustvarjeno, vendar ga ni bilo mogoče povezati z vnosom.", + "Project pages": "Strani projekta", + "Overview": "Pregled", + "Risks": "Tveganja", + "Log entry": "Vnos v dnevnik", + "A short name for the entry": "Kratko ime vnosa", + "Project": "Projekt", + "The project this entry belongs to": "Projekt, ki mu vnos pripada", + "What kind of entry this is": "Kakšne vrste je vnos", + "When it happened": "Kdaj se je zgodilo", + "What happened, in Markdown": "Kaj se je zgodilo, v Markdownu", + "Whether an issue is still open": "Ali je težava še odprta", + "The people at a meeting": "Ljudje na sestanku", + "Tasks that came out of this entry": "Opravila, ki izhajajo iz tega vnosa", + "Add risk": "Dodaj tveganje", + "Risk": "Tveganje", + "Score": "Ocena", + "Response": "Odziv", + "Review date": "Datum pregleda", + "no longer on the project": "ni več na projektu", + "Edit {title}": "Uredi {title}", + "No risks recorded yet": "Še ni zabeleženih tveganj", + "Add the first risk": "Dodaj prvo tveganje", + "Medium": "Srednje", + "Mitigating": "V blaženju", + "Occurred": "Nastopilo", + "Avoid": "Izogniti se", + "Reduce": "Zmanjšati", + "Transfer": "Prenesti", + "Accept": "Sprejeti", + "Highest open risks": "Najvišja odprta tveganja", + "No open risks": "Ni odprtih tveganj", + "Open the risk register": "Odpri register tveganj", + "Edit risk": "Uredi tveganje", + "What could go wrong": "Kaj lahko gre narobe", + "Likelihood": "Verjetnost", + "Impact": "Vpliv", + "Score {score}": "Ocena {score}", + "Countermeasures": "Protiukrepi", + "Could not save the risk. Please try again.": "Tveganja ni bilo mogoče shraniti. Poskusite znova.", + "Risks by likelihood and impact": "Tveganja po verjetnosti in vplivu", + "Risk scale": "Lestvica tveganj", + "The levels every project scores its risks on, and where low turns into medium and high": "Ravni, na katerih vsak projekt ocenjuje svoja tveganja, in kje nizko preide v srednje in visoko", + "Number of levels": "Število ravni", + "Likelihood labels": "Oznake verjetnosti", + "Impact labels": "Oznake vpliva", + "Level {level}": "Raven {level}", + "Medium from score": "Srednje od ocene", + "High from score": "Visoko od ocene", + "Risk scale saved": "Lestvica tveganj je shranjena", + "1 risk uses level {level}. Change it first.": "1 tveganje uporablja raven {level}. Najprej ga spremenite.", + "{count} risks use level {level}. Change them first.": "Tveganja z ravnjo {level}: {count}. Najprej jih spremenite.", + "Every level needs a label, and high must start above medium.": "Vsaka raven potrebuje oznako, visoko pa se mora začeti nad srednjim.", + "The project this risk belongs to": "Projekt, ki mu tveganje pripada", + "The cause and the effect, in Markdown": "Vzrok in posledica, v Markdownu", + "Category": "Kategorija", + "A free grouping, such as planning, budget or supplier": "Prosto združevanje, npr. načrtovanje, proračun ali dobavitelj", + "How likely it is, on the admin's risk scale": "Kako verjetno je, na lestvici tveganj skrbnika", + "How hard it would hit, on the admin's risk scale": "Kako močno bi prizadelo, na lestvici tveganj skrbnika", + "Likelihood times impact, calculated by the server": "Verjetnost krat vpliv, izračuna strežnik", + "Where the risk stands": "Kje je tveganje", + "The person who watches this risk": "Oseba, ki spremlja to tveganje", + "How the project answers the risk": "Kako se projekt odziva na tveganje", + "What is done to prevent it or soften it, in Markdown": "Kaj se naredi, da se prepreči ali omili, v Markdownu", + "When to look at this risk again": "Kdaj tveganje znova pregledati", + "Very low": "Zelo nizko", + "Very high": "Zelo visoko", + "All projects": "Vsi projekti" }, "plurals": null } diff --git a/l10n/sq.js b/l10n/sq.js index ea7008fd..e88f69b4 100644 --- a/l10n/sq.js +++ b/l10n/sq.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tabela", "List": "Lista", "Column": "Kolona", - "Board or list": "Tabela ose lista" + "Board or list": "Tabela ose lista", + "Log": "Regjistri", + "Add entry": "Shto hyrje", + "Show": "Shfaq", + "No actions yet": "Ende nuk ka veprime", + "Closed": "Mbyllur", + "Entry actions": "Veprimet e hyrjes", + "Add action": "Shto veprim", + "by {author}": "nga {author}", + "saved {time}": "ruajtur {time}", + "Attendees: {names}": "Të pranishëm: {names}", + "Add the first entry": "Shto hyrjen e parë", + "Issues": "Çështje", + "Lessons learned": "Mësime të nxjerra", + "Meetings": "Takime", + "Decisions": "Vendime", + "Actions": "Veprime", + "Issue": "Çështje", + "Lesson learned": "Mësim i nxjerrë", + "Meeting": "Takim", + "Decision": "Vendim", + "About this project": "Rreth këtij projekti", + "No description yet": "Ende pa përshkrim", + "Start date": "Data e fillimit", + "Not set": "E pacaktuar", + "End date": "Data e mbarimit", + "Progress": "Ecuria", + "No tasks yet": "Ende nuk ka detyra", + "Open the board": "Hap tabelën", + "People": "Njerëz", + "Owner": "Pronari", + "Latest log entries": "Hyrjet më të fundit", + "Open the log": "Hap regjistrin", + "{done} of {total} tasks done": "{done} nga {total} detyra të kryera", + "Edit entry": "Ndrysho hyrjen", + "Type": "Lloji", + "Attendees": "Të pranishëm", + "Issue is closed": "Çështja është mbyllur", + "Could not save the entry. Please try again.": "Hyrja nuk u ruajt dot. Provoni përsëri.", + "From: {title}": "Nga: {title}", + "What needs to happen": "Çfarë duhet bërë", + "Could not create the action. Please try again.": "Veprimi nuk u krijua dot. Provoni përsëri.", + "The action was created but could not be linked to the entry.": "Veprimi u krijua, por nuk u lidh dot me hyrjen.", + "Project pages": "Faqet e projektit", + "Overview": "Përmbledhje", + "Risks": "Rreziqe", + "Log entry": "Hyrje regjistri", + "A short name for the entry": "Një emër i shkurtër për hyrjen", + "Project": "Projekt", + "The project this entry belongs to": "Projekti të cilit i përket kjo hyrje", + "What kind of entry this is": "Çfarë lloj hyrjeje është", + "When it happened": "Kur ndodhi", + "What happened, in Markdown": "Çfarë ndodhi, në Markdown", + "Whether an issue is still open": "Nëse një çështje është ende e hapur", + "The people at a meeting": "Njerëzit në një takim", + "Tasks that came out of this entry": "Detyrat që dolën nga kjo hyrje", + "Add risk": "Shto rrezik", + "Risk": "Rrezik", + "Score": "Pikët", + "Response": "Përgjigjja", + "Review date": "Data e rishikimit", + "no longer on the project": "nuk është më në projekt", + "Edit {title}": "Ndrysho {title}", + "No risks recorded yet": "Ende nuk ka rreziqe të regjistruara", + "Add the first risk": "Shto rrezikun e parë", + "Medium": "Mesatar", + "Mitigating": "Në zbutje", + "Occurred": "Ndodhi", + "Avoid": "Shmang", + "Reduce": "Ul", + "Transfer": "Transfero", + "Accept": "Prano", + "Highest open risks": "Rreziqet e hapura më të larta", + "No open risks": "Nuk ka rreziqe të hapura", + "Open the risk register": "Hap regjistrin e rreziqeve", + "Edit risk": "Ndrysho rrezikun", + "What could go wrong": "Çfarë mund të shkojë keq", + "Likelihood": "Gjasat", + "Impact": "Ndikimi", + "Score {score}": "Pikët {score}", + "Countermeasures": "Kundërmasat", + "Could not save the risk. Please try again.": "Rreziku nuk u ruajt dot. Provoni përsëri.", + "Risks by likelihood and impact": "Rreziqet sipas gjasave dhe ndikimit", + "Risk scale": "Shkalla e rrezikut", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivelet mbi të cilat çdo projekt pikëzon rreziqet e veta, dhe ku i ulëti kalon në mesatar dhe të lartë", + "Number of levels": "Numri i niveleve", + "Likelihood labels": "Etiketat e gjasave", + "Impact labels": "Etiketat e ndikimit", + "Level {level}": "Niveli {level}", + "Medium from score": "Mesatar nga pikët", + "High from score": "I lartë nga pikët", + "Risk scale saved": "Shkalla e rrezikut u ruajt", + "1 risk uses level {level}. Change it first.": "1 rrezik përdor nivelin {level}. Ndryshojeni më parë.", + "{count} risks use level {level}. Change them first.": "{count} rreziqe përdorin nivelin {level}. Ndryshojini më parë.", + "Every level needs a label, and high must start above medium.": "Çdo nivel ka nevojë për etiketë, dhe i larti duhet të fillojë mbi mesataren.", + "The project this risk belongs to": "Projekti të cilit i përket ky rrezik", + "The cause and the effect, in Markdown": "Shkaku dhe pasoja, në Markdown", + "Category": "Kategoria", + "A free grouping, such as planning, budget or supplier": "Një grupim i lirë, si planifikim, buxhet ose furnizues", + "How likely it is, on the admin's risk scale": "Sa i mundshëm është, në shkallën e rrezikut të administratorit", + "How hard it would hit, on the admin's risk scale": "Sa rëndë do të godiste, në shkallën e rrezikut të administratorit", + "Likelihood times impact, calculated by the server": "Gjasat herë ndikimin, llogaritur nga serveri", + "Where the risk stands": "Ku ndodhet rreziku", + "The person who watches this risk": "Personi që ndjek këtë rrezik", + "How the project answers the risk": "Si i përgjigjet projekti rrezikut", + "What is done to prevent it or soften it, in Markdown": "Çfarë bëhet për ta parandaluar ose zbutur, në Markdown", + "When to look at this risk again": "Kur të rishikohet përsëri ky rrezik", + "Very low": "Shumë i ulët", + "Very high": "Shumë i lartë", + "All projects": "Të gjitha projektet" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sq.json b/l10n/sq.json index 0d6736e6..94532f19 100644 --- a/l10n/sq.json +++ b/l10n/sq.json @@ -308,7 +308,116 @@ "Board": "Tabela", "List": "Lista", "Column": "Kolona", - "Board or list": "Tabela ose lista" + "Board or list": "Tabela ose lista", + "Log": "Regjistri", + "Add entry": "Shto hyrje", + "Show": "Shfaq", + "No actions yet": "Ende nuk ka veprime", + "Closed": "Mbyllur", + "Entry actions": "Veprimet e hyrjes", + "Add action": "Shto veprim", + "by {author}": "nga {author}", + "saved {time}": "ruajtur {time}", + "Attendees: {names}": "Të pranishëm: {names}", + "Add the first entry": "Shto hyrjen e parë", + "Issues": "Çështje", + "Lessons learned": "Mësime të nxjerra", + "Meetings": "Takime", + "Decisions": "Vendime", + "Actions": "Veprime", + "Issue": "Çështje", + "Lesson learned": "Mësim i nxjerrë", + "Meeting": "Takim", + "Decision": "Vendim", + "About this project": "Rreth këtij projekti", + "No description yet": "Ende pa përshkrim", + "Start date": "Data e fillimit", + "Not set": "E pacaktuar", + "End date": "Data e mbarimit", + "Progress": "Ecuria", + "No tasks yet": "Ende nuk ka detyra", + "Open the board": "Hap tabelën", + "People": "Njerëz", + "Owner": "Pronari", + "Latest log entries": "Hyrjet më të fundit", + "Open the log": "Hap regjistrin", + "{done} of {total} tasks done": "{done} nga {total} detyra të kryera", + "Edit entry": "Ndrysho hyrjen", + "Type": "Lloji", + "Attendees": "Të pranishëm", + "Issue is closed": "Çështja është mbyllur", + "Could not save the entry. Please try again.": "Hyrja nuk u ruajt dot. Provoni përsëri.", + "From: {title}": "Nga: {title}", + "What needs to happen": "Çfarë duhet bërë", + "Could not create the action. Please try again.": "Veprimi nuk u krijua dot. Provoni përsëri.", + "The action was created but could not be linked to the entry.": "Veprimi u krijua, por nuk u lidh dot me hyrjen.", + "Project pages": "Faqet e projektit", + "Overview": "Përmbledhje", + "Risks": "Rreziqe", + "Log entry": "Hyrje regjistri", + "A short name for the entry": "Një emër i shkurtër për hyrjen", + "Project": "Projekt", + "The project this entry belongs to": "Projekti të cilit i përket kjo hyrje", + "What kind of entry this is": "Çfarë lloj hyrjeje është", + "When it happened": "Kur ndodhi", + "What happened, in Markdown": "Çfarë ndodhi, në Markdown", + "Whether an issue is still open": "Nëse një çështje është ende e hapur", + "The people at a meeting": "Njerëzit në një takim", + "Tasks that came out of this entry": "Detyrat që dolën nga kjo hyrje", + "Add risk": "Shto rrezik", + "Risk": "Rrezik", + "Score": "Pikët", + "Response": "Përgjigjja", + "Review date": "Data e rishikimit", + "no longer on the project": "nuk është më në projekt", + "Edit {title}": "Ndrysho {title}", + "No risks recorded yet": "Ende nuk ka rreziqe të regjistruara", + "Add the first risk": "Shto rrezikun e parë", + "Medium": "Mesatar", + "Mitigating": "Në zbutje", + "Occurred": "Ndodhi", + "Avoid": "Shmang", + "Reduce": "Ul", + "Transfer": "Transfero", + "Accept": "Prano", + "Highest open risks": "Rreziqet e hapura më të larta", + "No open risks": "Nuk ka rreziqe të hapura", + "Open the risk register": "Hap regjistrin e rreziqeve", + "Edit risk": "Ndrysho rrezikun", + "What could go wrong": "Çfarë mund të shkojë keq", + "Likelihood": "Gjasat", + "Impact": "Ndikimi", + "Score {score}": "Pikët {score}", + "Countermeasures": "Kundërmasat", + "Could not save the risk. Please try again.": "Rreziku nuk u ruajt dot. Provoni përsëri.", + "Risks by likelihood and impact": "Rreziqet sipas gjasave dhe ndikimit", + "Risk scale": "Shkalla e rrezikut", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivelet mbi të cilat çdo projekt pikëzon rreziqet e veta, dhe ku i ulëti kalon në mesatar dhe të lartë", + "Number of levels": "Numri i niveleve", + "Likelihood labels": "Etiketat e gjasave", + "Impact labels": "Etiketat e ndikimit", + "Level {level}": "Niveli {level}", + "Medium from score": "Mesatar nga pikët", + "High from score": "I lartë nga pikët", + "Risk scale saved": "Shkalla e rrezikut u ruajt", + "1 risk uses level {level}. Change it first.": "1 rrezik përdor nivelin {level}. Ndryshojeni më parë.", + "{count} risks use level {level}. Change them first.": "{count} rreziqe përdorin nivelin {level}. Ndryshojini më parë.", + "Every level needs a label, and high must start above medium.": "Çdo nivel ka nevojë për etiketë, dhe i larti duhet të fillojë mbi mesataren.", + "The project this risk belongs to": "Projekti të cilit i përket ky rrezik", + "The cause and the effect, in Markdown": "Shkaku dhe pasoja, në Markdown", + "Category": "Kategoria", + "A free grouping, such as planning, budget or supplier": "Një grupim i lirë, si planifikim, buxhet ose furnizues", + "How likely it is, on the admin's risk scale": "Sa i mundshëm është, në shkallën e rrezikut të administratorit", + "How hard it would hit, on the admin's risk scale": "Sa rëndë do të godiste, në shkallën e rrezikut të administratorit", + "Likelihood times impact, calculated by the server": "Gjasat herë ndikimin, llogaritur nga serveri", + "Where the risk stands": "Ku ndodhet rreziku", + "The person who watches this risk": "Personi që ndjek këtë rrezik", + "How the project answers the risk": "Si i përgjigjet projekti rrezikut", + "What is done to prevent it or soften it, in Markdown": "Çfarë bëhet për ta parandaluar ose zbutur, në Markdown", + "When to look at this risk again": "Kur të rishikohet përsëri ky rrezik", + "Very low": "Shumë i ulët", + "Very high": "Shumë i lartë", + "All projects": "Të gjitha projektet" }, "plurals": {} } diff --git a/l10n/sr.js b/l10n/sr.js index 88e970a4..f6a15e61 100644 --- a/l10n/sr.js +++ b/l10n/sr.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Табла", "List": "Листа", "Column": "Колона", - "Board or list": "Табла или листа" + "Board or list": "Табла или листа", + "Log": "Дневник", + "Add entry": "Додај унос", + "Show": "Прикажи", + "No actions yet": "Још нема радњи", + "Closed": "Затворено", + "Entry actions": "Радње уноса", + "Add action": "Додај радњу", + "by {author}": "аутор {author}", + "saved {time}": "сачувано {time}", + "Attendees: {names}": "Присутни: {names}", + "Add the first entry": "Додај први унос", + "Issues": "Проблеми", + "Lessons learned": "Научене лекције", + "Meetings": "Састанци", + "Decisions": "Одлуке", + "Actions": "Радње", + "Issue": "Проблем", + "Lesson learned": "Научена лекција", + "Meeting": "Састанак", + "Decision": "Одлука", + "About this project": "О овом пројекту", + "No description yet": "Још нема описа", + "Start date": "Датум почетка", + "Not set": "Није постављено", + "End date": "Датум завршетка", + "Progress": "Напредак", + "No tasks yet": "Још нема задатака", + "Open the board": "Отвори таблу", + "People": "Особе", + "Owner": "Власник", + "Latest log entries": "Најновији уноси", + "Open the log": "Отвори дневник", + "{done} of {total} tasks done": "Готово {done} од {total} задатака", + "Edit entry": "Уреди унос", + "Type": "Врста", + "Attendees": "Присутни", + "Issue is closed": "Проблем је затворен", + "Could not save the entry. Please try again.": "Унос није могуће сачувати. Покушајте поново.", + "From: {title}": "Из: {title}", + "What needs to happen": "Шта треба урадити", + "Could not create the action. Please try again.": "Радњу није могуће направити. Покушајте поново.", + "The action was created but could not be linked to the entry.": "Радња је направљена, али није могуће повезати је са уносом.", + "Project pages": "Странице пројекта", + "Overview": "Преглед", + "Risks": "Ризици", + "Log entry": "Унос у дневник", + "A short name for the entry": "Кратак назив уноса", + "Project": "Пројекат", + "The project this entry belongs to": "Пројекат коме овај унос припада", + "What kind of entry this is": "Која је ово врста уноса", + "When it happened": "Када се догодило", + "What happened, in Markdown": "Шта се догодило, у Markdown-у", + "Whether an issue is still open": "Да ли је проблем још отворен", + "The people at a meeting": "Особе на састанку", + "Tasks that came out of this entry": "Задаци проистекли из овог уноса", + "Add risk": "Додај ризик", + "Risk": "Ризик", + "Score": "Оцена", + "Response": "Одговор", + "Review date": "Датум прегледа", + "no longer on the project": "више није на пројекту", + "Edit {title}": "Уреди {title}", + "No risks recorded yet": "Још нема забележених ризика", + "Add the first risk": "Додај први ризик", + "Medium": "Средње", + "Mitigating": "Ублажава се", + "Occurred": "Наступио", + "Avoid": "Избећи", + "Reduce": "Смањити", + "Transfer": "Пренети", + "Accept": "Прихватити", + "Highest open risks": "Највиши отворени ризици", + "No open risks": "Нема отворених ризика", + "Open the risk register": "Отвори регистар ризика", + "Edit risk": "Уреди ризик", + "What could go wrong": "Шта може поћи наопако", + "Likelihood": "Вероватноћа", + "Impact": "Утицај", + "Score {score}": "Оцена {score}", + "Countermeasures": "Противмере", + "Could not save the risk. Please try again.": "Ризик није могуће сачувати. Покушајте поново.", + "Risks by likelihood and impact": "Ризици по вероватноћи и утицају", + "Risk scale": "Скала ризика", + "The levels every project scores its risks on, and where low turns into medium and high": "Нивои на којима сваки пројекат оцењује своје ризике, и где ниско прелази у средње и високо", + "Number of levels": "Број нивоа", + "Likelihood labels": "Ознаке вероватноће", + "Impact labels": "Ознаке утицаја", + "Level {level}": "Ниво {level}", + "Medium from score": "Средње од оцене", + "High from score": "Високо од оцене", + "Risk scale saved": "Скала ризика је сачувана", + "1 risk uses level {level}. Change it first.": "1 ризик користи ниво {level}. Прво га промените.", + "{count} risks use level {level}. Change them first.": "Ризика који користе ниво {level}: {count}. Прво их промените.", + "Every level needs a label, and high must start above medium.": "Сваки ниво треба ознаку, а високо мора почети изнад средњег.", + "The project this risk belongs to": "Пројекат коме овај ризик припада", + "The cause and the effect, in Markdown": "Узрок и последица, у Markdown-у", + "Category": "Категорија", + "A free grouping, such as planning, budget or supplier": "Слободно груписање, нпр. планирање, буџет или добављач", + "How likely it is, on the admin's risk scale": "Колико је вероватно, на скали ризика администратора", + "How hard it would hit, on the admin's risk scale": "Колико би снажно погодило, на скали ризика администратора", + "Likelihood times impact, calculated by the server": "Вероватноћа пута утицај, израчунава сервер", + "Where the risk stands": "Где је ризик", + "The person who watches this risk": "Особа која прати овај ризик", + "How the project answers the risk": "Како пројекат одговара на ризик", + "What is done to prevent it or soften it, in Markdown": "Шта се ради да се спречи или ублажи, у Markdown-у", + "When to look at this risk again": "Када поново прегледати овај ризик", + "Very low": "Веома ниско", + "Very high": "Веома високо", + "All projects": "Сви пројекти" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sr.json b/l10n/sr.json index 0ea5f66f..438e7f76 100644 --- a/l10n/sr.json +++ b/l10n/sr.json @@ -308,7 +308,116 @@ "Board": "Табла", "List": "Листа", "Column": "Колона", - "Board or list": "Табла или листа" + "Board or list": "Табла или листа", + "Log": "Дневник", + "Add entry": "Додај унос", + "Show": "Прикажи", + "No actions yet": "Још нема радњи", + "Closed": "Затворено", + "Entry actions": "Радње уноса", + "Add action": "Додај радњу", + "by {author}": "аутор {author}", + "saved {time}": "сачувано {time}", + "Attendees: {names}": "Присутни: {names}", + "Add the first entry": "Додај први унос", + "Issues": "Проблеми", + "Lessons learned": "Научене лекције", + "Meetings": "Састанци", + "Decisions": "Одлуке", + "Actions": "Радње", + "Issue": "Проблем", + "Lesson learned": "Научена лекција", + "Meeting": "Састанак", + "Decision": "Одлука", + "About this project": "О овом пројекту", + "No description yet": "Још нема описа", + "Start date": "Датум почетка", + "Not set": "Није постављено", + "End date": "Датум завршетка", + "Progress": "Напредак", + "No tasks yet": "Још нема задатака", + "Open the board": "Отвори таблу", + "People": "Особе", + "Owner": "Власник", + "Latest log entries": "Најновији уноси", + "Open the log": "Отвори дневник", + "{done} of {total} tasks done": "Готово {done} од {total} задатака", + "Edit entry": "Уреди унос", + "Type": "Врста", + "Attendees": "Присутни", + "Issue is closed": "Проблем је затворен", + "Could not save the entry. Please try again.": "Унос није могуће сачувати. Покушајте поново.", + "From: {title}": "Из: {title}", + "What needs to happen": "Шта треба урадити", + "Could not create the action. Please try again.": "Радњу није могуће направити. Покушајте поново.", + "The action was created but could not be linked to the entry.": "Радња је направљена, али није могуће повезати је са уносом.", + "Project pages": "Странице пројекта", + "Overview": "Преглед", + "Risks": "Ризици", + "Log entry": "Унос у дневник", + "A short name for the entry": "Кратак назив уноса", + "Project": "Пројекат", + "The project this entry belongs to": "Пројекат коме овај унос припада", + "What kind of entry this is": "Која је ово врста уноса", + "When it happened": "Када се догодило", + "What happened, in Markdown": "Шта се догодило, у Markdown-у", + "Whether an issue is still open": "Да ли је проблем још отворен", + "The people at a meeting": "Особе на састанку", + "Tasks that came out of this entry": "Задаци проистекли из овог уноса", + "Add risk": "Додај ризик", + "Risk": "Ризик", + "Score": "Оцена", + "Response": "Одговор", + "Review date": "Датум прегледа", + "no longer on the project": "више није на пројекту", + "Edit {title}": "Уреди {title}", + "No risks recorded yet": "Још нема забележених ризика", + "Add the first risk": "Додај први ризик", + "Medium": "Средње", + "Mitigating": "Ублажава се", + "Occurred": "Наступио", + "Avoid": "Избећи", + "Reduce": "Смањити", + "Transfer": "Пренети", + "Accept": "Прихватити", + "Highest open risks": "Највиши отворени ризици", + "No open risks": "Нема отворених ризика", + "Open the risk register": "Отвори регистар ризика", + "Edit risk": "Уреди ризик", + "What could go wrong": "Шта може поћи наопако", + "Likelihood": "Вероватноћа", + "Impact": "Утицај", + "Score {score}": "Оцена {score}", + "Countermeasures": "Противмере", + "Could not save the risk. Please try again.": "Ризик није могуће сачувати. Покушајте поново.", + "Risks by likelihood and impact": "Ризици по вероватноћи и утицају", + "Risk scale": "Скала ризика", + "The levels every project scores its risks on, and where low turns into medium and high": "Нивои на којима сваки пројекат оцењује своје ризике, и где ниско прелази у средње и високо", + "Number of levels": "Број нивоа", + "Likelihood labels": "Ознаке вероватноће", + "Impact labels": "Ознаке утицаја", + "Level {level}": "Ниво {level}", + "Medium from score": "Средње од оцене", + "High from score": "Високо од оцене", + "Risk scale saved": "Скала ризика је сачувана", + "1 risk uses level {level}. Change it first.": "1 ризик користи ниво {level}. Прво га промените.", + "{count} risks use level {level}. Change them first.": "Ризика који користе ниво {level}: {count}. Прво их промените.", + "Every level needs a label, and high must start above medium.": "Сваки ниво треба ознаку, а високо мора почети изнад средњег.", + "The project this risk belongs to": "Пројекат коме овај ризик припада", + "The cause and the effect, in Markdown": "Узрок и последица, у Markdown-у", + "Category": "Категорија", + "A free grouping, such as planning, budget or supplier": "Слободно груписање, нпр. планирање, буџет или добављач", + "How likely it is, on the admin's risk scale": "Колико је вероватно, на скали ризика администратора", + "How hard it would hit, on the admin's risk scale": "Колико би снажно погодило, на скали ризика администратора", + "Likelihood times impact, calculated by the server": "Вероватноћа пута утицај, израчунава сервер", + "Where the risk stands": "Где је ризик", + "The person who watches this risk": "Особа која прати овај ризик", + "How the project answers the risk": "Како пројекат одговара на ризик", + "What is done to prevent it or soften it, in Markdown": "Шта се ради да се спречи или ублажи, у Markdown-у", + "When to look at this risk again": "Када поново прегледати овај ризик", + "Very low": "Веома ниско", + "Very high": "Веома високо", + "All projects": "Сви пројекти" }, "plurals": null } diff --git a/l10n/sv.js b/l10n/sv.js index 31162d9d..2d1aa7e5 100644 --- a/l10n/sv.js +++ b/l10n/sv.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Tavla", "List": "Lista", "Column": "Kolumn", - "Board or list": "Tavla eller lista" + "Board or list": "Tavla eller lista", + "Log": "Logg", + "Add entry": "Lägg till post", + "Show": "Visa", + "No actions yet": "Inga åtgärder än", + "Closed": "Stängd", + "Entry actions": "Poståtgärder", + "Add action": "Lägg till åtgärd", + "by {author}": "av {author}", + "saved {time}": "sparad {time}", + "Attendees: {names}": "Deltagare: {names}", + "Add the first entry": "Lägg till den första posten", + "Issues": "Problem", + "Lessons learned": "Lärdomar", + "Meetings": "Möten", + "Decisions": "Beslut", + "Actions": "Åtgärder", + "Issue": "Problem", + "Lesson learned": "Lärdom", + "Meeting": "Möte", + "Decision": "Beslut", + "About this project": "Om projektet", + "No description yet": "Ingen beskrivning än", + "Start date": "Startdatum", + "Not set": "Inte angivet", + "End date": "Slutdatum", + "Progress": "Framsteg", + "No tasks yet": "Inga uppgifter än", + "Open the board": "Öppna tavlan", + "People": "Personer", + "Owner": "Ägare", + "Latest log entries": "Senaste poster", + "Open the log": "Öppna loggen", + "{done} of {total} tasks done": "{done} av {total} uppgifter klara", + "Edit entry": "Redigera post", + "Type": "Typ", + "Attendees": "Deltagare", + "Issue is closed": "Problemet är stängt", + "Could not save the entry. Please try again.": "Posten kunde inte sparas. Försök igen.", + "From: {title}": "Från: {title}", + "What needs to happen": "Vad som behöver göras", + "Could not create the action. Please try again.": "Åtgärden kunde inte skapas. Försök igen.", + "The action was created but could not be linked to the entry.": "Åtgärden skapades men kunde inte kopplas till posten.", + "Project pages": "Projektsidor", + "Overview": "Översikt", + "Risks": "Risker", + "Log entry": "Loggpost", + "A short name for the entry": "Ett kort namn på posten", + "Project": "Projekt", + "The project this entry belongs to": "Projektet som posten hör till", + "What kind of entry this is": "Vilken sorts post detta är", + "When it happened": "När det hände", + "What happened, in Markdown": "Vad som hände, i Markdown", + "Whether an issue is still open": "Om ett problem fortfarande är öppet", + "The people at a meeting": "Personerna på ett möte", + "Tasks that came out of this entry": "Uppgifter som kom ur den här posten", + "Add risk": "Lägg till risk", + "Risk": "Risk", + "Score": "Poäng", + "Response": "Hantering", + "Review date": "Granskningsdatum", + "no longer on the project": "är inte längre med i projektet", + "Edit {title}": "Redigera {title}", + "No risks recorded yet": "Inga risker registrerade än", + "Add the first risk": "Lägg till den första risken", + "Medium": "Medel", + "Mitigating": "Mildras", + "Occurred": "Inträffat", + "Avoid": "Undvika", + "Reduce": "Minska", + "Transfer": "Överföra", + "Accept": "Acceptera", + "Highest open risks": "Högsta öppna risker", + "No open risks": "Inga öppna risker", + "Open the risk register": "Öppna riskregistret", + "Edit risk": "Redigera risk", + "What could go wrong": "Vad som kan gå fel", + "Likelihood": "Sannolikhet", + "Impact": "Konsekvens", + "Score {score}": "Poäng {score}", + "Countermeasures": "Motåtgärder", + "Could not save the risk. Please try again.": "Risken kunde inte sparas. Försök igen.", + "Risks by likelihood and impact": "Risker efter sannolikhet och konsekvens", + "Risk scale": "Riskskala", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivåerna som varje projekt poängsätter sina risker på, och var låg övergår i medel och hög", + "Number of levels": "Antal nivåer", + "Likelihood labels": "Etiketter för sannolikhet", + "Impact labels": "Etiketter för konsekvens", + "Level {level}": "Nivå {level}", + "Medium from score": "Medel från poäng", + "High from score": "Hög från poäng", + "Risk scale saved": "Riskskalan sparad", + "1 risk uses level {level}. Change it first.": "1 risk använder nivå {level}. Ändra den först.", + "{count} risks use level {level}. Change them first.": "{count} risker använder nivå {level}. Ändra dem först.", + "Every level needs a label, and high must start above medium.": "Varje nivå behöver en etikett, och hög måste börja över medel.", + "The project this risk belongs to": "Projektet som risken hör till", + "The cause and the effect, in Markdown": "Orsak och verkan, i Markdown", + "Category": "Kategori", + "A free grouping, such as planning, budget or supplier": "En fri gruppering, till exempel planering, budget eller leverantör", + "How likely it is, on the admin's risk scale": "Hur sannolik den är, på administratörens riskskala", + "How hard it would hit, on the admin's risk scale": "Hur hårt den skulle slå, på administratörens riskskala", + "Likelihood times impact, calculated by the server": "Sannolikhet gånger konsekvens, beräknad av servern", + "Where the risk stands": "Var risken står", + "The person who watches this risk": "Personen som bevakar risken", + "How the project answers the risk": "Hur projektet hanterar risken", + "What is done to prevent it or soften it, in Markdown": "Vad som görs för att förhindra eller mildra den, i Markdown", + "When to look at this risk again": "När risken ska ses över igen", + "Very low": "Mycket låg", + "Very high": "Mycket hög", + "All projects": "Alla projekt" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sv.json b/l10n/sv.json index 05a3ce40..cea24fb0 100644 --- a/l10n/sv.json +++ b/l10n/sv.json @@ -308,7 +308,116 @@ "Board": "Tavla", "List": "Lista", "Column": "Kolumn", - "Board or list": "Tavla eller lista" + "Board or list": "Tavla eller lista", + "Log": "Logg", + "Add entry": "Lägg till post", + "Show": "Visa", + "No actions yet": "Inga åtgärder än", + "Closed": "Stängd", + "Entry actions": "Poståtgärder", + "Add action": "Lägg till åtgärd", + "by {author}": "av {author}", + "saved {time}": "sparad {time}", + "Attendees: {names}": "Deltagare: {names}", + "Add the first entry": "Lägg till den första posten", + "Issues": "Problem", + "Lessons learned": "Lärdomar", + "Meetings": "Möten", + "Decisions": "Beslut", + "Actions": "Åtgärder", + "Issue": "Problem", + "Lesson learned": "Lärdom", + "Meeting": "Möte", + "Decision": "Beslut", + "About this project": "Om projektet", + "No description yet": "Ingen beskrivning än", + "Start date": "Startdatum", + "Not set": "Inte angivet", + "End date": "Slutdatum", + "Progress": "Framsteg", + "No tasks yet": "Inga uppgifter än", + "Open the board": "Öppna tavlan", + "People": "Personer", + "Owner": "Ägare", + "Latest log entries": "Senaste poster", + "Open the log": "Öppna loggen", + "{done} of {total} tasks done": "{done} av {total} uppgifter klara", + "Edit entry": "Redigera post", + "Type": "Typ", + "Attendees": "Deltagare", + "Issue is closed": "Problemet är stängt", + "Could not save the entry. Please try again.": "Posten kunde inte sparas. Försök igen.", + "From: {title}": "Från: {title}", + "What needs to happen": "Vad som behöver göras", + "Could not create the action. Please try again.": "Åtgärden kunde inte skapas. Försök igen.", + "The action was created but could not be linked to the entry.": "Åtgärden skapades men kunde inte kopplas till posten.", + "Project pages": "Projektsidor", + "Overview": "Översikt", + "Risks": "Risker", + "Log entry": "Loggpost", + "A short name for the entry": "Ett kort namn på posten", + "Project": "Projekt", + "The project this entry belongs to": "Projektet som posten hör till", + "What kind of entry this is": "Vilken sorts post detta är", + "When it happened": "När det hände", + "What happened, in Markdown": "Vad som hände, i Markdown", + "Whether an issue is still open": "Om ett problem fortfarande är öppet", + "The people at a meeting": "Personerna på ett möte", + "Tasks that came out of this entry": "Uppgifter som kom ur den här posten", + "Add risk": "Lägg till risk", + "Risk": "Risk", + "Score": "Poäng", + "Response": "Hantering", + "Review date": "Granskningsdatum", + "no longer on the project": "är inte längre med i projektet", + "Edit {title}": "Redigera {title}", + "No risks recorded yet": "Inga risker registrerade än", + "Add the first risk": "Lägg till den första risken", + "Medium": "Medel", + "Mitigating": "Mildras", + "Occurred": "Inträffat", + "Avoid": "Undvika", + "Reduce": "Minska", + "Transfer": "Överföra", + "Accept": "Acceptera", + "Highest open risks": "Högsta öppna risker", + "No open risks": "Inga öppna risker", + "Open the risk register": "Öppna riskregistret", + "Edit risk": "Redigera risk", + "What could go wrong": "Vad som kan gå fel", + "Likelihood": "Sannolikhet", + "Impact": "Konsekvens", + "Score {score}": "Poäng {score}", + "Countermeasures": "Motåtgärder", + "Could not save the risk. Please try again.": "Risken kunde inte sparas. Försök igen.", + "Risks by likelihood and impact": "Risker efter sannolikhet och konsekvens", + "Risk scale": "Riskskala", + "The levels every project scores its risks on, and where low turns into medium and high": "Nivåerna som varje projekt poängsätter sina risker på, och var låg övergår i medel och hög", + "Number of levels": "Antal nivåer", + "Likelihood labels": "Etiketter för sannolikhet", + "Impact labels": "Etiketter för konsekvens", + "Level {level}": "Nivå {level}", + "Medium from score": "Medel från poäng", + "High from score": "Hög från poäng", + "Risk scale saved": "Riskskalan sparad", + "1 risk uses level {level}. Change it first.": "1 risk använder nivå {level}. Ändra den först.", + "{count} risks use level {level}. Change them first.": "{count} risker använder nivå {level}. Ändra dem först.", + "Every level needs a label, and high must start above medium.": "Varje nivå behöver en etikett, och hög måste börja över medel.", + "The project this risk belongs to": "Projektet som risken hör till", + "The cause and the effect, in Markdown": "Orsak och verkan, i Markdown", + "Category": "Kategori", + "A free grouping, such as planning, budget or supplier": "En fri gruppering, till exempel planering, budget eller leverantör", + "How likely it is, on the admin's risk scale": "Hur sannolik den är, på administratörens riskskala", + "How hard it would hit, on the admin's risk scale": "Hur hårt den skulle slå, på administratörens riskskala", + "Likelihood times impact, calculated by the server": "Sannolikhet gånger konsekvens, beräknad av servern", + "Where the risk stands": "Var risken står", + "The person who watches this risk": "Personen som bevakar risken", + "How the project answers the risk": "Hur projektet hanterar risken", + "What is done to prevent it or soften it, in Markdown": "Vad som görs för att förhindra eller mildra den, i Markdown", + "When to look at this risk again": "När risken ska ses över igen", + "Very low": "Mycket låg", + "Very high": "Mycket hög", + "All projects": "Alla projekt" }, "plurals": null } diff --git a/l10n/tr.js b/l10n/tr.js index 5571d398..69f82f92 100644 --- a/l10n/tr.js +++ b/l10n/tr.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Pano", "List": "Liste", "Column": "Sütun", - "Board or list": "Pano veya liste" + "Board or list": "Pano veya liste", + "Log": "Günlük", + "Add entry": "Kayıt ekle", + "Show": "Göster", + "No actions yet": "Henüz eylem yok", + "Closed": "Kapalı", + "Entry actions": "Kayıt işlemleri", + "Add action": "Eylem ekle", + "by {author}": "{author} tarafından", + "saved {time}": "{time} kaydedildi", + "Attendees: {names}": "Katılımcılar: {names}", + "Add the first entry": "İlk kaydı ekle", + "Issues": "Sorunlar", + "Lessons learned": "Alınan dersler", + "Meetings": "Toplantılar", + "Decisions": "Kararlar", + "Actions": "Eylemler", + "Issue": "Sorun", + "Lesson learned": "Alınan ders", + "Meeting": "Toplantı", + "Decision": "Karar", + "About this project": "Bu proje hakkında", + "No description yet": "Henüz açıklama yok", + "Start date": "Başlangıç tarihi", + "Not set": "Ayarlanmadı", + "End date": "Bitiş tarihi", + "Progress": "İlerleme", + "No tasks yet": "Henüz görev yok", + "Open the board": "Panoyu aç", + "People": "Kişiler", + "Owner": "Sahip", + "Latest log entries": "Son kayıtlar", + "Open the log": "Günlüğü aç", + "{done} of {total} tasks done": "{total} görevden {done} tanesi tamamlandı", + "Edit entry": "Kaydı düzenle", + "Type": "Tür", + "Attendees": "Katılımcılar", + "Issue is closed": "Sorun kapatıldı", + "Could not save the entry. Please try again.": "Kayıt kaydedilemedi. Lütfen tekrar deneyin.", + "From: {title}": "Kaynak: {title}", + "What needs to happen": "Yapılması gereken", + "Could not create the action. Please try again.": "Eylem oluşturulamadı. Lütfen tekrar deneyin.", + "The action was created but could not be linked to the entry.": "Eylem oluşturuldu ancak kayda bağlanamadı.", + "Project pages": "Proje sayfaları", + "Overview": "Genel bakış", + "Risks": "Riskler", + "Log entry": "Günlük kaydı", + "A short name for the entry": "Kayıt için kısa bir ad", + "Project": "Proje", + "The project this entry belongs to": "Bu kaydın ait olduğu proje", + "What kind of entry this is": "Bunun ne tür bir kayıt olduğu", + "When it happened": "Ne zaman oldu", + "What happened, in Markdown": "Ne oldu, Markdown ile", + "Whether an issue is still open": "Bir sorunun hâlâ açık olup olmadığı", + "The people at a meeting": "Bir toplantıdaki kişiler", + "Tasks that came out of this entry": "Bu kayıttan çıkan görevler", + "Add risk": "Risk ekle", + "Risk": "Risk", + "Score": "Puan", + "Response": "Yanıt", + "Review date": "Gözden geçirme tarihi", + "no longer on the project": "artık projede değil", + "Edit {title}": "{title} düzenle", + "No risks recorded yet": "Henüz kayıtlı risk yok", + "Add the first risk": "İlk riski ekle", + "Medium": "Orta", + "Mitigating": "Azaltılıyor", + "Occurred": "Gerçekleşti", + "Avoid": "Kaçın", + "Reduce": "Azalt", + "Transfer": "Devret", + "Accept": "Kabul et", + "Highest open risks": "En yüksek açık riskler", + "No open risks": "Açık risk yok", + "Open the risk register": "Risk kaydını aç", + "Edit risk": "Riski düzenle", + "What could go wrong": "Ne ters gidebilir", + "Likelihood": "Olasılık", + "Impact": "Etki", + "Score {score}": "Puan {score}", + "Countermeasures": "Karşı önlemler", + "Could not save the risk. Please try again.": "Risk kaydedilemedi. Lütfen tekrar deneyin.", + "Risks by likelihood and impact": "Olasılık ve etkiye göre riskler", + "Risk scale": "Risk ölçeği", + "The levels every project scores its risks on, and where low turns into medium and high": "Her projenin risklerini puanladığı düzeyler ve düşüğün ortaya ve yükseğe dönüştüğü yer", + "Number of levels": "Düzey sayısı", + "Likelihood labels": "Olasılık etiketleri", + "Impact labels": "Etki etiketleri", + "Level {level}": "Düzey {level}", + "Medium from score": "Şu puandan itibaren orta", + "High from score": "Şu puandan itibaren yüksek", + "Risk scale saved": "Risk ölçeği kaydedildi", + "1 risk uses level {level}. Change it first.": "1 risk {level} düzeyini kullanıyor. Önce onu değiştirin.", + "{count} risks use level {level}. Change them first.": "{count} risk {level} düzeyini kullanıyor. Önce onları değiştirin.", + "Every level needs a label, and high must start above medium.": "Her düzeyin bir etiketi olmalı ve yüksek, ortanın üzerinde başlamalı.", + "The project this risk belongs to": "Bu riskin ait olduğu proje", + "The cause and the effect, in Markdown": "Neden ve sonuç, Markdown ile", + "Category": "Kategori", + "A free grouping, such as planning, budget or supplier": "Planlama, bütçe veya tedarikçi gibi serbest bir gruplama", + "How likely it is, on the admin's risk scale": "Yöneticinin risk ölçeğine göre ne kadar olası olduğu", + "How hard it would hit, on the admin's risk scale": "Yöneticinin risk ölçeğine göre ne kadar sert vuracağı", + "Likelihood times impact, calculated by the server": "Olasılık çarpı etki, sunucu tarafından hesaplanır", + "Where the risk stands": "Riskin durumu", + "The person who watches this risk": "Bu riski izleyen kişi", + "How the project answers the risk": "Projenin riske nasıl yanıt verdiği", + "What is done to prevent it or soften it, in Markdown": "Önlemek veya hafifletmek için yapılanlar, Markdown ile", + "When to look at this risk again": "Riskin yeniden ne zaman gözden geçirileceği", + "Very low": "Çok düşük", + "Very high": "Çok yüksek", + "All projects": "Tüm projeler" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/tr.json b/l10n/tr.json index a85be3e8..5e77220b 100644 --- a/l10n/tr.json +++ b/l10n/tr.json @@ -308,7 +308,116 @@ "Board": "Pano", "List": "Liste", "Column": "Sütun", - "Board or list": "Pano veya liste" + "Board or list": "Pano veya liste", + "Log": "Günlük", + "Add entry": "Kayıt ekle", + "Show": "Göster", + "No actions yet": "Henüz eylem yok", + "Closed": "Kapalı", + "Entry actions": "Kayıt işlemleri", + "Add action": "Eylem ekle", + "by {author}": "{author} tarafından", + "saved {time}": "{time} kaydedildi", + "Attendees: {names}": "Katılımcılar: {names}", + "Add the first entry": "İlk kaydı ekle", + "Issues": "Sorunlar", + "Lessons learned": "Alınan dersler", + "Meetings": "Toplantılar", + "Decisions": "Kararlar", + "Actions": "Eylemler", + "Issue": "Sorun", + "Lesson learned": "Alınan ders", + "Meeting": "Toplantı", + "Decision": "Karar", + "About this project": "Bu proje hakkında", + "No description yet": "Henüz açıklama yok", + "Start date": "Başlangıç tarihi", + "Not set": "Ayarlanmadı", + "End date": "Bitiş tarihi", + "Progress": "İlerleme", + "No tasks yet": "Henüz görev yok", + "Open the board": "Panoyu aç", + "People": "Kişiler", + "Owner": "Sahip", + "Latest log entries": "Son kayıtlar", + "Open the log": "Günlüğü aç", + "{done} of {total} tasks done": "{total} görevden {done} tanesi tamamlandı", + "Edit entry": "Kaydı düzenle", + "Type": "Tür", + "Attendees": "Katılımcılar", + "Issue is closed": "Sorun kapatıldı", + "Could not save the entry. Please try again.": "Kayıt kaydedilemedi. Lütfen tekrar deneyin.", + "From: {title}": "Kaynak: {title}", + "What needs to happen": "Yapılması gereken", + "Could not create the action. Please try again.": "Eylem oluşturulamadı. Lütfen tekrar deneyin.", + "The action was created but could not be linked to the entry.": "Eylem oluşturuldu ancak kayda bağlanamadı.", + "Project pages": "Proje sayfaları", + "Overview": "Genel bakış", + "Risks": "Riskler", + "Log entry": "Günlük kaydı", + "A short name for the entry": "Kayıt için kısa bir ad", + "Project": "Proje", + "The project this entry belongs to": "Bu kaydın ait olduğu proje", + "What kind of entry this is": "Bunun ne tür bir kayıt olduğu", + "When it happened": "Ne zaman oldu", + "What happened, in Markdown": "Ne oldu, Markdown ile", + "Whether an issue is still open": "Bir sorunun hâlâ açık olup olmadığı", + "The people at a meeting": "Bir toplantıdaki kişiler", + "Tasks that came out of this entry": "Bu kayıttan çıkan görevler", + "Add risk": "Risk ekle", + "Risk": "Risk", + "Score": "Puan", + "Response": "Yanıt", + "Review date": "Gözden geçirme tarihi", + "no longer on the project": "artık projede değil", + "Edit {title}": "{title} düzenle", + "No risks recorded yet": "Henüz kayıtlı risk yok", + "Add the first risk": "İlk riski ekle", + "Medium": "Orta", + "Mitigating": "Azaltılıyor", + "Occurred": "Gerçekleşti", + "Avoid": "Kaçın", + "Reduce": "Azalt", + "Transfer": "Devret", + "Accept": "Kabul et", + "Highest open risks": "En yüksek açık riskler", + "No open risks": "Açık risk yok", + "Open the risk register": "Risk kaydını aç", + "Edit risk": "Riski düzenle", + "What could go wrong": "Ne ters gidebilir", + "Likelihood": "Olasılık", + "Impact": "Etki", + "Score {score}": "Puan {score}", + "Countermeasures": "Karşı önlemler", + "Could not save the risk. Please try again.": "Risk kaydedilemedi. Lütfen tekrar deneyin.", + "Risks by likelihood and impact": "Olasılık ve etkiye göre riskler", + "Risk scale": "Risk ölçeği", + "The levels every project scores its risks on, and where low turns into medium and high": "Her projenin risklerini puanladığı düzeyler ve düşüğün ortaya ve yükseğe dönüştüğü yer", + "Number of levels": "Düzey sayısı", + "Likelihood labels": "Olasılık etiketleri", + "Impact labels": "Etki etiketleri", + "Level {level}": "Düzey {level}", + "Medium from score": "Şu puandan itibaren orta", + "High from score": "Şu puandan itibaren yüksek", + "Risk scale saved": "Risk ölçeği kaydedildi", + "1 risk uses level {level}. Change it first.": "1 risk {level} düzeyini kullanıyor. Önce onu değiştirin.", + "{count} risks use level {level}. Change them first.": "{count} risk {level} düzeyini kullanıyor. Önce onları değiştirin.", + "Every level needs a label, and high must start above medium.": "Her düzeyin bir etiketi olmalı ve yüksek, ortanın üzerinde başlamalı.", + "The project this risk belongs to": "Bu riskin ait olduğu proje", + "The cause and the effect, in Markdown": "Neden ve sonuç, Markdown ile", + "Category": "Kategori", + "A free grouping, such as planning, budget or supplier": "Planlama, bütçe veya tedarikçi gibi serbest bir gruplama", + "How likely it is, on the admin's risk scale": "Yöneticinin risk ölçeğine göre ne kadar olası olduğu", + "How hard it would hit, on the admin's risk scale": "Yöneticinin risk ölçeğine göre ne kadar sert vuracağı", + "Likelihood times impact, calculated by the server": "Olasılık çarpı etki, sunucu tarafından hesaplanır", + "Where the risk stands": "Riskin durumu", + "The person who watches this risk": "Bu riski izleyen kişi", + "How the project answers the risk": "Projenin riske nasıl yanıt verdiği", + "What is done to prevent it or soften it, in Markdown": "Önlemek veya hafifletmek için yapılanlar, Markdown ile", + "When to look at this risk again": "Riskin yeniden ne zaman gözden geçirileceği", + "Very low": "Çok düşük", + "Very high": "Çok yüksek", + "All projects": "Tüm projeler" }, "plurals": {} } diff --git a/l10n/uk.js b/l10n/uk.js index 16081062..430233f9 100644 --- a/l10n/uk.js +++ b/l10n/uk.js @@ -309,7 +309,116 @@ OC.L10N.register( "Board": "Дошка", "List": "Список", "Column": "Стовпець", - "Board or list": "Дошка або список" + "Board or list": "Дошка або список", + "Log": "Журнал", + "Add entry": "Додати запис", + "Show": "Показати", + "No actions yet": "Дій ще немає", + "Closed": "Закрито", + "Entry actions": "Дії із записом", + "Add action": "Додати дію", + "by {author}": "автор {author}", + "saved {time}": "збережено {time}", + "Attendees: {names}": "Присутні: {names}", + "Add the first entry": "Додати перший запис", + "Issues": "Проблеми", + "Lessons learned": "Засвоєні уроки", + "Meetings": "Зустрічі", + "Decisions": "Рішення", + "Actions": "Дії", + "Issue": "Проблема", + "Lesson learned": "Засвоєний урок", + "Meeting": "Зустріч", + "Decision": "Рішення", + "About this project": "Про цей проєкт", + "No description yet": "Опису ще немає", + "Start date": "Дата початку", + "Not set": "Не встановлено", + "End date": "Дата завершення", + "Progress": "Поступ", + "No tasks yet": "Завдань ще немає", + "Open the board": "Відкрити дошку", + "People": "Люди", + "Owner": "Власник", + "Latest log entries": "Останні записи", + "Open the log": "Відкрити журнал", + "{done} of {total} tasks done": "Виконано {done} з {total} завдань", + "Edit entry": "Редагувати запис", + "Type": "Вид", + "Attendees": "Присутні", + "Issue is closed": "Проблему закрито", + "Could not save the entry. Please try again.": "Не вдалося зберегти запис. Спробуйте ще раз.", + "From: {title}": "З: {title}", + "What needs to happen": "Що треба зробити", + "Could not create the action. Please try again.": "Не вдалося створити дію. Спробуйте ще раз.", + "The action was created but could not be linked to the entry.": "Дію створено, але не вдалося пов'язати її із записом.", + "Project pages": "Сторінки проєкту", + "Overview": "Огляд", + "Risks": "Ризики", + "Log entry": "Запис журналу", + "A short name for the entry": "Коротка назва запису", + "Project": "Проєкт", + "The project this entry belongs to": "Проєкт, до якого належить цей запис", + "What kind of entry this is": "Який це вид запису", + "When it happened": "Коли це сталося", + "What happened, in Markdown": "Що сталося, у Markdown", + "Whether an issue is still open": "Чи проблема ще відкрита", + "The people at a meeting": "Люди на зустрічі", + "Tasks that came out of this entry": "Завдання, що виникли з цього запису", + "Add risk": "Додати ризик", + "Risk": "Ризик", + "Score": "Оцінка", + "Response": "Реагування", + "Review date": "Дата перегляду", + "no longer on the project": "більше не в проєкті", + "Edit {title}": "Редагувати {title}", + "No risks recorded yet": "Ризиків ще не записано", + "Add the first risk": "Додати перший ризик", + "Medium": "Середній", + "Mitigating": "Пом'якшується", + "Occurred": "Настав", + "Avoid": "Уникнути", + "Reduce": "Зменшити", + "Transfer": "Передати", + "Accept": "Прийняти", + "Highest open risks": "Найвищі відкриті ризики", + "No open risks": "Немає відкритих ризиків", + "Open the risk register": "Відкрити реєстр ризиків", + "Edit risk": "Редагувати ризик", + "What could go wrong": "Що може піти не так", + "Likelihood": "Імовірність", + "Impact": "Вплив", + "Score {score}": "Оцінка {score}", + "Countermeasures": "Контрзаходи", + "Could not save the risk. Please try again.": "Не вдалося зберегти ризик. Спробуйте ще раз.", + "Risks by likelihood and impact": "Ризики за ймовірністю та впливом", + "Risk scale": "Шкала ризиків", + "The levels every project scores its risks on, and where low turns into medium and high": "Рівні, за якими кожен проєкт оцінює свої ризики, і де низький переходить у середній та високий", + "Number of levels": "Кількість рівнів", + "Likelihood labels": "Назви ймовірності", + "Impact labels": "Назви впливу", + "Level {level}": "Рівень {level}", + "Medium from score": "Середній від оцінки", + "High from score": "Високий від оцінки", + "Risk scale saved": "Шкалу ризиків збережено", + "1 risk uses level {level}. Change it first.": "1 ризик використовує рівень {level}. Спершу змініть його.", + "{count} risks use level {level}. Change them first.": "Ризиків на рівні {level}: {count}. Спершу змініть їх.", + "Every level needs a label, and high must start above medium.": "Кожен рівень потребує назви, а високий має починатися вище за середній.", + "The project this risk belongs to": "Проєкт, до якого належить цей ризик", + "The cause and the effect, in Markdown": "Причина й наслідок, у Markdown", + "Category": "Категорія", + "A free grouping, such as planning, budget or supplier": "Вільне групування, наприклад планування, бюджет чи постачальник", + "How likely it is, on the admin's risk scale": "Наскільки це ймовірно, за шкалою ризиків адміністратора", + "How hard it would hit, on the admin's risk scale": "Наскільки сильно це вдарить, за шкалою ризиків адміністратора", + "Likelihood times impact, calculated by the server": "Імовірність помножена на вплив, обчислює сервер", + "Where the risk stands": "На якому етапі ризик", + "The person who watches this risk": "Людина, яка стежить за цим ризиком", + "How the project answers the risk": "Як проєкт реагує на ризик", + "What is done to prevent it or soften it, in Markdown": "Що робиться, щоб запобігти йому чи пом'якшити, у Markdown", + "When to look at this risk again": "Коли знову переглянути цей ризик", + "Very low": "Дуже низький", + "Very high": "Дуже високий", + "All projects": "Усі проєкти" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/uk.json b/l10n/uk.json index 86cd7219..e4f48c57 100644 --- a/l10n/uk.json +++ b/l10n/uk.json @@ -308,7 +308,116 @@ "Board": "Дошка", "List": "Список", "Column": "Стовпець", - "Board or list": "Дошка або список" + "Board or list": "Дошка або список", + "Log": "Журнал", + "Add entry": "Додати запис", + "Show": "Показати", + "No actions yet": "Дій ще немає", + "Closed": "Закрито", + "Entry actions": "Дії із записом", + "Add action": "Додати дію", + "by {author}": "автор {author}", + "saved {time}": "збережено {time}", + "Attendees: {names}": "Присутні: {names}", + "Add the first entry": "Додати перший запис", + "Issues": "Проблеми", + "Lessons learned": "Засвоєні уроки", + "Meetings": "Зустрічі", + "Decisions": "Рішення", + "Actions": "Дії", + "Issue": "Проблема", + "Lesson learned": "Засвоєний урок", + "Meeting": "Зустріч", + "Decision": "Рішення", + "About this project": "Про цей проєкт", + "No description yet": "Опису ще немає", + "Start date": "Дата початку", + "Not set": "Не встановлено", + "End date": "Дата завершення", + "Progress": "Поступ", + "No tasks yet": "Завдань ще немає", + "Open the board": "Відкрити дошку", + "People": "Люди", + "Owner": "Власник", + "Latest log entries": "Останні записи", + "Open the log": "Відкрити журнал", + "{done} of {total} tasks done": "Виконано {done} з {total} завдань", + "Edit entry": "Редагувати запис", + "Type": "Вид", + "Attendees": "Присутні", + "Issue is closed": "Проблему закрито", + "Could not save the entry. Please try again.": "Не вдалося зберегти запис. Спробуйте ще раз.", + "From: {title}": "З: {title}", + "What needs to happen": "Що треба зробити", + "Could not create the action. Please try again.": "Не вдалося створити дію. Спробуйте ще раз.", + "The action was created but could not be linked to the entry.": "Дію створено, але не вдалося пов'язати її із записом.", + "Project pages": "Сторінки проєкту", + "Overview": "Огляд", + "Risks": "Ризики", + "Log entry": "Запис журналу", + "A short name for the entry": "Коротка назва запису", + "Project": "Проєкт", + "The project this entry belongs to": "Проєкт, до якого належить цей запис", + "What kind of entry this is": "Який це вид запису", + "When it happened": "Коли це сталося", + "What happened, in Markdown": "Що сталося, у Markdown", + "Whether an issue is still open": "Чи проблема ще відкрита", + "The people at a meeting": "Люди на зустрічі", + "Tasks that came out of this entry": "Завдання, що виникли з цього запису", + "Add risk": "Додати ризик", + "Risk": "Ризик", + "Score": "Оцінка", + "Response": "Реагування", + "Review date": "Дата перегляду", + "no longer on the project": "більше не в проєкті", + "Edit {title}": "Редагувати {title}", + "No risks recorded yet": "Ризиків ще не записано", + "Add the first risk": "Додати перший ризик", + "Medium": "Середній", + "Mitigating": "Пом'якшується", + "Occurred": "Настав", + "Avoid": "Уникнути", + "Reduce": "Зменшити", + "Transfer": "Передати", + "Accept": "Прийняти", + "Highest open risks": "Найвищі відкриті ризики", + "No open risks": "Немає відкритих ризиків", + "Open the risk register": "Відкрити реєстр ризиків", + "Edit risk": "Редагувати ризик", + "What could go wrong": "Що може піти не так", + "Likelihood": "Імовірність", + "Impact": "Вплив", + "Score {score}": "Оцінка {score}", + "Countermeasures": "Контрзаходи", + "Could not save the risk. Please try again.": "Не вдалося зберегти ризик. Спробуйте ще раз.", + "Risks by likelihood and impact": "Ризики за ймовірністю та впливом", + "Risk scale": "Шкала ризиків", + "The levels every project scores its risks on, and where low turns into medium and high": "Рівні, за якими кожен проєкт оцінює свої ризики, і де низький переходить у середній та високий", + "Number of levels": "Кількість рівнів", + "Likelihood labels": "Назви ймовірності", + "Impact labels": "Назви впливу", + "Level {level}": "Рівень {level}", + "Medium from score": "Середній від оцінки", + "High from score": "Високий від оцінки", + "Risk scale saved": "Шкалу ризиків збережено", + "1 risk uses level {level}. Change it first.": "1 ризик використовує рівень {level}. Спершу змініть його.", + "{count} risks use level {level}. Change them first.": "Ризиків на рівні {level}: {count}. Спершу змініть їх.", + "Every level needs a label, and high must start above medium.": "Кожен рівень потребує назви, а високий має починатися вище за середній.", + "The project this risk belongs to": "Проєкт, до якого належить цей ризик", + "The cause and the effect, in Markdown": "Причина й наслідок, у Markdown", + "Category": "Категорія", + "A free grouping, such as planning, budget or supplier": "Вільне групування, наприклад планування, бюджет чи постачальник", + "How likely it is, on the admin's risk scale": "Наскільки це ймовірно, за шкалою ризиків адміністратора", + "How hard it would hit, on the admin's risk scale": "Наскільки сильно це вдарить, за шкалою ризиків адміністратора", + "Likelihood times impact, calculated by the server": "Імовірність помножена на вплив, обчислює сервер", + "Where the risk stands": "На якому етапі ризик", + "The person who watches this risk": "Людина, яка стежить за цим ризиком", + "How the project answers the risk": "Як проєкт реагує на ризик", + "What is done to prevent it or soften it, in Markdown": "Що робиться, щоб запобігти йому чи пом'якшити, у Markdown", + "When to look at this risk again": "Коли знову переглянути цей ризик", + "Very low": "Дуже низький", + "Very high": "Дуже високий", + "All projects": "Усі проєкти" }, "plurals": {} } diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 8c485ddf..285963f1 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -26,6 +26,7 @@ use OCA\Planninq\AppInfo\Application; use OCA\Planninq\Service\RegisterImportService; +use OCA\Planninq\Service\RiskScaleService; use OCA\Planninq\Service\SettingsService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; @@ -46,6 +47,7 @@ class SettingsController extends Controller { * @param SettingsService $settingsService The settings service * @param RegisterImportService $registerImport The register import service * @param IUserSession $userSession The user session + * @param RiskScaleService $riskScale Finds the risks a smaller risk scale would strand * * @return void */ @@ -54,6 +56,7 @@ public function __construct( private SettingsService $settingsService, private RegisterImportService $registerImport, private IUserSession $userSession, + private RiskScaleService $riskScale, ) { parent::__construct(appName: Application::APP_ID, request: $request); }//end __construct() @@ -99,6 +102,17 @@ public function create(): JSONResponse { } $data = $this->request->getParams(); + if (array_key_exists(RiskScaleService::CONFIG_KEY, $data) === true) { + $scale = $this->riskScale->normalise(raw: (string)$data[RiskScaleService::CONFIG_KEY]); + $refused = $this->refuseRiskScale(scale: $scale); + if ($refused !== null) { + return $refused; + } + + // Store the normalised form: trimmed labels, integer levels. + $data[RiskScaleService::CONFIG_KEY] = (string)json_encode($scale); + } + $config = $this->settingsService->updateSettings($data); return new JSONResponse( @@ -109,6 +123,39 @@ public function create(): JSONResponse { ); }//end create() + /** + * The refusal of a risk scale that is malformed or that risks still exceed, or null to go on. + * + * @param array|null $scale The normalised scale, or null when it did not parse. + * + * @return JSONResponse|null + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.4 + */ + private function refuseRiskScale(?array $scale): ?JSONResponse { + if ($scale === null) { + return new JSONResponse( + ['error' => 'risk-scale-invalid', 'message' => 'The risk scale is not valid.'], + Http::STATUS_BAD_REQUEST + ); + } + + $conflict = $this->riskScale->conflict(levels: (int)$scale['levels']); + if ($conflict === null) { + return null; + } + + return new JSONResponse( + [ + 'error' => 'risk-scale-in-use', + 'message' => $this->riskScale->refusal(count: $conflict['count'], level: $conflict['level']), + 'count' => $conflict['count'], + 'level' => $conflict['level'], + ], + Http::STATUS_CONFLICT + ); + }//end refuseRiskScale() + /** * Update app settings (PUT /api/settings). * diff --git a/lib/Listener/ProjectMemberAccessListener.php b/lib/Listener/ProjectMemberAccessListener.php index 69b5a7db..56eae543 100644 --- a/lib/Listener/ProjectMemberAccessListener.php +++ b/lib/Listener/ProjectMemberAccessListener.php @@ -83,7 +83,7 @@ class ProjectMemberAccessListener implements IEventListener { * * @var array */ - private const GATED_SCHEMAS = ['task', 'column', 'projectPhase']; + private const GATED_SCHEMAS = ['task', 'column', 'projectPhase', 'projectLogEntry', 'risk']; /** * OpenRegister's ambient system-operation marker, by name. diff --git a/lib/Service/ProjectMembershipService.php b/lib/Service/ProjectMembershipService.php index d8645092..41ed9023 100644 --- a/lib/Service/ProjectMembershipService.php +++ b/lib/Service/ProjectMembershipService.php @@ -76,7 +76,7 @@ class ProjectMembershipService { * * @var array */ - public const SCOPED_SCHEMAS = ['task', 'column', 'projectPhase', 'plannedTimeEntry']; + public const SCOPED_SCHEMAS = ['task', 'column', 'projectPhase', 'plannedTimeEntry', 'projectLogEntry', 'risk']; /** * The one scoped schema that may name its project only through its task. diff --git a/lib/Service/RiskScaleService.php b/lib/Service/RiskScaleService.php new file mode 100644 index 00000000..b558674e --- /dev/null +++ b/lib/Service/RiskScaleService.php @@ -0,0 +1,258 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + * + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + */ + +declare(strict_types=1); + +namespace OCA\Planninq\Service; + +use OCP\App\IAppManager; +use Psr\Container\ContainerInterface; +use Psr\Log\LoggerInterface; +use Throwable; + +/** + * Validates the risk scale and finds the risks a smaller scale would strand. + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.4 + */ +class RiskScaleService { + + /** + * App config key of the scale. + * + * @var string + */ + public const CONFIG_KEY = 'risk_scale'; + + /** + * The scale an install starts with: five levels. + * + * @var string + */ + public const DEFAULT_SCALE = '{"levels":5,' + . '"likelihood":["Very low","Low","Medium","High","Very high"],' + . '"impact":["Very low","Low","Medium","High","Very high"],' + . '"thresholds":{"medium":5,"high":12}}'; + + /** + * Fewest levels a scale may have. + * + * @var integer + */ + public const MIN_LEVELS = 3; + + /** + * Most levels a scale may have; the risk schema caps likelihood and impact at it. + * + * @var integer + */ + public const MAX_LEVELS = 5; + + /** + * Constructor. + * + * @param ContainerInterface $container Resolves OpenRegister's ObjectService at runtime. + * @param IAppManager $appManager Tells whether OpenRegister is installed. + * @param LoggerInterface $logger The logger. + */ + public function __construct( + private ContainerInterface $container, + private IAppManager $appManager, + private LoggerInterface $logger, + ) { + }//end __construct() + + /** + * Parse and check a submitted scale. + * + * @param string $raw The JSON the admin page sends. + * + * @return array{levels: int, likelihood: array, impact: array, thresholds: array{medium: int, high: int}}|null + * The scale, or null when it is not valid. + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.4 + */ + public function normalise(string $raw): ?array { + $decoded = json_decode($raw, true); + if (is_array($decoded) === false) { + return null; + } + + $levels = ($decoded['levels'] ?? null); + if (is_int($levels) === false || $levels < self::MIN_LEVELS || $levels > self::MAX_LEVELS) { + return null; + } + + $likelihood = $this->labels(value: ($decoded['likelihood'] ?? null), levels: $levels); + $impact = $this->labels(value: ($decoded['impact'] ?? null), levels: $levels); + $thresholds = $this->thresholds(value: ($decoded['thresholds'] ?? null), levels: $levels); + if ($likelihood === null || $impact === null || $thresholds === null) { + return null; + } + + return [ + 'levels' => $levels, + 'likelihood' => $likelihood, + 'impact' => $impact, + 'thresholds' => $thresholds, + ]; + }//end normalise() + + /** + * The two thresholds, when medium starts above 1 and high above medium and within the top score. + * + * @param mixed $value The submitted thresholds. + * @param int $levels The number of levels. + * + * @return array{medium: int, high: int}|null + */ + private function thresholds(mixed $value, int $levels): ?array { + $medium = ($value['medium'] ?? null); + $high = ($value['high'] ?? null); + if (is_int($medium) === false || is_int($high) === false) { + return null; + } + + if ($medium < 2 || $high <= $medium || $high > ($levels * $levels)) { + return null; + } + + return ['medium' => $medium, 'high' => $high]; + }//end thresholds() + + /** + * The risks a scale of this many levels would strand, and the highest level they use. + * + * @param int $levels The number of levels the admin wants. + * + * @return array{count: int, level: int}|null Null when no risk is in the way. + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.4 + */ + public function conflict(int $levels): ?array { + if ($levels >= self::MAX_LEVELS || $this->appManager->isInstalled('openregister') === false) { + return null; + } + + $above = range($levels + 1, self::MAX_LEVELS); + $stranded = []; + foreach (['likelihood', 'impact'] as $axis) { + foreach ($this->search(filters: [$axis => $above]) as $id => $data) { + $stranded[$id] = max((int)($data['likelihood'] ?? 0), (int)($data['impact'] ?? 0)); + } + } + + if ($stranded === []) { + return null; + } + + return ['count' => count($stranded), 'level' => max($stranded)]; + }//end conflict() + + /** + * The refusal an admin reads. + * + * @param int $count How many risks are in the way. + * @param int $level The highest level they use. + * + * @return string + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.4 + */ + public function refusal(int $count, int $level): string { + if ($count === 1) { + return sprintf('1 risk uses level %d. Change it first.', $level); + } + + return sprintf('%d risks use level %d. Change them first.', $count, $level); + }//end refusal() + + /** + * A trimmed list of exactly `$levels` non-empty labels. + * + * @param mixed $value The submitted labels. + * @param int $levels The number of levels. + * + * @return array|null + */ + private function labels(mixed $value, int $levels): ?array { + if (is_array($value) === false || count($value) !== $levels) { + return null; + } + + $labels = []; + foreach (array_values($value) as $label) { + if (is_string($label) === false || trim($label) === '') { + return null; + } + + $labels[] = trim($label); + } + + return $labels; + }//end labels() + + /** + * Risks matching the filters, read as the system: the check must see every + * risk, not only the ones the saving admin is a member of. + * + * @param array $filters Property filters. + * + * @return array> Data keyed by uuid. + */ + private function search(array $filters): array { + try { + $objectService = $this->container->get('OCA\\OpenRegister\\Service\\ObjectService'); + $results = $objectService->searchObjectsBySlug( + registerSlug: ProjectMembershipService::REGISTER, + schemaSlug: 'risk', + filters: $filters, + _rbac: false, + _multitenancy: false + ); + } catch (Throwable $e) { + $this->logger->warning('Planninq: could not read risks for the risk scale check', ['exception' => $e->getMessage()]); + return []; + } + + if (is_array($results) === true && array_key_exists('results', $results) === true) { + $results = (array)$results['results']; + } + + $rows = []; + foreach ((array)$results as $row) { + if (is_object($row) === true && is_callable([$row, 'getUuid']) === true) { + $rows[(string)$row->getUuid()] = (array)$row->getObject(); + continue; + } + + if (is_array($row) === true) { + $rows[(string)($row['@self']['id'] ?? ($row['id'] ?? ''))] = $row; + } + } + + return $rows; + }//end search() +}//end class diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php index af741ddf..5283ac78 100644 --- a/lib/Service/SettingsService.php +++ b/lib/Service/SettingsService.php @@ -61,6 +61,7 @@ class SettingsService { 'default_columns' => '["To do","In progress","Review","Done"]', 'allow_project_creation' => 'all', 'due_reminder_lead_hours' => '24', + RiskScaleService::CONFIG_KEY => RiskScaleService::DEFAULT_SCALE, ]; /** diff --git a/lib/Settings/planninq_mock_register.json b/lib/Settings/planninq_mock_register.json index 5466e763..244071f4 100644 --- a/lib/Settings/planninq_mock_register.json +++ b/lib/Settings/planninq_mock_register.json @@ -413,6 +413,114 @@ "roomReference": "C0.03", "roomLabel": "C0.03", "status": "cancelled" + }, + { + "@self": { + "register": "planninq", + "schema": "projectLogEntry", + "slug": "projectlogentry-voorbeeld-title-1-1" + }, + "title": "Voorbeeld Title 1", + "project": "00000000-0000-4000-8000-000000000000", + "type": "issue", + "date": "2026-03-01", + "members": [], + "body": "Voorbeeld Body 1", + "status": "open", + "attendees": [], + "actions": [] + }, + { + "@self": { + "register": "planninq", + "schema": "projectLogEntry", + "slug": "projectlogentry-voorbeeld-title-2-2" + }, + "title": "Voorbeeld Title 2", + "project": "00000000-0000-4000-8000-000000000001", + "type": "lesson", + "date": "2026-03-02", + "members": [], + "body": "Voorbeeld Body 2", + "status": "closed", + "attendees": [], + "actions": [] + }, + { + "@self": { + "register": "planninq", + "schema": "projectLogEntry", + "slug": "projectlogentry-voorbeeld-title-3-3" + }, + "title": "Voorbeeld Title 3", + "project": "00000000-0000-4000-8000-000000000002", + "type": "meeting", + "date": "2026-03-03", + "members": [], + "body": "Voorbeeld Body 3", + "status": "open", + "attendees": [], + "actions": [] + }, + { + "@self": { + "register": "planninq", + "schema": "risk", + "slug": "risk-voorbeeld-title-1-1" + }, + "title": "Voorbeeld Title 1", + "project": "00000000-0000-4000-8000-000000000000", + "likelihood": 1, + "impact": 1, + "members": [], + "description": "Voorbeeld Description 1", + "category": "Voorbeeld Category 1", + "score": 1, + "status": "open", + "owner": "Voorbeeld Owner 1", + "response": "avoid", + "countermeasures": "Voorbeeld Countermeasures 1", + "reviewDate": "2026-03-01" + }, + { + "@self": { + "register": "planninq", + "schema": "risk", + "slug": "risk-voorbeeld-title-2-2" + }, + "title": "Voorbeeld Title 2", + "project": "00000000-0000-4000-8000-000000000001", + "likelihood": 2, + "impact": 2, + "members": [], + "description": "Voorbeeld Description 2", + "category": "Voorbeeld Category 2", + "score": 2, + "status": "mitigating", + "owner": "Voorbeeld Owner 2", + "response": "reduce", + "countermeasures": "Voorbeeld Countermeasures 2", + "reviewDate": "2026-03-02" + }, + { + "@self": { + "register": "planninq", + "schema": "risk", + "slug": "risk-voorbeeld-title-3-3" + }, + "title": "Voorbeeld Title 3", + "project": "00000000-0000-4000-8000-000000000002", + "likelihood": 3, + "impact": 3, + "members": [], + "description": "Voorbeeld Description 3", + "category": "Voorbeeld Category 3", + "score": 3, + "status": "closed", + "owner": "Voorbeeld Owner 3", + "response": "transfer", + "countermeasures": "Voorbeeld Countermeasures 3", + "reviewDate": "2026-03-03" } ] } diff --git a/lib/Settings/planninq_register.json b/lib/Settings/planninq_register.json index e1e31a44..c0c00833 100644 --- a/lib/Settings/planninq_register.json +++ b/lib/Settings/planninq_register.json @@ -3,7 +3,7 @@ "info": { "title": "Planninq Register", "description": "Register containing all schemas for the Planninq application.", - "version": "0.7.0" + "version": "0.8.0" }, "x-openregister": { "type": "application", @@ -18,7 +18,7 @@ "title": "Planninq", "description": "Planninq application data register — tasks, projects, columns, time entries, and labels.", "slug": "planninq", - "version": "0.7.0", + "version": "0.8.0", "schemas": [ "task", "project", @@ -27,7 +27,9 @@ "plannedTimeEntry", "label", "dependency", - "timetableSession" + "timetableSession", + "projectLogEntry", + "risk" ], "_note": "`tablePrefix` and `folder` stay on the PRE-RENAME `planix` deliberately, and are not part of this rename. They name PHYSICAL things that already exist: the folder is the Nextcloud Files node OpenRegister created for this register (RegisterService::ensureRegisterFolderExists() replaces a legacy string path with the numeric node id on first run), and moving either would strand what is already stored there rather than rename it. The slug is the only value the import resolves a register by; it is the only one that moves.", "tablePrefix": "planix", @@ -1191,6 +1193,345 @@ "format": "date-time" } } + }, + "projectLogEntry": { + "slug": "projectLogEntry", + "icon": "NotebookOutline", + "version": "0.1.0", + "title": "Log entry", + "description": "An issue, lesson learned, meeting or decision recorded in a project's log.", + "x-schema-org": "schema:Comment", + "type": "object", + "required": [ + "title", + "project", + "type", + "date" + ], + "authorization": { + "read": [ + { + "group": "authenticated", + "match": { + "members": { + "$contains": "$userId" + } + } + }, + { + "group": "admin" + } + ], + "create": [ + { + "group": "authenticated" + }, + { + "group": "admin" + } + ], + "update": [ + { + "group": "authenticated", + "match": { + "members": { + "$contains": "$userId" + } + } + }, + { + "group": "admin" + } + ], + "delete": [ + { + "group": "authenticated", + "match": { + "members": { + "$contains": "$userId" + } + } + }, + { + "group": "admin" + } + ] + }, + "properties": { + "title": { + "title": "Title", + "type": "string", + "description": "A short name for the entry" + }, + "project": { + "title": "Project", + "type": "string", + "description": "The project this entry belongs to", + "format": "uuid", + "$ref": "project" + }, + "members": { + "title": "Members", + "type": "array", + "description": "Nextcloud user UIDs of project members", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." + }, + "type": { + "title": "Type", + "type": "string", + "description": "What kind of entry this is", + "enum": [ + "issue", + "lesson", + "meeting", + "decision" + ], + "default": "issue", + "x-enum-labels": { + "issue": "Issue", + "lesson": "Lesson learned", + "meeting": "Meeting", + "decision": "Decision" + } + }, + "date": { + "title": "Date", + "type": "string", + "description": "When it happened", + "format": "date" + }, + "body": { + "title": "Details", + "type": "string", + "description": "What happened, in Markdown" + }, + "status": { + "title": "Status", + "type": "string", + "description": "Whether an issue is still open", + "enum": [ + "open", + "closed" + ], + "default": "open", + "x-enum-labels": { + "open": "Open", + "closed": "Closed" + } + }, + "attendees": { + "title": "Attendees", + "type": "array", + "description": "The people at a meeting", + "items": { + "type": "string" + }, + "default": [] + }, + "actions": { + "title": "Actions", + "type": "array", + "description": "Tasks that came out of this entry", + "items": { + "type": "string", + "format": "uuid" + }, + "default": [], + "x-notes": "Ids of task objects with issueType 'action' in the same project. The author and the time of an entry are OpenRegister's own object metadata (@self.owner, @self.created), so a client cannot set them (projects-overview-logs-risks, design decision 2)." + } + } + }, + "risk": { + "slug": "risk", + "icon": "AlertOutline", + "version": "0.1.0", + "title": "Risk", + "description": "A risk to a project, scored on likelihood and impact, with an owner, a response and its countermeasures.", + "x-schema-org": "schema:Thing", + "type": "object", + "required": [ + "title", + "project", + "likelihood", + "impact" + ], + "authorization": { + "read": [ + { + "group": "authenticated", + "match": { + "members": { + "$contains": "$userId" + } + } + }, + { + "group": "admin" + } + ], + "create": [ + { + "group": "authenticated" + }, + { + "group": "admin" + } + ], + "update": [ + { + "group": "authenticated", + "match": { + "members": { + "$contains": "$userId" + } + } + }, + { + "group": "admin" + } + ], + "delete": [ + { + "group": "authenticated", + "match": { + "members": { + "$contains": "$userId" + } + } + }, + { + "group": "admin" + } + ] + }, + "x-openregister-calculations": { + "score": { + "type": "integer", + "materialise": true, + "expression": { + "*": [ + { + "prop": "likelihood" + }, + { + "prop": "impact" + } + ] + } + } + }, + "properties": { + "title": { + "title": "Title", + "type": "string", + "description": "What could go wrong" + }, + "project": { + "title": "Project", + "type": "string", + "description": "The project this risk belongs to", + "format": "uuid", + "$ref": "project" + }, + "members": { + "title": "Members", + "type": "array", + "description": "Nextcloud user UIDs of project members", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." + }, + "description": { + "title": "Description", + "type": "string", + "description": "The cause and the effect, in Markdown" + }, + "category": { + "title": "Category", + "type": "string", + "description": "A free grouping, such as planning, budget or supplier" + }, + "likelihood": { + "title": "Likelihood", + "type": "integer", + "description": "How likely it is, on the admin's risk scale", + "minimum": 1, + "maximum": 5 + }, + "impact": { + "title": "Impact", + "type": "integer", + "description": "How hard it would hit, on the admin's risk scale", + "minimum": 1, + "maximum": 5 + }, + "score": { + "title": "Score", + "type": "integer", + "description": "Likelihood times impact, calculated by the server", + "x-notes": "Materialised by OpenRegister from x-openregister-calculations on every save; a score a client sends is overwritten (projects-overview-logs-risks, design decision 3)." + }, + "status": { + "title": "Status", + "type": "string", + "description": "Where the risk stands", + "enum": [ + "open", + "mitigating", + "closed", + "occurred" + ], + "default": "open", + "x-enum-labels": { + "open": "Open", + "mitigating": "Mitigating", + "closed": "Closed", + "occurred": "Occurred" + } + }, + "owner": { + "title": "Owner", + "type": "string", + "description": "The person who watches this risk" + }, + "response": { + "title": "Response", + "type": "string", + "description": "How the project answers the risk", + "enum": [ + "avoid", + "reduce", + "transfer", + "accept" + ], + "default": "reduce", + "x-enum-labels": { + "avoid": "Avoid", + "reduce": "Reduce", + "transfer": "Transfer", + "accept": "Accept" + } + }, + "countermeasures": { + "title": "Countermeasures", + "type": "string", + "description": "What is done to prevent it or soften it, in Markdown" + }, + "reviewDate": { + "title": "Review date", + "type": "string", + "description": "When to look at this risk again", + "format": "date" + } + } } }, "objects": [ @@ -1616,4 +1957,4 @@ } ] } -} \ No newline at end of file +} diff --git a/openspec/changes/projects-overview-logs-risks/design.md b/openspec/changes/archive/2026-09-28-projects-overview-logs-risks/design.md similarity index 74% rename from openspec/changes/projects-overview-logs-risks/design.md rename to openspec/changes/archive/2026-09-28-projects-overview-logs-risks/design.md index a7f5f03c..52af8479 100644 --- a/openspec/changes/projects-overview-logs-risks/design.md +++ b/openspec/changes/archive/2026-09-28-projects-overview-logs-risks/design.md @@ -67,3 +67,14 @@ Alternative considered: OpenRegister notes on the project object, as the task si - [The heat map and the list disagree after a scale change] -> The scale bounds `likelihood` and `impact`. Lowering the number of levels is refused while a risk uses a higher value, with the count of risks in the way. - [Log entries grow without bound] -> The Log tab pages through OpenRegister with `_limit`, and the overview reads only the latest five. - [A risk owner who leaves the project] -> The Risks tab marks an owner who is no longer on the project, and the member clean-up of `projects-members-and-roles` leaves the risk in place for a manager to reassign. + +## Changes at build time (28 Sep 2026, at development 86840fa) + +The design was written before three things it leans on were settled. The build follows the code at HEAD: + +- **Authorization.** `projects-members-and-roles` task 1.1 is not built. `projectLogEntry` and `risk` use the settled pattern from planninq#681 instead: a hidden `members` list with the rule `{"members": {"$contains": "$userId"}}` on read, update and delete, no `match` on create, and both schemas added to `ProjectMembershipService::SCOPED_SCHEMAS` and to the gated set of `ProjectMemberAccessListener`, which stamps the members and refuses a create by someone not on the project. The existing member sync and back-fill repair step cover the new schemas through `SCOPED_SCHEMAS`. +- **Creating the action.** `tasks-create-edit-delete` is not built. "Add action" uses `createTask` in `src/store/projects.js` (added by backlog-list). Because the board is column based since boards-configurable-columns, the action lands at the bottom of the project's first column, so it appears on the board as the scenario asks; a project with no columns gets it in the backlog. +- **The risk scale.** The scale lives in `RiskScaleService` (validation, the in-use check, the refusal text) rather than in `SettingsService`. `SettingsController::create()` refuses a malformed scale with 400 and a smaller scale that risks still exceed with 409, `{"error": "risk-scale-in-use", "count": 2, "level": 5}`; `SettingsService` keeps a malformed value out of the config on any other path. The tests are `tests/unit/Service/RiskScaleServiceTest.php` and `tests/unit/Controller/SettingsControllerTest.php`, not `SettingsServiceTest.php`. +- **Schema count.** The register now declares ten schemas; `testRegisterDeclaresExactlyTenSchemas` and the scenario in `openspec/specs/project-delivery/spec.md` name them. +- **Names.** The overview lists people by display name through Nextcloud's autocomplete endpoint (`src/utils/userNames.js`), which every signed-in user may call, falling back to the user id. +- **Menu.** Risks under Projects is a child entry of the Projects menu item, beside "All projects". The `RiskIndex` page is declared before `ProjectBoard` in the manifest. diff --git a/openspec/changes/projects-overview-logs-risks/proposal.md b/openspec/changes/archive/2026-09-28-projects-overview-logs-risks/proposal.md similarity index 100% rename from openspec/changes/projects-overview-logs-risks/proposal.md rename to openspec/changes/archive/2026-09-28-projects-overview-logs-risks/proposal.md diff --git a/openspec/changes/projects-overview-logs-risks/specs/project-logs/spec.md b/openspec/changes/archive/2026-09-28-projects-overview-logs-risks/specs/project-logs/spec.md similarity index 100% rename from openspec/changes/projects-overview-logs-risks/specs/project-logs/spec.md rename to openspec/changes/archive/2026-09-28-projects-overview-logs-risks/specs/project-logs/spec.md diff --git a/openspec/changes/projects-overview-logs-risks/specs/project-overview/spec.md b/openspec/changes/archive/2026-09-28-projects-overview-logs-risks/specs/project-overview/spec.md similarity index 100% rename from openspec/changes/projects-overview-logs-risks/specs/project-overview/spec.md rename to openspec/changes/archive/2026-09-28-projects-overview-logs-risks/specs/project-overview/spec.md diff --git a/openspec/changes/projects-overview-logs-risks/specs/risk-register/spec.md b/openspec/changes/archive/2026-09-28-projects-overview-logs-risks/specs/risk-register/spec.md similarity index 100% rename from openspec/changes/projects-overview-logs-risks/specs/risk-register/spec.md rename to openspec/changes/archive/2026-09-28-projects-overview-logs-risks/specs/risk-register/spec.md diff --git a/openspec/changes/projects-overview-logs-risks/tasks.md b/openspec/changes/archive/2026-09-28-projects-overview-logs-risks/tasks.md similarity index 54% rename from openspec/changes/projects-overview-logs-risks/tasks.md rename to openspec/changes/archive/2026-09-28-projects-overview-logs-risks/tasks.md index d56128c5..d3f0c416 100644 --- a/openspec/changes/projects-overview-logs-risks/tasks.md +++ b/openspec/changes/archive/2026-09-28-projects-overview-logs-risks/tasks.md @@ -2,24 +2,24 @@ ## 1. Overview and tabs (MVP) -- [ ] 1.1 Add `src/components/ProjectTabs.vue` and use it in `ProjectBoard.vue`, `ProjectBacklog.vue` and `ProjectTimeline.vue` in place of the Backlog and Timeline buttons. Verify: `tests/e2e/project-overview.spec.ts` "moving between project pages by keyboard". -- [ ] 1.2 Add the `ProjectOverview` page (`/projects/:id/overview`) to `src/manifest.json` and `src/registry.js`, with progress computed from `fetchTasks`. Verify: `tests/vitest/projectProgress.spec.js` for the done over not-cancelled rule; e2e "the overview shows where the project stands" and "an empty project says so". -- [ ] 1.3 Update `testRegisterDeclaresExactlySevenSchemas` in `tests/unit/Settings/PlanninqRegisterSchemaTest.php` and the scenario in `openspec/specs/project-delivery/spec.md:68-72` to the new schema list. Verify: the PHPUnit suite passes with nine schemas named. +- [x] 1.1 Add `src/components/ProjectTabs.vue` and use it in `ProjectBoard.vue`, `ProjectBacklog.vue` and `ProjectTimeline.vue` in place of the Backlog and Timeline buttons. Verify: `tests/e2e/project-overview.spec.ts` "moving between project pages by keyboard". +- [x] 1.2 Add the `ProjectOverview` page (`/projects/:id/overview`) to `src/manifest.json` and `src/registry.js`, with progress computed from `fetchTasks`. Verify: `tests/vitest/projectProgress.spec.js` for the done over not-cancelled rule; e2e "the overview shows where the project stands" and "an empty project says so". +- [x] 1.3 Update the exact-schema-count test in `tests/unit/Settings/PlanninqRegisterSchemaTest.php` (was `testRegisterDeclaresExactlyEightSchemas`, now `testRegisterDeclaresExactlyTenSchemas`) and the scenario in `openspec/specs/project-delivery/spec.md` to the new schema list. Verify: the PHPUnit suite passes with ten schemas named. ## 2. Project log (V1) -- [ ] 2.1 Add the `projectLogEntry` schema with the project-scoped authorization settled by `projects-members-and-roles` task 1.1. Verify: `PlanninqRegisterSchemaTest.php` asserts the properties and the read rule; a live GET as an outsider returns none of the entries. -- [ ] 2.2 Add the `ProjectLog` page (`/projects/:id/log`) with type filters and `src/dialogs/LogEntryEditDialog.vue`. Verify: `tests/e2e/project-log.spec.ts` "recording a meeting". -- [ ] 2.3 Add "Add action", which creates a task with `issueType: 'action'` through the task create path of `tasks-create-edit-delete` and links it in `actions`. Verify: e2e "turning a meeting outcome into an action". +- [x] 2.1 Add the `projectLogEntry` schema with the project-scoped authorization settled by `projects-members-and-roles` task 1.1. Verify: `PlanninqRegisterSchemaTest.php` asserts the properties and the read rule; a live GET as an outsider returns none of the entries. +- [x] 2.2 Add the `ProjectLog` page (`/projects/:id/log`) with type filters and `src/dialogs/LogEntryEditDialog.vue`. Verify: `tests/e2e/project-log.spec.ts` "recording a meeting". +- [x] 2.3 Add "Add action", which creates a task with `issueType: 'action'` through the task create path of `tasks-create-edit-delete` and links it in `actions`. Verify: e2e "turning a meeting outcome into an action". ## 3. Risk register (V1) -- [ ] 3.1 Add the `risk` schema with `score` as a materialised calculation and the project-scoped authorization. Verify: `PlanninqRegisterSchemaTest.php` asserts the calculation; a live POST with a wrong score stores the calculated one. -- [ ] 3.2 Add the `ProjectRisks` page (`/projects/:id/risks`), `src/components/RiskHeatMap.vue` and `src/dialogs/RiskEditDialog.vue`. Verify: `tests/vitest/riskHeatMap.spec.js` for cell counts and band text; e2e `tests/e2e/risk-register.spec.ts` "adding a risk". -- [ ] 3.3 Add the declarative `RiskIndex` page (`/projects/risks`, `type: "index"`) under Projecten. Verify: e2e "a project leader scans risks across projects", run as a user on two of three seeded projects. -- [ ] 3.4 Add `risk_scale` to `SettingsService` with validation against the levels in use, and its form on the admin page. Verify: `tests/unit/Service/SettingsServiceTest.php` for a valid scale and a refused one; e2e "switching to a three-level scale" and "a smaller scale is refused while risks use a higher level". +- [x] 3.1 Add the `risk` schema with `score` as a materialised calculation and the project-scoped authorization. Verify: `PlanninqRegisterSchemaTest.php` asserts the calculation; a live POST with a wrong score stores the calculated one. +- [x] 3.2 Add the `ProjectRisks` page (`/projects/:id/risks`), `src/components/RiskHeatMap.vue` and `src/dialogs/RiskEditDialog.vue`. Verify: `tests/vitest/riskHeatMap.spec.js` for cell counts and band text; e2e `tests/e2e/risk-register.spec.ts` "adding a risk". +- [x] 3.3 Add the declarative `RiskIndex` page (`/projects/risks`, `type: "index"`) under Projecten. Verify: e2e "a project leader scans risks across projects", run as a user on two of three seeded projects. +- [x] 3.4 Add `risk_scale` to the admin settings (`RiskScaleService`, `SettingsController`, `SettingsService`) with validation against the levels in use, and its form on the admin page. Verify: `tests/unit/Service/RiskScaleServiceTest.php` and `tests/unit/Controller/SettingsControllerTest.php` for a valid scale and a refused one; e2e "switching to a three-level scale" and "a smaller scale is refused while risks use a higher level". ## 4. Verification -- [ ] 4.1 `openspec validate projects-overview-logs-risks --type change --strict` passes. -- [ ] 4.2 Every scenario in specs/ is covered by a test named in the task above it, or carries an `@e2e exclude ` note. +- [x] 4.1 `openspec validate projects-overview-logs-risks --type change --strict` passes. +- [x] 4.2 Every scenario in specs/ is covered by a test named in the task above it, or carries an `@e2e exclude ` note. diff --git a/openspec/parity/capabilities.json b/openspec/parity/capabilities.json index 17008c0b..bf5c803e 100644 --- a/openspec/parity/capabilities.json +++ b/openspec/parity/capabilities.json @@ -1324,7 +1324,7 @@ "area": "projects", "name": "Open a project overview page showing its description, members and progress.", "origin": "competitor-derived", - "planninq": "partial", + "planninq": "yes", "nextcloud-deck": "partial", "openproject": "yes", "plane": "partial", @@ -1342,11 +1342,12 @@ "plane": "source: no overview route under apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/ (archives, cycles, intake, issues, modules, pages, views only); the project card shows description and member count (apps/web/core/components/project/card.tsx:285-292). Project updates are a paid string never emitted (corpus: plane/round4/code-census.md opening section, is_project_updates_enabled 0 hits) ; source read at v1.4.2: project detail routes under apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/ are archives, cycles, intake, issues, modules, pages, views only, no overview; apps/web/core/components/project/card.tsx:275-285 the project card shows description and member count" }, "built": { - "state": "specified", - "evidence": "src/views/ProjectBoard.vue:26-64 (title/icon/color header) opens src/components/ProjectSettingsSidebar.vue:16-127 (Details tab shows description; Members tab lists members). No progress indicator anywhere: task.percentComplete (schema property) is never read in src/ (grep confirmed zero matches).", - "reachedOn": "ProjectBoard page + its settings sidebar (Details/Members tabs), not a single dedicated overview page", + "state": "built", + "evidence": "src/views/ProjectOverview.vue shows description, start and end dates, the people by display name, progress as '{done} of {total} tasks done' (src/utils/projectOverview.js projectProgress(): done over not cancelled), the three highest open risks and the five latest log entries; src/components/ProjectTabs.vue is the shared tab row on every project page", + "reachedOn": "ProjectOverview page /projects/:id/overview, Overview tab on every project page", "owner": "ConductionNL/planninq", - "change": "openspec/changes/projects-overview-logs-risks" + "change": "openspec/changes/archive/2026-09-28-projects-overview-logs-risks", + "readOn": "2026-09-28" }, "provider": "planninq", "providerHow": "read-from-code", @@ -6051,7 +6052,7 @@ "kanboard": "source read at v1.2.54: searched app/ (php) for 'likelihood', 'risk_register', 'riskregister': none", "jira": "https://marketplace.atlassian.com/apps/1213146/risk-register paid app 'Risk Register', build 8.1.1 compatible to Jira 11.3.11: 'record risks, assess their magnitude ... collaborate with others on their treatment'; Jira itself has no risk object (Marketplace REST API, read 2026-09-26)" }, - "planninq": "no", + "planninq": "yes", "nextcloud-deck": "no", "openproject": "no", "plane": "no", @@ -6062,11 +6063,12 @@ "xedule": "unknown", "timeedit": "unknown", "built": { - "state": "specified", - "evidence": "searched src/ lib/ and lib/Settings/planninq_register.json for risk, likelihood, impact, countermeasure: none. No risk schema, no risk page in src/manifest.json pages, no risk component in src/registry.js:42-51.", - "reachedOn": "nothing reaches it", + "state": "built", + "evidence": "lib/Settings/planninq_register.json risk with score as a materialised x-openregister-calculations likelihood * impact, scoped to the project's members; src/views/ProjectRisks.vue list by score and src/components/RiskHeatMap.vue with band text; RiskIndex index page over every readable risk; lib/Service/RiskScaleService.php admin scale (3 to 5 levels, labels, thresholds) refused while risks use a higher level", + "reachedOn": "ProjectRisks page /projects/:id/risks, Risks under Projects (/projects/risks), Risk scale on the admin settings page", "owner": "ConductionNL/planninq", - "change": "openspec/changes/projects-overview-logs-risks" + "change": "openspec/changes/archive/2026-09-28-projects-overview-logs-risks", + "readOn": "2026-09-28" }, "provider": "planninq", "providerHow": "read-from-code", @@ -6087,7 +6089,7 @@ "kanboard": "source read at v1.2.54: searched app/Model and app/Controller for 'lesson', 'meeting', 'issue log': none; tasks only", "jira": "https://confluence.atlassian.com/adminjiraserver/defining-a-project-938847066.html 'Jira enables you to keep track of different types of things ... by using different issue types': an admin can add action, issue, lesson or meeting log issue types per project; there is no built-in project log (read 2026-09-26)" }, - "planninq": "no", + "planninq": "yes", "nextcloud-deck": "no", "openproject": "partial", "plane": "partial", @@ -6098,11 +6100,12 @@ "xedule": "unknown", "timeedit": "unknown", "built": { - "state": "specified", - "evidence": "No project-scoped log of actions, issues, lessons learned or meetings. The only logs are per task: CnObjectSidebar Comments (notes) and Activity (audit trail) tabs on src/views/TaskDetail.vue:129-137. The project settings sidebar has Details, Members and Danger zone tabs only (src/components/ProjectSettingsSidebar.vue:8-165).", - "reachedOn": "nothing reaches it", + "state": "built", + "evidence": "lib/Settings/planninq_register.json projectLogEntry (issue, lesson, meeting, decision; attendees; actions) scoped to the project's members (ProjectMembershipService::SCOPED_SCHEMAS, ProjectMemberAccessListener); src/views/ProjectLog.vue with type filters and an Actions filter; src/dialogs/LogActionDialog.vue creates a task with issueType action on the board and links it", + "reachedOn": "ProjectLog page /projects/:id/log, Log tab on every project page", "owner": "ConductionNL/planninq", - "change": "openspec/changes/projects-overview-logs-risks" + "change": "openspec/changes/archive/2026-09-28-projects-overview-logs-risks", + "readOn": "2026-09-28" }, "provider": "planninq", "providerHow": "read-from-code", diff --git a/openspec/specs/project-delivery/spec.md b/openspec/specs/project-delivery/spec.md index 56455852..d743dc1e 100644 --- a/openspec/specs/project-delivery/spec.md +++ b/openspec/specs/project-delivery/spec.md @@ -65,11 +65,11 @@ project. project-scoped schemas, because a phase is exactly as sensitive as the project it belongs to. -#### Scenario: The register declares exactly seven schemas -@e2e exclude Descriptor contents, asserted by PHPUnit (testRegisterDeclaresExactlySevenSchemas) +#### Scenario: The register declares exactly ten schemas +@e2e exclude Descriptor contents, asserted by PHPUnit (testRegisterDeclaresExactlyTenSchemas) - GIVEN the planninq register descriptor - WHEN its schema list is read -- THEN it MUST be exactly task, project, projectPhase, column, timeEntry, label and dependency +- THEN it MUST be exactly task, project, projectPhase, column, plannedTimeEntry, label, dependency, timetableSession, projectLogEntry and risk #### Scenario: An unphased project still works @e2e exclude Schema shape, asserted by PHPUnit against the register descriptor diff --git a/openspec/specs/project-logs/spec.md b/openspec/specs/project-logs/spec.md new file mode 100644 index 00000000..fa3da736 --- /dev/null +++ b/openspec/specs/project-logs/spec.md @@ -0,0 +1,36 @@ +# project-logs Specification + +## Purpose +A project keeps a log of issues, lessons learned, meetings and decisions, and turns their outcomes into actions that are ordinary tasks. + +## Requirements + +### Requirement: A project member keeps logs of issues, lessons, meetings and decisions + +Every project MUST have a log where its members record issues, lessons learned, meetings and decisions. Each entry MUST carry its type, a title, a date and a body, and the author and time MUST come from the server. Only people who can read the project MUST see its log. Tier: V1 (docs/FEATURES.md has no row; tender 365739 requirement 4012). + +#### Scenario: Recording a meeting + +- **GIVEN** a project member on the Log tab at /projects/:id/log +- **WHEN** the member presses "Add entry", picks "Meeting", fills in the title "Kick-off", the date and two attendees, and saves +- **THEN** the entry appears at the top of the log with the type "Meeting", the author's name and the time it was saved +- **AND** choosing the filter "Meetings" shows it and hides the other types + +#### Scenario: An outsider cannot read the log + +@e2e exclude API-level refusal with no screen, asserted by a PHPUnit schema test and a live GET in task 2.1 +- **GIVEN** a signed-in user who is not on the project +- **WHEN** the user sends `GET /apps/openregister/api/objects/planninq/projectLogEntry?project={id}` +- **THEN** the answer contains none of that project's entries + +### Requirement: An action from a log entry is a normal task + +A project member MUST be able to create an action from an issue, meeting or decision entry. The action MUST be a task in the same project, linked from the entry, so it can be assigned, dated and moved on the board like any other task. Tier: V1 (docs/FEATURES.md has no row; tender 365739 requirement 4012). + +#### Scenario: Turning a meeting outcome into an action + +- **GIVEN** a meeting entry "Kick-off" in the project log +- **WHEN** a project member presses "Add action" on that entry, types "Send the planning to the steering group", assigns it to "Ada Jansen" and saves +- **THEN** the task appears on the project board at /projects/:id +- **AND** the entry lists the action with its status +- **AND** the Log tab filter "Actions" shows the task diff --git a/openspec/specs/project-overview/spec.md b/openspec/specs/project-overview/spec.md new file mode 100644 index 00000000..26fd5cb2 --- /dev/null +++ b/openspec/specs/project-overview/spec.md @@ -0,0 +1,36 @@ +# project-overview Specification + +## Purpose +A project member opens one screen that shows where a project stands, and moves between the project's pages through one shared row of tabs. + +## Requirements + +### Requirement: A project member opens one overview of the project + +Every project MUST have an overview page that shows its description, planned dates, the people on it, its progress, its highest open risks and its latest log entries. Progress MUST count done tasks against all tasks that are not cancelled. Tier: MVP (docs/FEATURES.md, "Project progress (tasks done / total)"). + +#### Scenario: The overview shows where the project stands + +- **GIVEN** a project member on a project with 10 tasks, of which 4 are done and 1 is cancelled +- **WHEN** the member opens the Overview tab at /projects/:id/overview +- **THEN** the page shows the project's description and its start and end dates +- **AND** it lists the people on the project by display name +- **AND** it shows progress as "4 of 9 tasks done" + +#### Scenario: An empty project says so + +- **GIVEN** a project member on a project with no tasks, no risks and no log entries +- **WHEN** the member opens /projects/:id/overview +- **THEN** the progress block says "No tasks yet" +- **AND** the risks and log blocks each offer a link to add the first one + +### Requirement: The project pages share one row of tabs + +The board, backlog, timeline, overview, risks and log pages of a project MUST show the same row of tabs, with the current page marked for assistive technology. The tabs MUST be reachable and operable by keyboard. Tier: MVP (docs/FEATURES.md, "Kanban board view per project"). + +#### Scenario: Moving between project pages by keyboard + +- **GIVEN** a project member on the board at /projects/:id +- **WHEN** the member moves focus with Tab to the row of project tabs and activates "Risks" with Enter +- **THEN** the page at /projects/:id/risks opens +- **AND** the "Risks" tab carries `aria-current="page"` diff --git a/openspec/specs/risk-register/spec.md b/openspec/specs/risk-register/spec.md new file mode 100644 index 00000000..55fc0bad --- /dev/null +++ b/openspec/specs/risk-register/spec.md @@ -0,0 +1,51 @@ +# risk-register Specification + +## Purpose +A project keeps a register of its risks, each scored by the server on the admin's likelihood and impact scale, shown per project as a list and a heat map and across projects under Projects. + +## Requirements + +### Requirement: A project member keeps a scored risk register + +Every project MUST have a risk register. Each risk MUST carry a likelihood and an impact on the admin's risk scale, a score that the server calculates from them, a status, an owner, a response and its countermeasures. The Risks tab MUST show the risks as a list sorted by score and as a heat map of likelihood against impact. Tier: V1 (docs/FEATURES.md has no row; tender 365739 requirements 4003 and 4004). + +#### Scenario: Adding a risk + +- **GIVEN** a project member on the Risks tab at /projects/:id/risks +- **WHEN** the member presses "Add risk", enters the title "Supplier delivers late", sets likelihood 4 and impact 3, picks the response "Reduce", writes a countermeasure and saves +- **THEN** the risk appears in the list with the score 12 +- **AND** the heat map cell for likelihood 4 and impact 3 shows the count 1 and the band name as text + +#### Scenario: A client cannot write its own score + +@e2e exclude Server-side calculation with no screen, asserted by a PHPUnit schema test and a live POST in task 3.1 +- **GIVEN** a project member +- **WHEN** the member sends `POST /apps/openregister/api/objects/planninq/risk` with likelihood 2, impact 2 and score 25 +- **THEN** the stored risk has the score 4 + +### Requirement: Projecten lists the risks of every project you can see + +The Projecten menu MUST have a Risks sub-page that lists the risks of every project the user can read, highest score first, with the project, owner, status and review date. A user MUST NOT see risks of projects they cannot read. Tier: V1 (docs/FEATURES.md has no row; ADR-001 maps `risk-register-issue-tracking` to Projecten). + +#### Scenario: A project leader scans risks across projects + +- **GIVEN** a user on two projects with three open risks between them, and a third project the user is not on +- **WHEN** the user opens Risks under Projecten at /projects/risks +- **THEN** the page lists the three risks, highest score first, each with its project +- **AND** no risk of the third project is listed + +### Requirement: An admin sets the risk scale + +An admin MUST be able to set the number of likelihood and impact levels (3 to 5), a label per level, and the two score thresholds between low, medium and high. The heat map MUST colour its cells by band with NL Design tokens and MUST also show each band as text. Tier: V1 (docs/FEATURES.md has no row; tender 365739 requirement 64441). + +#### Scenario: Switching to a three-level scale + +- **GIVEN** an admin on the planninq admin settings page, and no risk uses a level above 3 +- **WHEN** the admin sets the risk scale to 3 levels with the labels "Low", "Medium" and "High" and saves +- **THEN** the Risks tab of every project shows a three by three heat map with those labels + +#### Scenario: A smaller scale is refused while risks use a higher level + +- **GIVEN** an admin, and two risks with likelihood 5 +- **WHEN** the admin tries to save a scale of 4 levels +- **THEN** the save is refused with "2 risks use level 5. Change them first." diff --git a/src/components/ProjectTabs.vue b/src/components/ProjectTabs.vue new file mode 100644 index 00000000..5bc56406 --- /dev/null +++ b/src/components/ProjectTabs.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/src/components/RiskHeatMap.vue b/src/components/RiskHeatMap.vue new file mode 100644 index 00000000..46ccecba --- /dev/null +++ b/src/components/RiskHeatMap.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/dialogs/LogActionDialog.vue b/src/dialogs/LogActionDialog.vue new file mode 100644 index 00000000..2d8f28c6 --- /dev/null +++ b/src/dialogs/LogActionDialog.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/src/dialogs/LogEntryEditDialog.vue b/src/dialogs/LogEntryEditDialog.vue new file mode 100644 index 00000000..f541be06 --- /dev/null +++ b/src/dialogs/LogEntryEditDialog.vue @@ -0,0 +1,207 @@ + + + + + diff --git a/src/dialogs/RiskEditDialog.vue b/src/dialogs/RiskEditDialog.vue new file mode 100644 index 00000000..10c66e52 --- /dev/null +++ b/src/dialogs/RiskEditDialog.vue @@ -0,0 +1,242 @@ + + + + + diff --git a/src/icons.js b/src/icons.js index eb9231e4..857a3594 100644 --- a/src/icons.js +++ b/src/icons.js @@ -16,6 +16,7 @@ import AccountGroup from 'vue-material-design-icons/AccountGroup.vue' import AccountMultiplePlus from 'vue-material-design-icons/AccountMultiplePlus.vue' +import AlertOutline from 'vue-material-design-icons/AlertOutline.vue' import BookOpenVariantOutline from 'vue-material-design-icons/BookOpenVariantOutline.vue' import BriefcaseOutline from 'vue-material-design-icons/BriefcaseOutline.vue' import ChartBar from 'vue-material-design-icons/ChartBar.vue' @@ -40,6 +41,7 @@ import Web from 'vue-material-design-icons/Web.vue' export default { AccountGroup, AccountMultiplePlus, + AlertOutline, BookOpenVariantOutline, BriefcaseOutline, ChartBar, diff --git a/src/manifest.json b/src/manifest.json index 22c5f332..aebbb5a7 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -48,7 +48,10 @@ "menu": [ { "id": "Dashboard", "label": "Dashboard", "icon": "ViewDashboardOutline", "route": "Dashboard", "order": 10 }, { "id": "Boards", "label": "Boards", "icon": "ViewDashboardOutline", "route": "Boards", "order": 20 }, - { "id": "Projects", "label": "Projects", "icon": "FolderOutline", "route": "Projects", "order": 30 }, + { "id": "Projects", "label": "Projects", "icon": "FolderOutline", "route": "Projects", "order": 30, "children": [ + { "id": "ProjectsAll", "label": "All projects", "icon": "FolderOutline", "route": "Projects" }, + { "id": "RiskIndexMenu", "label": "Risks", "icon": "AlertOutline", "route": "RiskIndex" } + ] }, { "id": "Timesheet", "label": "Timesheet", "icon": "ClockOutline", "route": "Timesheet", "order": 50 }, { "id": "Documentation", "label": "Documentation", "icon": "BookOpenVariantOutline", "href": "https://planix.conduction.nl", "section": "footer", "order": 90 }, { "id": "StoreMenu", "label": "Store", "icon": "StoreOutline", "route": "Store", "section": "footer", "order": 92 }, @@ -142,9 +145,13 @@ } }, { "id": "Projects", "_note": "Project index rendered as cards with a client-side status chip filter over filteredProjects, plus a free-text field. The chip row is a filter surface CnIndexPage does not express, so the list is a host-app SFC.", "route": "/projects", "type": "custom", "title": "Projects", "component": "ProjectList", "config": {} }, + { "id": "RiskIndex", "_note": "Every risk of every project the user can read, highest score first. OpenRegister's read rule on the members list decides which risks a user sees, so the page is a declarative index. Declared before ProjectBoard so /projects/risks is never read as a project id.", "route": "/projects/risks", "type": "index", "title": "Risks", "config": { "register": "planninq", "schema": "risk", "readOnly": true, "showViewAction": false, "columns": ["title", "project", "score", "status", "owner", "reviewDate"], "defaultSort": [{ "field": "score", "order": "desc" }] } }, { "id": "ProjectBoard", "_note": "Bespoke Kanban board: one column per project status, with drag-to-transition between columns and a per-card move menu listing the other columns as targets. No v2 primitive renders a column-per-status board or owns the drop-to-transition write.", "route": "/projects/:id", "type": "custom", "title": "Project board", "component": "ProjectBoard", "config": {} }, { "id": "ProjectBacklog", "_note": "Backlog under a project: a parent breadcrumb back to the board above an ordered task list scoped to :id. The parent-scoped header is the part CnIndexPage does not carry.", "route": "/projects/:id/backlog", "type": "custom", "title": "Backlog", "component": "ProjectBacklog", "config": {} }, { "id": "ProjectTimeline", "_note": "Gantt over a project's tasks: positioned bars plus dependency arrows drawn between them, fed by /api/timeline. The roadmap type renders milestones on a track and has no vocabulary for task bars with dependency edges.", "route": "/projects/:id/timeline", "type": "custom", "title": "Timeline", "component": "ProjectTimeline", "config": {} }, + { "id": "ProjectOverview", "_note": "One screen per project: description, dates, people, progress from the project's tasks, and its latest log entries, each block linking to its tab. It reads three schemas for one project and computes progress, which no detail page expresses.", "route": "/projects/:id/overview", "type": "custom", "title": "Overview", "component": "ProjectOverview", "config": {} }, + { "id": "ProjectRisks", "_note": "The project's risk register: a heat map of likelihood against impact on the admin's risk scale beside the list sorted by score. The heat map is not an index, so the page is custom.", "route": "/projects/:id/risks", "type": "custom", "title": "Risks", "component": "ProjectRisks", "config": {} }, + { "id": "ProjectLog", "_note": "The project log: issues, lessons, meetings and decisions with type filters, plus an Actions filter over the project's tasks with issueType action, and an Add action that creates such a task. The mix of two schemas under one filter row is what makes it custom.", "route": "/projects/:id/log", "type": "custom", "title": "Log", "component": "ProjectLog", "config": {} }, { "id": "TaskDetail", "_note": "Task detail that combines a CnObjectSidebar with an inline time-entry log written against the same task. A detail page renders the record; the embedded logging surface is what makes this custom.", "route": "/projects/:id/tasks/:taskId", "type": "custom", "title": "Task", "component": "TaskDetail", "config": {} }, { "id": "Timesheet", "_note": "Week timesheet: entries grouped per day with inline edit and a project selector. A grouped, editable grid over time entries, which no v2 list primitive expresses.", "route": "/timesheet", "type": "custom", "title": "Timesheet", "component": "Timesheet", "config": {} }, { "id": "Boards", "_note": "Board picker: the projects a user can open a board for, as entry points rather than records. The cards route to /projects/:id and expose no record actions.", "route": "/boards", "type": "custom", "title": "Boards", "component": "Boards", "config": {} }, diff --git a/src/registry.js b/src/registry.js index e37dc737..a135adc4 100644 --- a/src/registry.js +++ b/src/registry.js @@ -25,6 +25,9 @@ import Portfolio from './views/Portfolio.vue' import ProjectBacklog from './views/ProjectBacklog.vue' import ProjectBoard from './views/ProjectBoard.vue' import ProjectList from './views/ProjectList.vue' +import ProjectLog from './views/ProjectLog.vue' +import ProjectOverview from './views/ProjectOverview.vue' +import ProjectRisks from './views/ProjectRisks.vue' import ProjectTimeline from './views/ProjectTimeline.vue' import TaskDetail from './views/TaskDetail.vue' import Timesheet from './views/Timesheet.vue' @@ -45,6 +48,9 @@ export default { ProjectBacklog: page(ProjectBacklog), ProjectBoard: page(ProjectBoard), ProjectList: page(ProjectList), + ProjectLog: page(ProjectLog), + ProjectOverview: page(ProjectOverview), + ProjectRisks: page(ProjectRisks), ProjectTimeline: page(ProjectTimeline), TaskDetail: page(TaskDetail), Timesheet: page(Timesheet), diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js index 38c3d7cf..a84fab16 100644 --- a/src/store/modules/settings.js +++ b/src/store/modules/settings.js @@ -83,6 +83,38 @@ export const useSettingsStore = defineStore('settings', { return null }, + /** + * Save the risk scale. The server refuses a malformed scale (400) and a + * smaller one while risks still use a higher level (409, with the count + * and the level). + * + * @param {object} scale The scale + * @return {Promise<{ok: boolean, error?: string, count?: number, level?: number}>} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.4 + */ + async saveRiskScale(scale) { + try { + const response = await fetch(generateUrl('/apps/planninq/api/settings'), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + requesttoken: OC.requestToken, + }, + body: JSON.stringify({ risk_scale: JSON.stringify(scale) }), + }) + const body = await response.json().catch(() => ({})) + if (!response.ok) { + return { ok: false, error: body?.error || 'risk-scale-error', count: body?.count, level: body?.level } + } + await this.fetchSettings() + return { ok: true } + } catch (error) { + console.error('Failed to save the risk scale:', error) + return { ok: false, error: 'risk-scale-error' } + } + }, + /** * Persist per-user settings (notification toggles) to the backend. * diff --git a/src/store/projects.js b/src/store/projects.js index 252f6840..315aaf19 100644 --- a/src/store/projects.js +++ b/src/store/projects.js @@ -28,6 +28,8 @@ const COLUMN_SCHEMA = 'column' const TASK_SCHEMA = 'task' const TIME_ENTRY_SCHEMA = 'plannedTimeEntry' const LABEL_SCHEMA = 'label' +const LOG_SCHEMA = 'projectLogEntry' +const RISK_SCHEMA = 'risk' /** * Largest page OpenRegister will return. Asking for more is silently capped. @@ -124,6 +126,12 @@ export const useProjectsStore = defineStore('projects', { if (!store.objectTypeRegistry?.[LABEL_SCHEMA]) { store.registerObjectType(LABEL_SCHEMA, LABEL_SCHEMA, REGISTER, { registerSlug: REGISTER, schemaSlug: LABEL_SCHEMA }) } + if (!store.objectTypeRegistry?.[LOG_SCHEMA]) { + store.registerObjectType(LOG_SCHEMA, LOG_SCHEMA, REGISTER, { registerSlug: REGISTER, schemaSlug: LOG_SCHEMA }) + } + if (!store.objectTypeRegistry?.[RISK_SCHEMA]) { + store.registerObjectType(RISK_SCHEMA, RISK_SCHEMA, REGISTER, { registerSlug: REGISTER, schemaSlug: RISK_SCHEMA }) + } return store }, @@ -509,6 +517,106 @@ export const useProjectsStore = defineStore('projects', { } }, + /** + * Every log entry of a project. OpenRegister's read rule on the + * members list scopes the read to the project's members. + * + * @param {string} projectId Parent project UUID + * @return {Promise} The entries (empty array on error) + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.2 + */ + async fetchLogEntries(projectId) { + try { + const objectStore = this._objectStore() + const entries = await fetchEvery(objectStore, LOG_SCHEMA, { project: projectId }) + return Array.isArray(entries) ? entries : [] + } catch (err) { + console.error('fetchLogEntries error:', err) + return [] + } + }, + + /** + * Write a log entry: POST when it has no id, PATCH the given fields when + * it has one. The server stamps the author, the time and the members + * list, and refuses a caller who is not on the project. + * + * @param {object} entry The entry, or the fields to change plus its id + * @return {Promise} The saved entry, or null on failure + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.2 + */ + async saveLogEntry(entry) { + const { id, ...fields } = entry + const url = id + ? generateUrl(`/apps/openregister/api/objects/planninq/${LOG_SCHEMA}/${id}`) + : generateUrl(`/apps/openregister/api/objects/planninq/${LOG_SCHEMA}`) + try { + const response = await fetch(url, { + method: id ? 'PATCH' : 'POST', + headers: buildHeaders(), + body: JSON.stringify(fields), + }) + if (!response.ok) { + return null + } + return await response.json() + } catch (err) { + console.error('saveLogEntry error:', err) + return null + } + }, + + /** + * Every risk of a project, scoped by OpenRegister to its members. + * + * @param {string} projectId Parent project UUID + * @return {Promise} The risks (empty array on error) + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.2 + */ + async fetchRisks(projectId) { + try { + const objectStore = this._objectStore() + const risks = await fetchEvery(objectStore, RISK_SCHEMA, { project: projectId }) + return Array.isArray(risks) ? risks : [] + } catch (err) { + console.error('fetchRisks error:', err) + return [] + } + }, + + /** + * Write a risk: POST when it has no id, PATCH when it has one. The + * server calculates the score and refuses a caller not on the project. + * + * @param {object} risk The risk, or the fields to change plus its id + * @return {Promise} The saved risk, or null on failure + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.2 + */ + async saveRisk(risk) { + const { id, ...fields } = risk + const url = id + ? generateUrl(`/apps/openregister/api/objects/planninq/${RISK_SCHEMA}/${id}`) + : generateUrl(`/apps/openregister/api/objects/planninq/${RISK_SCHEMA}`) + try { + const response = await fetch(url, { + method: id ? 'PATCH' : 'POST', + headers: buildHeaders(), + body: JSON.stringify(fields), + }) + if (!response.ok) { + return null + } + return await response.json() + } catch (err) { + console.error('saveRisk error:', err) + return null + } + }, + // ── 2.7 archiveProject ──────────────────────────────────────────── /** diff --git a/src/utils/projectOverview.js b/src/utils/projectOverview.js new file mode 100644 index 00000000..43c840c5 --- /dev/null +++ b/src/utils/projectOverview.js @@ -0,0 +1,216 @@ +/** + * Pure helpers for the project overview, the project tabs and the project log + * (projects-overview-logs-risks). + * + * SPDX-License-Identifier: EUPL-1.2 + * SPDX-FileCopyrightText: 2026 Conduction B.V. + */ +import { buildMovePatch, sortColumns } from './columnHelpers.js' + +/** The log entry types, in the order the filter shows them. */ +export const LOG_TYPES = ['issue', 'lesson', 'meeting', 'decision'] + +/** The `issueType` of a task that came out of a log entry. */ +export const ACTION_ISSUE_TYPE = 'action' + +/** The project tabs in display order. Each names the manifest page it opens. */ +export const PROJECT_TABS = [ + { id: 'overview', route: 'ProjectOverview' }, + { id: 'board', route: 'ProjectBoard' }, + { id: 'backlog', route: 'ProjectBacklog' }, + { id: 'timeline', route: 'ProjectTimeline' }, + { id: 'risks', route: 'ProjectRisks' }, + { id: 'log', route: 'ProjectLog' }, +] + +/** + * The id of an OpenRegister object, wherever the response put it. + * + * @param {object} object The object. + * @return {string|undefined} + */ +function idOf(object) { + return object?.id ?? object?.uuid ?? object?.['@self']?.id +} + +/** + * Progress of a project: done tasks against every task that is not cancelled. + * + * `percentComplete` is not used: nothing sets it, and a mean of unset values + * reads as zero. + * + * @param {Array} tasks The project's tasks. + * @return {{done: number, total: number}} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-1.2 + */ +export function projectProgress(tasks = []) { + let done = 0 + let total = 0 + for (const task of tasks || []) { + if (!task || task.status === 'cancelled') { + continue + } + total++ + if (task.status === 'done') { + done++ + } + } + return { done, total } +} + +/** + * The people on a project: its owner first, then its members, each once. + * + * @param {object} project The project. + * @return {Array} User ids. + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-1.2 + */ +export function projectPeople(project) { + const people = [] + for (const uid of [project?.owner, ...(project?.members || [])]) { + if (typeof uid === 'string' && uid !== '' && !people.includes(uid)) { + people.push(uid) + } + } + return people +} + +/** + * When an entry was saved, from OpenRegister's own metadata. + * + * @param {object} entry The log entry. + * @return {string} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.2 + */ +export function entryCreated(entry) { + return String(entry?.['@self']?.created ?? entry?.created ?? '') +} + +/** + * Who saved an entry, from OpenRegister's own metadata. + * + * @param {object} entry The log entry. + * @return {string} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.2 + */ +export function entryAuthor(entry) { + return String(entry?.['@self']?.owner ?? '') +} + +/** + * Log entries newest first: by date, then by the time they were saved. + * + * @param {Array} entries The entries. + * @return {Array} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.2 + */ +export function sortLog(entries = []) { + return [...(entries || [])].sort((a, b) => { + const byDate = String(b?.date ?? '').localeCompare(String(a?.date ?? '')) + return byDate !== 0 ? byDate : entryCreated(b).localeCompare(entryCreated(a)) + }) +} + +/** + * The newest entries of a log, for the overview. + * + * @param {Array} entries The entries. + * @param {number} [limit] How many. + * @return {Array} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-1.2 + */ +export function latestLogEntries(entries = [], limit = 5) { + return sortLog(entries).slice(0, limit) +} + +/** + * The entries a log filter shows. `all` shows every entry; a type shows that type. + * + * @param {Array} entries The entries. + * @param {string} filter `all` or one of LOG_TYPES. + * @return {Array} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.2 + */ +export function filterLog(entries = [], filter = 'all') { + const sorted = sortLog(entries) + return LOG_TYPES.includes(filter) ? sorted.filter((entry) => entry?.type === filter) : sorted +} + +/** + * The project's actions: its tasks that came out of a log entry. + * + * @param {Array} tasks The project's tasks. + * @return {Array} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.3 + */ +export function actionTasks(tasks = []) { + return (tasks || []).filter((task) => task?.issueType === ACTION_ISSUE_TYPE) +} + +/** + * The payload of a new or edited log entry. The server adds the author, the + * time and the members list. + * + * @param {object} fields The form fields. + * @param {string} projectId The project. + * @return {object} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.2 + */ +export function logEntryPayload(fields, projectId) { + const type = LOG_TYPES.includes(fields?.type) ? fields.type : 'issue' + const payload = { + title: String(fields?.title ?? '').trim(), + project: projectId, + type, + date: String(fields?.date ?? ''), + body: String(fields?.body ?? ''), + status: fields?.status === 'closed' ? 'closed' : 'open', + attendees: type === 'meeting' ? [...new Set((fields?.attendees || []).filter(Boolean))] : [], + actions: [...(fields?.actions || [])], + } + return payload +} + +/** + * The task an "Add action" creates: an ordinary task in the same project with + * `issueType: 'action'`, at the bottom of the board's first column so it + * appears on the board. With no columns it lands in the backlog. + * + * @param {object} fields `title`, and optionally `assignedTo` and `dueDate`. + * @param {string} projectId The project. + * @param {Array} columns The project's board columns. + * @param {Array} tasks The project's tasks. + * @return {object} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-2.3 + */ +export function newActionTask(fields, projectId, columns = [], tasks = []) { + const task = { + title: String(fields?.title ?? '').trim(), + status: 'open', + priority: 'normal', + issueType: ACTION_ISSUE_TYPE, + project: projectId, + } + if (fields?.assignedTo) { + task.assignedTo = fields.assignedTo + } + if (fields?.dueDate) { + task.dueDate = fields.dueDate + } + const first = sortColumns(columns)[0] + if (!first) { + return { ...task, column: null } + } + const lane = (tasks || []).filter((other) => other?.column === idOf(first)) + return { ...task, ...buildMovePatch(first, lane) } +} diff --git a/src/utils/riskHelpers.js b/src/utils/riskHelpers.js new file mode 100644 index 00000000..89dea859 --- /dev/null +++ b/src/utils/riskHelpers.js @@ -0,0 +1,167 @@ +/** + * Pure helpers for the risk register: the admin's risk scale, the bands, the + * heat map and the payload a risk is saved with (projects-overview-logs-risks). + * + * SPDX-License-Identifier: EUPL-1.2 + * SPDX-FileCopyrightText: 2026 Conduction B.V. + */ + +/** The scale an install starts with; the same as RiskScaleService::DEFAULT_SCALE. */ +export const DEFAULT_RISK_SCALE = Object.freeze({ + levels: 5, + likelihood: ['Very low', 'Low', 'Medium', 'High', 'Very high'], + impact: ['Very low', 'Low', 'Medium', 'High', 'Very high'], + thresholds: { medium: 5, high: 12 }, +}) + +/** The statuses under which a risk still needs watching. */ +const OPEN_STATUSES = ['open', 'mitigating'] + +/** + * Whether a parsed value is a usable scale. + * + * @param {object} scale The candidate. + * @return {boolean} + */ +function isScale(scale) { + const levels = scale?.levels + return Number.isInteger(levels) && levels >= 3 && levels <= 5 + && Array.isArray(scale.likelihood) && scale.likelihood.length === levels + && Array.isArray(scale.impact) && scale.impact.length === levels + && Number.isInteger(scale.thresholds?.medium) && Number.isInteger(scale.thresholds?.high) +} + +/** + * The scale from the stored setting, or the default when it is missing or broken. + * + * @param {string|object} value The `risk_scale` setting. + * @return {object} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.4 + */ +export function parseRiskScale(value) { + let scale = value + if (typeof value === 'string') { + try { + scale = JSON.parse(value) + } catch { + scale = null + } + } + return isScale(scale) ? scale : DEFAULT_RISK_SCALE +} + +/** + * Thresholds that suit a number of levels, offered when the admin changes it. + * + * @param {number} levels 3, 4 or 5. + * @return {{medium: number, high: number}} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.4 + */ +export function defaultThresholds(levels) { + return { 3: { medium: 3, high: 6 }, 4: { medium: 4, high: 9 } }[levels] || { medium: 5, high: 12 } +} + +/** + * The band of a score: low, medium or high. + * + * @param {number} score likelihood times impact. + * @param {object} scale The risk scale. + * @return {'low'|'medium'|'high'} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.2 + */ +export function riskBand(score, scale = DEFAULT_RISK_SCALE) { + const value = Number(score) || 0 + if (value >= scale.thresholds.high) { + return 'high' + } + return value >= scale.thresholds.medium ? 'medium' : 'low' +} + +/** + * The heat map: one row per impact level, highest first, and one cell per + * likelihood level, each with its count, score and band. + * + * @param {Array} risks The risks. + * @param {object} scale The risk scale. + * @return {Array<{impact: number, label: string, cells: Array}>} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.2 + */ +export function heatMapRows(risks = [], scale = DEFAULT_RISK_SCALE) { + const rows = [] + for (let impact = scale.levels; impact >= 1; impact--) { + const cells = [] + for (let likelihood = 1; likelihood <= scale.levels; likelihood++) { + const score = likelihood * impact + cells.push({ + likelihood, + label: scale.likelihood[likelihood - 1], + score, + band: riskBand(score, scale), + count: (risks || []).filter((risk) => Number(risk?.likelihood) === likelihood && Number(risk?.impact) === impact).length, + }) + } + rows.push({ impact, label: scale.impact[impact - 1], cells }) + } + return rows +} + +/** + * Risks by score, highest first; ties by title. + * + * @param {Array} risks The risks. + * @return {Array} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.2 + */ +export function sortRisks(risks = []) { + return [...(risks || [])].sort((a, b) => (Number(b?.score) || 0) - (Number(a?.score) || 0) + || String(a?.title ?? '').localeCompare(String(b?.title ?? ''))) +} + +/** + * The highest-scored risks that are still open or being mitigated. + * + * @param {Array} risks The risks. + * @param {number} [limit] How many. + * @return {Array} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-1.2 + */ +export function topOpenRisks(risks = [], limit = 3) { + return sortRisks((risks || []).filter((risk) => OPEN_STATUSES.includes(risk?.status ?? 'open'))).slice(0, limit) +} + +/** + * The payload of a new or edited risk. It carries no score: the server + * calculates it from likelihood and impact, and overwrites any a client sends. + * + * @param {object} fields The form fields. + * @param {string} projectId The project. + * @return {object} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-3.2 + */ +export function riskPayload(fields, projectId) { + const payload = { + title: String(fields?.title ?? '').trim(), + project: projectId, + likelihood: Number(fields?.likelihood), + impact: Number(fields?.impact), + status: ['open', 'mitigating', 'closed', 'occurred'].includes(fields?.status) ? fields.status : 'open', + response: ['avoid', 'reduce', 'transfer', 'accept'].includes(fields?.response) ? fields.response : 'reduce', + description: String(fields?.description ?? ''), + category: String(fields?.category ?? ''), + countermeasures: String(fields?.countermeasures ?? ''), + } + if (fields?.owner) { + payload.owner = fields.owner + } + if (fields?.reviewDate) { + payload.reviewDate = fields.reviewDate + } + return payload +} diff --git a/src/utils/userNames.js b/src/utils/userNames.js new file mode 100644 index 00000000..4de6f693 --- /dev/null +++ b/src/utils/userNames.js @@ -0,0 +1,53 @@ +/** + * Display names of Nextcloud users, for pages that list people by name. + * + * Reads Nextcloud's own autocomplete endpoint, which every signed-in user may + * call, and falls back to the user id when a name does not resolve. + * + * SPDX-License-Identifier: EUPL-1.2 + * SPDX-FileCopyrightText: 2026 Conduction B.V. + */ +import { generateOcsUrl } from '@nextcloud/router' + +const cache = new Map() + +/** + * The display name of one user. + * + * @param {string} uid The user id. + * @return {Promise} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-1.2 + */ +export async function displayName(uid) { + if (!uid) { + return '' + } + if (!cache.has(uid)) { + cache.set(uid, (async () => { + try { + const url = generateOcsUrl('/core/autocomplete/get') + '?' + new URLSearchParams({ search: uid, itemType: '', itemId: '', 'shareTypes[]': '0', limit: '10', format: 'json' }) + const response = await fetch(url, { headers: { 'OCS-APIRequest': 'true', Accept: 'application/json' } }) + const body = response.ok ? await response.json() : null + const match = (body?.ocs?.data || []).find((user) => user?.id === uid) + return match?.label || uid + } catch { + return uid + } + })()) + } + return cache.get(uid) +} + +/** + * Display names for a list of users, keyed by user id. + * + * @param {Array} uids The user ids. + * @return {Promise} + * + * @spec openspec/changes/projects-overview-logs-risks/tasks.md#task-1.2 + */ +export async function displayNames(uids = []) { + const names = await Promise.all((uids || []).map((uid) => displayName(uid))) + return Object.fromEntries((uids || []).map((uid, i) => [uid, names[i]])) +} diff --git a/src/views/ProjectBacklog.vue b/src/views/ProjectBacklog.vue index ded1f706..e358a8fb 100644 --- a/src/views/ProjectBacklog.vue +++ b/src/views/ProjectBacklog.vue @@ -15,6 +15,8 @@ {{ t('planninq', 'Backlog') }} + +

{{ t('planninq', 'Backlog') }}

@@ -163,6 +165,7 @@ import ArrowRightIcon from 'vue-material-design-icons/ArrowRight.vue' import ArrowUpIcon from 'vue-material-design-icons/ArrowUp.vue' import DragIcon from 'vue-material-design-icons/Drag.vue' import FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue' +import ProjectTabs from '../components/ProjectTabs.vue' import { useProjectsStore } from '../store/projects.js' import { BACKLOG_SORTS, @@ -192,6 +195,7 @@ export default { NcLoadingIcon, NcSelect, NcTextField, + ProjectTabs, ArrowDownIcon, ArrowRightIcon, ArrowUpIcon, diff --git a/src/views/ProjectBoard.vue b/src/views/ProjectBoard.vue index a8c2258c..fd9d90c0 100644 --- a/src/views/ProjectBoard.vue +++ b/src/views/ProjectBoard.vue @@ -59,18 +59,6 @@ {{ t('planninq', 'List') }}
- - {{ t('planninq', 'Backlog') }} - - - {{ t('planninq', 'Timeline') }} - + + @@ -331,6 +321,7 @@ import LockOutline from 'vue-material-design-icons/LockOutline.vue' import PlusIcon from 'vue-material-design-icons/Plus.vue' import ColumnActions from '../components/ColumnActions.vue' import ProjectSettingsSidebar from '../components/ProjectSettingsSidebar.vue' +import ProjectTabs from '../components/ProjectTabs.vue' import TaskCard from '../components/TaskCard.vue' import ColumnEditDialog from '../dialogs/ColumnEditDialog.vue' import ColumnRemoveDialog from '../dialogs/ColumnRemoveDialog.vue' @@ -367,6 +358,7 @@ export default { ColumnRemoveDialog, LockOutline, PlusIcon, + ProjectTabs, TaskCard, }, diff --git a/src/views/ProjectLog.vue b/src/views/ProjectLog.vue new file mode 100644 index 00000000..debea53e --- /dev/null +++ b/src/views/ProjectLog.vue @@ -0,0 +1,519 @@ + + + + + diff --git a/src/views/ProjectOverview.vue b/src/views/ProjectOverview.vue new file mode 100644 index 00000000..3b73c607 --- /dev/null +++ b/src/views/ProjectOverview.vue @@ -0,0 +1,414 @@ + + + + + diff --git a/src/views/ProjectRisks.vue b/src/views/ProjectRisks.vue new file mode 100644 index 00000000..695fdbce --- /dev/null +++ b/src/views/ProjectRisks.vue @@ -0,0 +1,353 @@ + + + + + diff --git a/src/views/ProjectTimeline.vue b/src/views/ProjectTimeline.vue index 4b976668..4502ab46 100644 --- a/src/views/ProjectTimeline.vue +++ b/src/views/ProjectTimeline.vue @@ -14,6 +14,8 @@ {{ t('planninq', 'Timeline') }} + +

@@ -153,6 +155,7 @@ import { NcButton, NcEmptyContent, NcLoadingIcon, NcSelect } from '@nextcloud/vu import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue' import ArrowLeft from 'vue-material-design-icons/ArrowLeft.vue' import ChartTimeline from 'vue-material-design-icons/ChartTimeline.vue' +import ProjectTabs from '../components/ProjectTabs.vue' import { fetchProjectTimeline } from '../api/timeline.js' import { useProjectsStore } from '../store/projects.js' import { @@ -174,6 +177,7 @@ export default { ArrowLeft, AlertCircleOutline, ChartTimeline, + ProjectTabs, }, data() { diff --git a/src/views/settings/Settings.vue b/src/views/settings/Settings.vue index 950fbac9..621b842a 100644 --- a/src/views/settings/Settings.vue +++ b/src/views/settings/Settings.vue @@ -148,6 +148,73 @@ + + +
+
+ + +
+
+ {{ axis === 'likelihood' ? t('planninq', 'Likelihood labels') : t('planninq', 'Impact labels') }} +
+ + +
+
+
+ + +
+
+ + +
+
+ {{ riskScaleSuccess }} +
+ + + {{ savingRiskScale ? t('planninq', 'Saving…') : t('planninq', 'Save') }} + +
+
+ diff --git a/src/components/ProjectTabs.vue b/src/components/ProjectTabs.vue index 5bc56406..b90bc468 100644 --- a/src/components/ProjectTabs.vue +++ b/src/components/ProjectTabs.vue @@ -51,6 +51,7 @@ export default { backlog: this.t('planninq', 'Backlog'), timeline: this.t('planninq', 'Timeline'), risks: this.t('planninq', 'Risks'), + status: this.t('planninq', 'Status'), log: this.t('planninq', 'Log'), } return PROJECT_TABS.map((tab) => ({ ...tab, label: labels[tab.id] })) diff --git a/src/dialogs/StatusReportDialog.vue b/src/dialogs/StatusReportDialog.vue new file mode 100644 index 00000000..4926e846 --- /dev/null +++ b/src/dialogs/StatusReportDialog.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/src/manifest.json b/src/manifest.json index aebbb5a7..c3c20da8 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -151,6 +151,7 @@ { "id": "ProjectTimeline", "_note": "Gantt over a project's tasks: positioned bars plus dependency arrows drawn between them, fed by /api/timeline. The roadmap type renders milestones on a track and has no vocabulary for task bars with dependency edges.", "route": "/projects/:id/timeline", "type": "custom", "title": "Timeline", "component": "ProjectTimeline", "config": {} }, { "id": "ProjectOverview", "_note": "One screen per project: description, dates, people, progress from the project's tasks, and its latest log entries, each block linking to its tab. It reads three schemas for one project and computes progress, which no detail page expresses.", "route": "/projects/:id/overview", "type": "custom", "title": "Overview", "component": "ProjectOverview", "config": {} }, { "id": "ProjectRisks", "_note": "The project's risk register: a heat map of likelihood against impact on the admin's risk scale beside the list sorted by score. The heat map is not an index, so the page is custom.", "route": "/projects/:id/risks", "type": "custom", "title": "Risks", "component": "ProjectRisks", "config": {} }, + { "id": "ProjectStatus", "_note": "Status reports on the six GROTIK aspects: the latest report with its notes, the history, and a report form that suggests money, time and risk from the project's tasks, risks and budget. The suggestions read three schemas, which no detail page expresses.", "route": "/projects/:id/status", "type": "custom", "title": "Status", "component": "ProjectStatus", "config": {} }, { "id": "ProjectLog", "_note": "The project log: issues, lessons, meetings and decisions with type filters, plus an Actions filter over the project's tasks with issueType action, and an Add action that creates such a task. The mix of two schemas under one filter row is what makes it custom.", "route": "/projects/:id/log", "type": "custom", "title": "Log", "component": "ProjectLog", "config": {} }, { "id": "TaskDetail", "_note": "Task detail that combines a CnObjectSidebar with an inline time-entry log written against the same task. A detail page renders the record; the embedded logging surface is what makes this custom.", "route": "/projects/:id/tasks/:taskId", "type": "custom", "title": "Task", "component": "TaskDetail", "config": {} }, { "id": "Timesheet", "_note": "Week timesheet: entries grouped per day with inline edit and a project selector. A grouped, editable grid over time entries, which no v2 list primitive expresses.", "route": "/timesheet", "type": "custom", "title": "Timesheet", "component": "Timesheet", "config": {} }, diff --git a/src/registry.js b/src/registry.js index a135adc4..8aab2b77 100644 --- a/src/registry.js +++ b/src/registry.js @@ -28,6 +28,7 @@ import ProjectList from './views/ProjectList.vue' import ProjectLog from './views/ProjectLog.vue' import ProjectOverview from './views/ProjectOverview.vue' import ProjectRisks from './views/ProjectRisks.vue' +import ProjectStatus from './views/ProjectStatus.vue' import ProjectTimeline from './views/ProjectTimeline.vue' import TaskDetail from './views/TaskDetail.vue' import Timesheet from './views/Timesheet.vue' @@ -51,6 +52,7 @@ export default { ProjectLog: page(ProjectLog), ProjectOverview: page(ProjectOverview), ProjectRisks: page(ProjectRisks), + ProjectStatus: page(ProjectStatus), ProjectTimeline: page(ProjectTimeline), TaskDetail: page(TaskDetail), Timesheet: page(Timesheet), diff --git a/src/store/projects.js b/src/store/projects.js index 315aaf19..b35500b3 100644 --- a/src/store/projects.js +++ b/src/store/projects.js @@ -30,6 +30,7 @@ const TIME_ENTRY_SCHEMA = 'plannedTimeEntry' const LABEL_SCHEMA = 'label' const LOG_SCHEMA = 'projectLogEntry' const RISK_SCHEMA = 'risk' +const STATUS_REPORT_SCHEMA = 'projectStatusReport' /** * Largest page OpenRegister will return. Asking for more is silently capped. @@ -617,6 +618,52 @@ export const useProjectsStore = defineStore('projects', { } }, + /** + * Every status report of a project, scoped by OpenRegister to its members. + * + * @param {string} projectId Parent project UUID + * @return {Promise} The reports (empty array on error) + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ + async fetchStatusReports(projectId) { + try { + const objectStore = this._objectStore() + const reports = await fetchEvery(objectStore, STATUS_REPORT_SCHEMA, { project: projectId }) + return Array.isArray(reports) ? reports : [] + } catch (err) { + console.error('fetchStatusReports error:', err) + return [] + } + }, + + /** + * Write a new status report. The server calculates the overall status, + * refuses anyone but the project owner, and copies the newest report + * onto the project. + * + * @param {object} report The report payload + * @return {Promise} The saved report, or null on failure + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ + async saveStatusReport(report) { + try { + const response = await fetch(generateUrl(`/apps/openregister/api/objects/planninq/${STATUS_REPORT_SCHEMA}`), { + method: 'POST', + headers: buildHeaders(), + body: JSON.stringify(report), + }) + if (!response.ok) { + return null + } + return await response.json() + } catch (err) { + console.error('saveStatusReport error:', err) + return null + } + }, + // ── 2.7 archiveProject ──────────────────────────────────────────── /** diff --git a/src/utils/projectOverview.js b/src/utils/projectOverview.js index 43c840c5..d4dde561 100644 --- a/src/utils/projectOverview.js +++ b/src/utils/projectOverview.js @@ -20,6 +20,7 @@ export const PROJECT_TABS = [ { id: 'backlog', route: 'ProjectBacklog' }, { id: 'timeline', route: 'ProjectTimeline' }, { id: 'risks', route: 'ProjectRisks' }, + { id: 'status', route: 'ProjectStatus' }, { id: 'log', route: 'ProjectLog' }, ] diff --git a/src/utils/statusReports.js b/src/utils/statusReports.js new file mode 100644 index 00000000..45b694aa --- /dev/null +++ b/src/utils/statusReports.js @@ -0,0 +1,187 @@ +/** + * Pure helpers for project status reports (portfolio-status-overview): the + * six aspects, the worst-status rule, the suggestions for money, time and + * risk, the report order and the payload the Status tab writes. + * + * SPDX-License-Identifier: EUPL-1.2 + * SPDX-FileCopyrightText: 2026 Conduction B.V. + */ +import { riskBand, sortRisks } from './riskHelpers.js' + +/** The six aspects in the order the form and the tables show them. */ +export const ASPECTS = ['money', 'organisation', 'time', 'information', 'quality', 'risk'] + +/** The statuses from best to worst. */ +export const STATUSES = ['onTrack', 'atRisk', 'offTrack'] + +/** Money is at risk once cost passes this share of the budget, off track past the budget. */ +export const MONEY_AT_RISK_RATIO = 0.9 + +/** Task statuses that no longer count as open work. */ +const CLOSED_TASK_STATUSES = ['done', 'cancelled'] + +/** Risk statuses that still count as open. */ +const OPEN_RISK_STATUSES = ['open', 'mitigating'] + +/** Risk band to the status it suggests. */ +const BAND_STATUS = { low: 'onTrack', medium: 'atRisk', high: 'offTrack' } + +/** + * `money` to `Money`, the suffix of `statusMoney`, `noteMoney` and `healthMoney`. + * + * @param {string} aspect The aspect. + * @return {string} + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +export function aspectKey(aspect) { + return aspect.charAt(0).toUpperCase() + aspect.slice(1) +} + +/** + * The worst status in a list, or null when it holds none. + * + * @param {Array} statuses The statuses. + * @return {string|null} + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +export function worstStatus(statuses = []) { + let worst = -1 + for (const status of statuses || []) { + worst = Math.max(worst, STATUSES.indexOf(status)) + } + return worst >= 0 ? STATUSES[worst] : null +} + +/** + * @param {string|undefined} value A date or date-time. + * @return {string} The YYYY-MM-DD part, or ''. + */ +function day(value) { + return typeof value === 'string' ? value.slice(0, 10) : '' +} + +/** + * Today as YYYY-MM-DD in the viewer's time zone. + * + * @return {string} + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +export function localToday() { + const now = new Date() + const pad = (n) => String(n).padStart(2, '0') + return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}` +} + +/** + * The time suggestion: off track when the end date has passed with open + * tasks, at risk when an open task is past its due date, else on track. + * + * @param {Array} tasks The project's tasks. + * @param {object|null} project The project, for its end date. + * @param {string} today YYYY-MM-DD. + * @return {{status: string, code: string, count: number}} + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +export function suggestTime(tasks = [], project = null, today = localToday()) { + const open = (tasks || []).filter((task) => task && !CLOSED_TASK_STATUSES.includes(task.status)) + const end = day(project?.endDate) + if (end && end < today && open.length > 0) { + return { status: 'offTrack', code: 'endPassed', count: open.length } + } + const late = open.filter((task) => day(task.dueDate) && day(task.dueDate) < today).length + if (late > 0) { + return { status: 'atRisk', code: 'late', count: late } + } + return { status: 'onTrack', code: 'onSchedule', count: 0 } +} + +/** + * The risk suggestion: the band of the highest-scored open risk. + * + * @param {Array} risks The project's risks. + * @param {object} scale The risk scale. + * @return {{status: string, code: string, score?: number, title?: string}} + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +export function suggestRisk(risks = [], scale) { + const open = sortRisks((risks || []).filter((risk) => OPEN_RISK_STATUSES.includes(risk?.status ?? 'open'))) + if (open.length === 0) { + return { status: 'onTrack', code: 'noOpenRisks' } + } + const top = open[0] + const score = Number(top.score) || 0 + return { status: BAND_STATUS[riskBand(score, scale)], code: 'highestRisk', score, title: String(top.title ?? '') } +} + +/** + * The money suggestion from the budget and the cost so far. Without a budget + * or without recorded costs there is no suggestion. + * + * @param {{budget: number, cost: number|null}} figures Budget and cost in the same currency. + * @return {{status: string|null, code: string, percent?: number}} + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +export function suggestMoney({ budget, cost } = {}) { + const planned = Number(budget) || 0 + if (planned <= 0) { + return { status: null, code: 'noBudget' } + } + if (cost === null || cost === undefined) { + return { status: null, code: 'noCosts' } + } + const ratio = (Number(cost) || 0) / planned + const percent = Math.round(ratio * 100) + if (ratio > 1) { + return { status: 'offTrack', code: 'overBudget', percent } + } + if (ratio > MONEY_AT_RISK_RATIO) { + return { status: 'atRisk', code: 'nearBudget', percent } + } + return { status: 'onTrack', code: 'withinBudget', percent } +} + +/** + * Reports newest first: by report date, then by the time they were saved. + * + * @param {Array} reports The reports. + * @return {Array} + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +export function sortReports(reports = []) { + return [...(reports || [])].sort((a, b) => day(b?.reportDate).localeCompare(day(a?.reportDate)) + || String(b?.['@self']?.created ?? '').localeCompare(String(a?.['@self']?.created ?? ''))) +} + +/** + * The payload of a new report. It carries no overall status: the server + * calculates the worst of the six and overwrites any a client sends. An + * aspect without a chosen status is left out, so the schema refuses it. + * + * @param {object} fields `reportDate` and, per aspect, `{status, note}`. + * @param {string} projectId The project. + * @return {object} + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +export function reportPayload(fields, projectId) { + const payload = { project: projectId, reportDate: day(fields?.reportDate) || localToday() } + for (const aspect of ASPECTS) { + const key = aspectKey(aspect) + const status = fields?.[aspect]?.status + if (STATUSES.includes(status)) { + payload[`status${key}`] = status + } + const note = String(fields?.[aspect]?.note ?? '').trim() + if (note) { + payload[`note${key}`] = note + } + } + return payload +} diff --git a/src/views/ProjectStatus.vue b/src/views/ProjectStatus.vue new file mode 100644 index 00000000..0fd62d1b --- /dev/null +++ b/src/views/ProjectStatus.vue @@ -0,0 +1,379 @@ + + + + + diff --git a/tests/e2e/project-status.spec.ts b/tests/e2e/project-status.spec.ts new file mode 100644 index 00000000..a8d44968 --- /dev/null +++ b/tests/e2e/project-status.spec.ts @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * E2E coverage for project status reports (portfolio-status-overview). + * + * @e2e project-status-report::writing-a-status-report + * @e2e project-status-report::a-time-suggestion-from-late-tasks + * + * "The server decides the overall status" carries an `@e2e exclude` in the + * spec: OpenRegister's calculation overwrites the overall on save, asserted by + * PlanninqRegisterSchemaTest::testProjectStatusReportSchemaCalculatesTheOverallStatus. + * + * The suite runs as the admin, who may write reports on any project. + */ + +import { expect, test } from '@playwright/test' +import { openFixtureProjectBoard } from './nav.ts' + +async function openStatus(page) { + const projectId = await openFixtureProjectBoard(page) + await page.getByTestId('project-tab-status').click() + await expect(page).toHaveURL(new RegExp(`/projects/${projectId}/status`)) + return projectId +} + +const ASPECTS = ['money', 'organisation', 'time', 'information', 'quality', 'risk'] + +test.describe('Project status reports', () => { + test('writing a status report', async ({ page }) => { + await openStatus(page) + const before = await page.getByTestId('status-history-row').count() + + await page.getByTestId('status-report-new').click() + for (const aspect of ASPECTS) { + await page.getByTestId(`status-${aspect}-${aspect === 'time' ? 'atRisk' : 'onTrack'}`).click() + } + await page.getByTestId('status-note-time').locator('input').fill('Permit delayed by 3 weeks') + await page.getByTestId('status-report-save').click() + + const latest = page.getByTestId('status-latest') + await expect(latest.getByTestId('status-latest-overall')).toHaveAttribute('data-status', 'atRisk') + await expect(page.getByTestId('status-latest-time')).toContainText('Permit delayed by 3 weeks') + await expect(page.getByTestId('status-history-row')).toHaveCount(before + 1) + }) + + test('a time suggestion from late tasks', async ({ page }) => { + await openStatus(page) + await page.getByTestId('status-report-new').click() + + const suggestion = page.getByTestId('status-suggestion-time') + await expect(suggestion).toBeVisible() + for (const status of ['onTrack', 'atRisk', 'offTrack']) { + await expect(page.getByTestId(`status-time-${status}`).locator('input')).not.toBeChecked() + } + }) +}) diff --git a/tests/e2e/visual/views.spec.ts b/tests/e2e/visual/views.spec.ts index c23e09a5..14484032 100644 --- a/tests/e2e/visual/views.spec.ts +++ b/tests/e2e/visual/views.spec.ts @@ -157,6 +157,7 @@ test.describe('visual baselines — planninq views', () => { ['ProjectTimeline', 'timeline', 'timeline', 'project-timeline.png'], ['ProjectOverview', 'overview', 'overview', 'project-overview.png'], ['ProjectRisks', 'risks', 'risks', 'project-risks.png'], + ['ProjectStatus', 'status', 'status', 'project-status.png'], ['ProjectLog', 'log', 'log', 'project-log.png'], ]) { test(`${component} renders from its project tab @visual`, async ({ page }) => { diff --git a/tests/stubs/openregister/Service/SystemOperationContext.php b/tests/stubs/openregister/Service/SystemOperationContext.php new file mode 100644 index 00000000..756d7b59 --- /dev/null +++ b/tests/stubs/openregister/Service/SystemOperationContext.php @@ -0,0 +1,63 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\OpenRegister\Service; + +/** + * Ambient marker for a trusted system operation. + */ +final class SystemOperationContext { + + /** + * Nesting depth of active scopes. + * + * @var integer + */ + private static int $depth = 0; + + /** + * Run a callable inside the system-operation scope. + * + * @param callable $operation The operation. + * + * @return mixed Whatever the callable returns. + */ + public static function run(callable $operation) { + self::$depth++; + + try { + return $operation(); + } finally { + self::$depth--; + } + }//end run() + + /** + * Whether a system operation is running. + * + * @return bool + */ + public static function isActive(): bool { + return self::$depth > 0; + }//end isActive() +}//end class diff --git a/tests/unit/AppInfo/BoardColumnWiringTest.php b/tests/unit/AppInfo/BoardColumnWiringTest.php index 6a6e6dbc..2a757c07 100644 --- a/tests/unit/AppInfo/BoardColumnWiringTest.php +++ b/tests/unit/AppInfo/BoardColumnWiringTest.php @@ -75,5 +75,38 @@ public function testBootSubscribesBothBoardColumnListeners(): void { $boot = (string)file_get_contents(__DIR__ . '/../../../lib/AppInfo/Application.php'); self::assertStringContainsString('$this->registerBoardColumnListeners(dispatcher: $dispatcher);', $boot, 'boot() calls the registration'); }//end testBootSubscribesBothBoardColumnListeners() + /** + * Every project-scoped schema is stamped and gated live, not only in the listener's own list. + * + * risk and projectLogEntry were in ProjectMembershipService::SCOPED_SCHEMAS + * but not in this subscription, so on a live instance their members list + * stayed empty and members could not read what they wrote. + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 + */ + public function testBootSubscribesTheMembershipAndStatusListenersForEveryScopedSchema(): void { + ObjectEventSubscription::$calls = []; + $app = (new \ReflectionClass(Application::class))->newInstanceWithoutConstructor(); + (new \ReflectionMethod(Application::class, 'registerMembershipListeners'))->invoke($app, $this->createMock(originalClassName: IEventDispatcher::class)); + + $byListener = []; + foreach (ObjectEventSubscription::$calls as $call) { + self::assertTrue(class_exists($call['listener']), $call['listener'] . ' must exist'); + $byListener[$call['listener']][] = [substr($call['event'], strrpos($call['event'], '\\') + 1), $call['schemas']]; + } + + $scoped = \OCA\Planninq\Service\ProjectMembershipService::SCOPED_SCHEMAS; + self::assertSame( + [['ObjectCreatingEvent', $scoped], ['ObjectUpdatingEvent', $scoped]], + $byListener['OCA\\Planninq\\Listener\\ProjectMemberAccessListener'] + ); + self::assertContains('projectStatusReport', $scoped); + + $status = ['project', 'projectStatusReport']; + self::assertSame( + [['ObjectCreatingEvent', $status], ['ObjectUpdatingEvent', $status], ['ObjectDeletingEvent', $status]], + $byListener['OCA\\Planninq\\Listener\\ProjectStatusListener'] + ); + }//end testBootSubscribesTheMembershipAndStatusListenersForEveryScopedSchema() }//end class } diff --git a/tests/unit/Listener/ProjectStatusListenerTest.php b/tests/unit/Listener/ProjectStatusListenerTest.php new file mode 100644 index 00000000..f67226d2 --- /dev/null +++ b/tests/unit/Listener/ProjectStatusListenerTest.php @@ -0,0 +1,270 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\Planninq\Tests\Unit\Listener; + +require_once __DIR__ . '/../Support/ObjectServiceDouble.php'; +require_once __DIR__ . '/../Support/InMemoryObjectService.php'; +require_once __DIR__ . '/../Support/MembershipFixture.php'; +require_once __DIR__ . '/../Support/RegisterSchemaValidation.php'; + +use OCA\OpenRegister\Db\ObjectEntity; +use OCA\OpenRegister\Event\ObjectCreatingEvent; +use OCA\OpenRegister\Event\ObjectDeletingEvent; +use OCA\OpenRegister\Event\ObjectUpdatingEvent; +use OCA\OpenRegister\Service\SystemOperationContext; +use OCA\Planninq\Listener\ProjectStatusListener; +use OCA\Planninq\Service\ProjectHealthService; +use OCA\Planninq\Tests\Unit\Support\InMemoryObjectService; +use OCA\Planninq\Tests\Unit\Support\MembershipFixture; +use OCA\Planninq\Tests\Unit\Support\RegisterSchemaValidation; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserSession; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class ProjectStatusListenerTest extends TestCase { + use MembershipFixture; + use RegisterSchemaValidation; + + private const PROJECT = ['title' => 'Stadspark', 'status' => 'active', 'owner' => 'carol', 'members' => ['bob']]; + + /** + * Whether each project save ran in the system-operation scope. + * + * @var array + */ + private array $savedAsSystem = []; + + protected function setUp(): void { + parent::setUp(); + $saved = &$this->savedAsSystem; + $this->objects = new class ($saved) extends InMemoryObjectService { + /** @var array */ + private array $asSystem; + + public function __construct(array &$asSystem) { + $this->asSystem = &$asSystem; + } + + public function saveObject( + array|object $object, + ?array $extend = [], + mixed $register = null, + mixed $schema = null, + ?string $uuid = null, + bool $_rbac = true, + bool $_multitenancy = true, + bool $silent = false, + bool $_validation = true, + ): ?object { + $this->asSystem[] = SystemOperationContext::isActive(); + return parent::saveObject($object, $extend, $register, $schema, $uuid, $_rbac, $_multitenancy, $silent, $_validation); + } + }; + $this->objects->seed('project', '5b0c7d8e-1f2a-4b3c-9d4e-5f6a7b8c9d0e', self::PROJECT); + }//end setUp() + + private function listener(string $actor, bool $isAdmin = false): ProjectStatusListener { + $user = $this->createMock(originalClassName: IUser::class); + $user->method('getUID')->willReturn($actor); + $session = $this->createMock(originalClassName: IUserSession::class); + $session->method('getUser')->willReturn($actor === '' ? null : $user); + $groups = $this->createMock(originalClassName: IGroupManager::class); + $groups->method('isAdmin')->willReturnCallback(static fn (string $uid): bool => ($isAdmin === true && $uid === $actor)); + + $membership = $this->membershipService(); + $logger = $this->createMock(originalClassName: LoggerInterface::class); + + return new ProjectStatusListener( + health: new ProjectHealthService(membership: $membership, container: $this->container(), logger: $logger), + membership: $membership, + scopeResolver: $this->scopeResolver(), + userSession: $session, + groupManager: $groups, + logger: $logger + ); + }//end listener() + + private function report(string $uuid, array $data): ObjectEntity { + return InMemoryObjectService::entity(uuid: $uuid, data: $data, register: '1', schema: $this->schemaId(slug: 'projectStatusReport')); + }//end report() + + private function project(array $data): ObjectEntity { + return InMemoryObjectService::entity(uuid: '5b0c7d8e-1f2a-4b3c-9d4e-5f6a7b8c9d0e', data: $data, register: '1', schema: $this->schemaId(slug: 'project')); + }//end project() + + /** + * A report with every aspect on track except the ones given. + * + * @param string $date The report date. + * @param array $statuses Statuses to set, keyed `status`. + * + * @return array + */ + private static function reportData(string $date, array $statuses = []): array { + $data = ['project' => '5b0c7d8e-1f2a-4b3c-9d4e-5f6a7b8c9d0e', 'reportDate' => $date]; + foreach (ProjectHealthService::ASPECTS as $aspect) { + $data['status' . $aspect] = 'onTrack'; + } + + return array_merge($data, $statuses); + }//end reportData() + + private function storedProject(): array { + return (array)$this->objects->find(id: '5b0c7d8e-1f2a-4b3c-9d4e-5f6a7b8c9d0e', schema: 'project')->getObject(); + }//end storedProject() + + /** + * Task 1.2: a new report is copied onto its project, as the system, in a payload the project schema accepts. + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 + */ + public function testANewerReportIsCopiedOntoTheProject(): void { + $this->objects->seed('projectStatusReport', 'rep-1', self::reportData(date: '2026-09-01')); + $data = self::reportData(date: '2026-09-28', statuses: ['statusTime' => 'atRisk', 'noteTime' => 'Permit delayed by 3 weeks']); + $event = new ObjectCreatingEvent($this->report(uuid: 'rep-2', data: $data)); + + $this->listener(actor: 'carol')->handle($event); + + self::assertFalse($event->isPropagationStopped()); + $project = $this->storedProject(); + self::assertSame('atRisk', $project['healthTime']); + self::assertSame('onTrack', $project['healthMoney']); + self::assertSame('atRisk', $project['healthOverall']); + self::assertSame('2026-09-28', $project['healthDate']); + self::assertSame('Stadspark', $project['title'], 'the rest of the project is kept'); + self::assertSame([true], $this->savedAsSystem, 'one write, inside the system-operation scope'); + self::assertSame([], $this->registerSchemaErrors(slug: 'project', payload: $project)); + self::assertSame([], $this->registerSchemaErrors(slug: 'projectStatusReport', payload: $data)); + }//end testANewerReportIsCopiedOntoTheProject() + + /** + * Task 1.2: a report dated before the newest one leaves the project alone. + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 + */ + public function testAnOlderReportDoesNotReplaceTheNewest(): void { + $newest = self::reportData(date: '2026-09-20', statuses: ['statusQuality' => 'offTrack']); + $this->objects->seed('projectStatusReport', 'rep-1', $newest); + $this->listener(actor: 'carol')->handle(new ObjectCreatingEvent($this->report(uuid: 'rep-1', data: $newest))); + self::assertSame('offTrack', $this->storedProject()['healthOverall']); + + $event = new ObjectCreatingEvent($this->report(uuid: 'rep-0', data: self::reportData(date: '2026-08-01'))); + $this->listener(actor: 'carol')->handle($event); + + self::assertFalse($event->isPropagationStopped(), 'an older report may still be written'); + self::assertSame('offTrack', $this->storedProject()['healthQuality']); + self::assertSame('2026-09-20', $this->storedProject()['healthDate']); + }//end testAnOlderReportDoesNotReplaceTheNewest() + + /** + * Task 1.2: deleting the newest report copies the one before it; deleting the last clears the health. + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 + */ + public function testDeletingTheNewestReportCopiesTheOneBefore(): void { + $older = self::reportData(date: '2026-09-01', statuses: ['statusMoney' => 'atRisk']); + $newest = self::reportData(date: '2026-09-28', statuses: ['statusRisk' => 'offTrack']); + $this->objects->seed('projectStatusReport', 'rep-1', $older); + $this->objects->seed('projectStatusReport', 'rep-2', $newest); + $this->objects->seed('project', '5b0c7d8e-1f2a-4b3c-9d4e-5f6a7b8c9d0e', self::PROJECT + ['healthRisk' => 'offTrack', 'healthOverall' => 'offTrack', 'healthDate' => '2026-09-28']); + + $this->listener(actor: 'carol')->handle(new ObjectDeletingEvent($this->report(uuid: 'rep-2', data: $newest))); + + $project = $this->storedProject(); + self::assertSame('atRisk', $project['healthMoney']); + self::assertSame('onTrack', $project['healthRisk']); + self::assertSame('atRisk', $project['healthOverall']); + self::assertSame('2026-09-01', $project['healthDate']); + + unset($this->objects->rows['projectStatusReport']['rep-2']); + $this->listener(actor: 'carol')->handle(new ObjectDeletingEvent($this->report(uuid: 'rep-1', data: $older))); + self::assertNull($this->storedProject()['healthOverall']); + self::assertSame([], $this->registerSchemaErrors(slug: 'project', payload: $this->storedProject())); + }//end testDeletingTheNewestReportCopiesTheOneBefore() + + /** + * Requirement "a project owner or manager writes the report": a member who is not the owner is refused, and nothing is copied. + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.1 + */ + public function testOnlyTheOwnerOrAnAdminWritesAReport(): void { + $event = new ObjectCreatingEvent($this->report(uuid: 'rep-1', data: self::reportData(date: '2026-09-28'))); + $this->listener(actor: 'bob')->handle($event); + + self::assertTrue($event->isPropagationStopped()); + self::assertSame(ProjectStatusListener::ERROR_CODE, $event->getErrors()['code']); + self::assertSame([], $this->savedAsSystem, 'a refused report is not copied'); + + $admin = new ObjectCreatingEvent($this->report(uuid: 'rep-1', data: self::reportData(date: '2026-09-28'))); + $this->listener(actor: 'root', isAdmin: true)->handle($admin); + self::assertFalse($admin->isPropagationStopped()); + }//end testOnlyTheOwnerOrAnAdminWritesAReport() + + /** + * A members-only rewrite by the membership sync is neither refused nor copied. + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 + */ + public function testAMembersOnlyRewritePasses(): void { + $data = self::reportData(date: '2026-09-28') + ['members' => ['bob', 'carol']]; + $event = new ObjectUpdatingEvent($this->report(uuid: 'rep-1', data: ['members' => ['carol']] + $data), $this->report(uuid: 'rep-1', data: $data)); + + $this->listener(actor: 'bob')->handle($event); + + self::assertFalse($event->isPropagationStopped()); + self::assertSame([], $this->savedAsSystem); + }//end testAMembersOnlyRewritePasses() + + /** + * Risk 1: no person writes the health fields; an update keeps the stored ones and a create gets none. + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 + */ + public function testAPersonCannotWriteTheHealthFields(): void { + $stored = self::PROJECT + ['healthMoney' => 'offTrack', 'healthOverall' => 'offTrack', 'healthDate' => '2026-09-28']; + $update = new ObjectUpdatingEvent( + $this->project(data: ['healthMoney' => 'onTrack', 'healthOverall' => 'onTrack'] + $stored), + $this->project(data: $stored) + ); + $this->listener(actor: 'carol')->handle($update); + + self::assertFalse($update->isPropagationStopped(), 'the settings form still saves'); + self::assertSame('offTrack', $update->getModifiedData()['healthMoney']); + self::assertSame('offTrack', $update->getModifiedData()['healthOverall']); + + $create = new ObjectCreatingEvent($this->project(data: self::PROJECT + ['healthOverall' => 'onTrack'])); + $this->listener(actor: 'carol')->handle($create); + self::assertNull($create->getModifiedData()['healthOverall']); + + $untouched = new ObjectUpdatingEvent($this->project(data: ['title' => 'Renamed'] + $stored), $this->project(data: $stored)); + $this->listener(actor: 'carol')->handle($untouched); + self::assertSame([], $untouched->getModifiedData(), 'an update that leaves health alone is not modified'); + + $system = new ObjectUpdatingEvent($this->project(data: ['healthMoney' => 'onTrack'] + $stored), $this->project(data: $stored)); + SystemOperationContext::run(fn () => $this->listener(actor: 'carol')->handle($system)); + self::assertSame([], $system->getModifiedData(), 'the system copy passes'); + }//end testAPersonCannotWriteTheHealthFields() +}//end class diff --git a/tests/unit/Settings/PlanninqRegisterSchemaTest.php b/tests/unit/Settings/PlanninqRegisterSchemaTest.php index f0f21709..92103a25 100644 --- a/tests/unit/Settings/PlanninqRegisterSchemaTest.php +++ b/tests/unit/Settings/PlanninqRegisterSchemaTest.php @@ -357,19 +357,21 @@ public function testDueSoonRecipientFieldExistsOnSchema(): void { }//end testDueSoonRecipientFieldExistsOnSchema() /** - * The register MUST declare exactly the ten expected schemas. + * The register MUST declare exactly the eleven expected schemas. * * Adds `projectPhase` to the previous exact set of six, when planninq took * over the project work breakdown structure pipelinq had built, and * `timetableSession` when planninq became the school timetable owner - * (school-timetable-target, decision D10). `example` must not be present. + * (school-timetable-target, decision D10), `projectLogEntry` and `risk` + * (projects-overview-logs-risks) and `projectStatusReport` + * (portfolio-status-overview). `example` must not be present. * * @return void * * @spec openspec/changes/task-dependencies/specs/register-schemas/spec.md */ - public function testRegisterDeclaresExactlyTenSchemas(): void { - $expected = ['task', 'project', 'projectPhase', 'column', 'plannedTimeEntry', 'label', 'dependency', 'timetableSession', 'projectLogEntry', 'risk']; + public function testRegisterDeclaresExactlyElevenSchemas(): void { + $expected = ['task', 'project', 'projectPhase', 'column', 'plannedTimeEntry', 'label', 'dependency', 'timetableSession', 'projectLogEntry', 'risk', 'projectStatusReport']; $listed = $this->register['components']['registers']['planninq']['schemas']; sort($listed); @@ -378,7 +380,7 @@ public function testRegisterDeclaresExactlyTenSchemas(): void { self::assertSame( expected: $sortedExpected, actual: $listed, - message: 'register schema list must be exactly the ten expected schemas' + message: 'register schema list must be exactly the eleven expected schemas' ); $defined = array_keys($this->register['components']['schemas']); @@ -386,7 +388,7 @@ public function testRegisterDeclaresExactlyTenSchemas(): void { self::assertSame( expected: $sortedExpected, actual: $defined, - message: 'components.schemas must define exactly the ten expected schemas' + message: 'components.schemas must define exactly the eleven expected schemas' ); self::assertArrayNotHasKey( @@ -395,7 +397,7 @@ public function testRegisterDeclaresExactlyTenSchemas(): void { message: 'placeholder example schema must not be present' ); - }//end testRegisterDeclaresExactlyTenSchemas() + }//end testRegisterDeclaresExactlyElevenSchemas() /** * The dependency schema MUST require blocker + blocked as UUID strings. @@ -573,7 +575,7 @@ public function testMockRegisterCarriesTimetableSessionDemoRows(): void { * @return void */ public function testProjectScopedSchemasCarryAHiddenMembersList(): void { - foreach (['task', 'column', 'projectPhase', 'plannedTimeEntry', 'projectLogEntry', 'risk'] as $slug) { + foreach (['task', 'column', 'projectPhase', 'plannedTimeEntry', 'projectLogEntry', 'risk', 'projectStatusReport'] as $slug) { $schema = $this->register['components']['schemas'][$slug]; $members = ($schema['properties']['members'] ?? null); @@ -596,7 +598,7 @@ public function testProjectScopedSchemasCarryAHiddenMembersList(): void { * @return void */ public function testProjectScopedSchemasMatchTheMembersList(): void { - foreach (['task', 'column', 'projectPhase', 'projectLogEntry', 'risk'] as $slug) { + foreach (['task', 'column', 'projectPhase', 'projectLogEntry', 'risk', 'projectStatusReport'] as $slug) { $authorization = $this->register['components']['schemas'][$slug]['authorization']; foreach (['read', 'update', 'delete'] as $action) { @@ -632,7 +634,7 @@ public function testProjectScopedSchemasMatchTheMembersList(): void { * @return void */ public function testProjectScopedCreateIsGatedByTheListenerNotByAMatch(): void { - foreach (['task', 'column', 'projectPhase', 'projectLogEntry', 'risk'] as $slug) { + foreach (['task', 'column', 'projectPhase', 'projectLogEntry', 'risk', 'projectStatusReport'] as $slug) { self::assertSame( expected: [['group' => 'authenticated'], ['group' => 'admin']], actual: $this->register['components']['schemas'][$slug]['authorization']['create'], @@ -708,4 +710,73 @@ public function testRiskSchemaCalculatesItsScore(): void { ); }//end testRiskSchemaCalculatesItsScore() + /** + * A status report sets six aspects, each with a note, and the server calculates the overall status. + * + * `overall` is a materialised `x-openregister-calculations` entry: the worst + * of the six, so an overall a client sends is overwritten on save. + * + * @return void + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.1 + */ + public function testProjectStatusReportSchemaCalculatesTheOverallStatus(): void { + $schema = $this->register['components']['schemas']['projectStatusReport']; + $properties = $schema['properties']; + $aspects = ['Money', 'Organisation', 'Time', 'Information', 'Quality', 'Risk']; + + self::assertSame( + expected: array_merge(['project', 'reportDate'], array_map(static fn (string $a): string => 'status' . $a, $aspects)), + actual: $schema['required'] + ); + self::assertSame(expected: 'project', actual: $properties['project']['$ref']); + self::assertSame(expected: 'date', actual: $properties['reportDate']['format']); + foreach ($aspects as $aspect) { + self::assertSame(expected: ['onTrack', 'atRisk', 'offTrack'], actual: $properties['status' . $aspect]['enum'], message: $aspect); + self::assertSame(expected: 'string', actual: $properties['note' . $aspect]['type'], message: $aspect); + } + + self::assertSame(expected: ['onTrack', 'atRisk', 'offTrack'], actual: $properties['overall']['enum']); + $calculation = $schema['x-openregister-calculations']['overall']; + self::assertTrue(condition: $calculation['materialise']); + self::assertSame(expected: 'string', actual: $calculation['type']); + + $anyIs = static fn (string $value): array => [ + 'or' => array_map( + static fn (string $a): array => ['eq' => [['prop' => 'status' . $a], ['lit' => $value]]], + $aspects + ), + ]; + self::assertSame( + expected: [ + 'if' => [ + $anyIs('offTrack'), + ['lit' => 'offTrack'], + ['if' => [$anyIs('atRisk'), ['lit' => 'atRisk'], ['lit' => 'onTrack']]], + ], + ], + actual: $calculation['expression'] + ); + + }//end testProjectStatusReportSchemaCalculatesTheOverallStatus() + + /** + * A project carries the newest report's statuses and date, nullable, for the portfolio roll-up. + * + * @return void + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 + */ + public function testProjectCarriesTheHealthOfItsNewestReport(): void { + $properties = $this->register['components']['schemas']['project']['properties']; + foreach (['Money', 'Organisation', 'Time', 'Information', 'Quality', 'Risk', 'Overall'] as $aspect) { + $field = $properties['health' . $aspect]; + self::assertSame(expected: ['onTrack', 'atRisk', 'offTrack'], actual: $field['enum'], message: $aspect); + self::assertTrue(condition: $field['nullable'], message: $aspect); + } + + self::assertSame(expected: 'date', actual: $properties['healthDate']['format']); + self::assertNotContains(needle: 'healthOverall', haystack: $this->register['components']['schemas']['project']['required']); + + }//end testProjectCarriesTheHealthOfItsNewestReport() }//end class diff --git a/tests/unit/Support/MembershipFixture.php b/tests/unit/Support/MembershipFixture.php index ff09dce8..18334509 100644 --- a/tests/unit/Support/MembershipFixture.php +++ b/tests/unit/Support/MembershipFixture.php @@ -70,6 +70,7 @@ trait MembershipFixture { '15' => 'label', '16' => 'projectLogEntry', '17' => 'risk', + '18' => 'projectStatusReport', ]; /** diff --git a/tests/vitest/projectOverview.spec.js b/tests/vitest/projectOverview.spec.js index 04e93484..6244eccd 100644 --- a/tests/vitest/projectOverview.spec.js +++ b/tests/vitest/projectOverview.spec.js @@ -80,7 +80,7 @@ describe('project tabs', () => { for (const tab of PROJECT_TABS) { expect(pages[tab.route]?.route, tab.route).toMatch(/^\/projects\/:id(\/|$)/) } - expect(PROJECT_TABS.map((tab) => tab.id)).toEqual(['overview', 'board', 'backlog', 'timeline', 'risks', 'log']) + expect(PROJECT_TABS.map((tab) => tab.id)).toEqual(['overview', 'board', 'backlog', 'timeline', 'risks', 'status', 'log']) }) }) diff --git a/tests/vitest/statusSuggestions.spec.js b/tests/vitest/statusSuggestions.spec.js new file mode 100644 index 00000000..35889773 --- /dev/null +++ b/tests/vitest/statusSuggestions.spec.js @@ -0,0 +1,134 @@ +/** + * Vitest unit tests for the status report helpers: the suggestions for money, + * time and risk, the worst-status rule, the report order and the payload the + * Status tab writes (portfolio-status-overview). + * + * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.3 + */ +import Ajv from 'ajv' +import addFormats from 'ajv-formats' +import { readFileSync } from 'node:fs' +import { describe, expect, it } from 'vitest' +import { DEFAULT_RISK_SCALE } from '../../src/utils/riskHelpers.js' +import { + ASPECTS, + MONEY_AT_RISK_RATIO, + reportPayload, + sortReports, + suggestMoney, + suggestRisk, + suggestTime, + worstStatus, +} from '../../src/utils/statusReports.js' + +const project = '11111111-1111-4111-8111-111111111111' +const today = '2026-09-28' +const register = JSON.parse(readFileSync(new URL('../../lib/Settings/planninq_register.json', import.meta.url), 'utf8')) + +describe('suggestTime', () => { + it('suggests at risk with the count of late open tasks (scenario: a time suggestion from late tasks)', () => { + const tasks = [ + { status: 'todo', dueDate: '2026-09-01' }, + { status: 'in_progress', dueDate: '2026-09-27' }, + { status: 'review', dueDate: '2026-09-10T12:00:00Z' }, + { status: 'done', dueDate: '2026-09-01' }, + { status: 'cancelled', dueDate: '2026-09-01' }, + { status: 'todo', dueDate: '2026-09-28' }, + { status: 'todo' }, + ] + expect(suggestTime(tasks, { endDate: '2026-12-31' }, today)).toEqual({ status: 'atRisk', code: 'late', count: 3 }) + }) + + it('suggests off track when the end date has passed with open tasks', () => { + const tasks = [{ status: 'todo' }, { status: 'done' }] + expect(suggestTime(tasks, { endDate: '2026-09-27' }, today)).toEqual({ status: 'offTrack', code: 'endPassed', count: 1 }) + }) + + it('suggests on track when nothing is late', () => { + expect(suggestTime([{ status: 'todo', dueDate: '2026-10-01' }], { endDate: '2026-12-01' }, today).status).toBe('onTrack') + expect(suggestTime([], null, today)).toEqual({ status: 'onTrack', code: 'onSchedule', count: 0 }) + }) +}) + +describe('suggestRisk', () => { + it('takes the band of the highest open risk', () => { + const risks = [ + { title: 'Closed', score: 25, status: 'closed' }, + { title: 'Supplier', score: 12, status: 'open' }, + { title: 'Weather', score: 6, status: 'mitigating' }, + ] + expect(suggestRisk(risks, DEFAULT_RISK_SCALE)).toEqual({ status: 'offTrack', code: 'highestRisk', score: 12, title: 'Supplier' }) + expect(suggestRisk([{ title: 'W', score: 6, status: 'open' }], DEFAULT_RISK_SCALE).status).toBe('atRisk') + expect(suggestRisk([{ title: 'W', score: 2, status: 'open' }], DEFAULT_RISK_SCALE).status).toBe('onTrack') + }) + + it('suggests on track without open risks', () => { + expect(suggestRisk([{ score: 25, status: 'occurred' }], DEFAULT_RISK_SCALE)).toEqual({ status: 'onTrack', code: 'noOpenRisks' }) + }) +}) + +describe('suggestMoney', () => { + it('uses the named threshold: at risk past 90 percent, off track past the budget', () => { + expect(MONEY_AT_RISK_RATIO).toBe(0.9) + expect(suggestMoney({ budget: 1000, cost: 900 }).status).toBe('onTrack') + expect(suggestMoney({ budget: 1000, cost: 901 })).toEqual({ status: 'atRisk', code: 'nearBudget', percent: 90 }) + expect(suggestMoney({ budget: 1000, cost: 1200 })).toEqual({ status: 'offTrack', code: 'overBudget', percent: 120 }) + }) + + it('suggests nothing without a budget or without recorded costs', () => { + expect(suggestMoney({ budget: 0, cost: 10 })).toEqual({ status: null, code: 'noBudget' }) + expect(suggestMoney({ budget: 1000, cost: null })).toEqual({ status: null, code: 'noCosts' }) + }) +}) + +describe('worstStatus and sortReports', () => { + it('takes the worst of the six, like the server calculation', () => { + expect(worstStatus(['onTrack', 'atRisk', 'onTrack'])).toBe('atRisk') + expect(worstStatus(['onTrack', 'offTrack', 'atRisk'])).toBe('offTrack') + expect(worstStatus([])).toBe(null) + }) + + it('lists the newest report first (scenario: writing a status report)', () => { + const reports = [ + { id: 'a', reportDate: '2026-08-01' }, + { id: 'b', reportDate: '2026-09-28', '@self': { created: '2026-09-28T09:00:00Z' } }, + { id: 'c', reportDate: '2026-09-28', '@self': { created: '2026-09-28T15:00:00Z' } }, + ] + expect(sortReports(reports).map((r) => r.id)).toEqual(['c', 'b', 'a']) + }) +}) + +describe('reportPayload', () => { + const schema = register.components.schemas.projectStatusReport + const properties = {} + for (const [name, property] of Object.entries(schema.properties)) { + const { $ref, visible, ...rest } = property + for (const key of Object.keys(rest)) { + if (key.startsWith('x-')) { + delete rest[key] + } + } + properties[name] = rest + } + const ajv = new Ajv({ strict: false }) + addFormats(ajv) + const validate = ajv.compile({ type: 'object', required: schema.required, properties }) + + it('builds a report the real schema accepts, without an overall of its own', () => { + const fields = { reportDate: today } + for (const aspect of ASPECTS) { + fields[aspect] = { status: 'onTrack', note: '' } + } + fields.time = { status: 'atRisk', note: ' Permit delayed by 3 weeks ' } + const payload = reportPayload(fields, project) + expect(payload).toMatchObject({ project, reportDate: today, statusTime: 'atRisk', noteTime: 'Permit delayed by 3 weeks', statusMoney: 'onTrack' }) + expect(payload).not.toHaveProperty('overall') + expect(validate(payload), JSON.stringify(validate.errors)).toBe(true) + }) + + it('a report with an aspect left open is refused by the schema', () => { + const payload = reportPayload({ reportDate: today, money: { status: 'onTrack' } }, project) + expect(payload).not.toHaveProperty('statusTime') + expect(validate(payload)).toBe(false) + }) +}) From 5172b4c913e62a0f3269cc1c14d5435cf686e86b Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 29 Sep 2026 07:09:44 +0200 Subject: [PATCH 159/289] feat(projects): group projects into portfolios whose managers read every project in them (#714) * feat(portfolios): a portfolio schema whose managers read every project in it, derived as portfolioReaders and copied onto the project's objects * wip: mid-task state when the lane was killed by the session limit (29 Sep 02:00); not verified * fix(portfolios): name the schema projectPortfolio, because learniq owns the portfolio slug on a shared OpenRegister Verified the wip commit before it: vitest 207, phpunit 331, check:strict 0, lint, stylelint, l10n, l10n-js, schema-l10n, manifest and build 0; hydra gates 0 after this rename (gate-106 was the one finding). --- appinfo/info.xml | 2 +- l10n/.schema-l10n-baseline.json | 2 +- l10n/be.js | 18 +- l10n/be.json | 18 +- l10n/bg.js | 18 +- l10n/bg.json | 18 +- l10n/bs.js | 18 +- l10n/bs.json | 18 +- l10n/ca.js | 18 +- l10n/ca.json | 18 +- l10n/cs.js | 18 +- l10n/cs.json | 18 +- l10n/da.js | 18 +- l10n/da.json | 18 +- l10n/de.js | 18 +- l10n/de.json | 18 +- l10n/el.js | 18 +- l10n/el.json | 18 +- l10n/en.js | 18 +- l10n/en.json | 18 +- l10n/es.js | 18 +- l10n/es.json | 18 +- l10n/et.js | 18 +- l10n/et.json | 18 +- l10n/fi.js | 18 +- l10n/fi.json | 18 +- l10n/fr.js | 18 +- l10n/fr.json | 18 +- l10n/ga.js | 18 +- l10n/ga.json | 18 +- l10n/hr.js | 18 +- l10n/hr.json | 18 +- l10n/hu.js | 18 +- l10n/hu.json | 18 +- l10n/is.js | 18 +- l10n/is.json | 18 +- l10n/it.js | 18 +- l10n/it.json | 18 +- l10n/lb.js | 18 +- l10n/lb.json | 18 +- l10n/lt.js | 18 +- l10n/lt.json | 18 +- l10n/lv.js | 18 +- l10n/lv.json | 18 +- l10n/mk.js | 18 +- l10n/mk.json | 18 +- l10n/mt.js | 18 +- l10n/mt.json | 18 +- l10n/nb.js | 18 +- l10n/nb.json | 18 +- l10n/nl.js | 18 +- l10n/nl.json | 18 +- l10n/pl.js | 18 +- l10n/pl.json | 18 +- l10n/pt.js | 18 +- l10n/pt.json | 18 +- l10n/rm.js | 18 +- l10n/rm.json | 18 +- l10n/ro.js | 18 +- l10n/ro.json | 18 +- l10n/ru.js | 18 +- l10n/ru.json | 18 +- l10n/sk.js | 18 +- l10n/sk.json | 18 +- l10n/sl.js | 18 +- l10n/sl.json | 18 +- l10n/sq.js | 18 +- l10n/sq.json | 18 +- l10n/sr.js | 18 +- l10n/sr.json | 18 +- l10n/sv.js | 18 +- l10n/sv.json | 18 +- l10n/tr.js | 18 +- l10n/tr.json | 18 +- l10n/uk.js | 18 +- l10n/uk.json | 18 +- lib/AppInfo/Application.php | 13 + .../ProjectHierarchyGuardListener.php | 299 ++++++++++++++++++ lib/Listener/ProjectMemberAccessListener.php | 46 ++- .../ProjectMembershipSyncListener.php | 30 +- lib/Service/ProjectHealthService.php | 2 +- lib/Service/ProjectMembershipService.php | 84 +++-- lib/Settings/planninq_mock_register.json | 39 +++ lib/Settings/planninq_register.json | 261 ++++++++++++++- .../design.md | 12 + .../tasks.md | 12 +- openspec/specs/project-delivery/spec.md | 6 +- src/components/PortfolioSections.vue | 146 +++++++++ src/components/ProjectSettingsSidebar.vue | 54 ++++ src/icons.js | 2 + src/manifest.json | 4 +- src/store/projects.js | 26 +- src/utils/portfolioGrouping.js | 131 ++++++++ src/views/Boards.vue | 61 ++-- src/views/ProjectBoard.vue | 28 +- src/views/ProjectList.vue | 79 ++++- src/views/ProjectRisks.vue | 12 +- tests/e2e/project-portfolios.spec.ts | 103 ++++++ tests/unit/AppInfo/BoardColumnWiringTest.php | 6 + .../ProjectHierarchyGuardListenerTest.php | 215 +++++++++++++ .../Settings/PlanninqRegisterSchemaTest.php | 69 +++- tests/unit/Support/MembershipFixture.php | 1 + tests/vitest/projectGrouping.spec.js | 86 +++++ 103 files changed, 2973 insertions(+), 190 deletions(-) create mode 100644 lib/Listener/ProjectHierarchyGuardListener.php create mode 100644 src/components/PortfolioSections.vue create mode 100644 src/utils/portfolioGrouping.js create mode 100644 tests/e2e/project-portfolios.spec.ts create mode 100644 tests/unit/Listener/ProjectHierarchyGuardListenerTest.php create mode 100644 tests/vitest/projectGrouping.spec.js diff --git a/appinfo/info.xml b/appinfo/info.xml index 8015fa8b..e8a79466 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -42,7 +42,7 @@ Vrij en open source onder de EUPL-1.2-licentie. **Ondersteuning:** Voor ondersteuning, neem contact op via support@conduction.nl. ]]> - 0.2.23-unstable.20260929013000 + 0.2.23-unstable.20260929030000 EUPL-1.2 Conduction Planninq diff --git a/l10n/.schema-l10n-baseline.json b/l10n/.schema-l10n-baseline.json index 8a3b5634..f59a18f5 100644 --- a/l10n/.schema-l10n-baseline.json +++ b/l10n/.schema-l10n-baseline.json @@ -1,3 +1,3 @@ { - "uncovered": 131 + "uncovered": 130 } diff --git a/l10n/be.js b/l10n/be.js index 6a89b43a..a54edf00 100644 --- a/l10n/be.js +++ b/l10n/be.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Няма прапановы: у праекта няма бюджэту.", "No suggestion: no costs are recorded for this project yet.": "Няма прапановы: па гэтым праекце яшчэ не ўлічаны выдаткі.", "The report was not saved. Only the project owner can write status reports.": "Справаздача не захавана. Справаздачы пра стан можа пісаць толькі ўладальнік праекта.", - "No report": "Няма справаздачы" + "No report": "Няма справаздачы", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група праектаў, якімі кіруюць адны і тыя ж людзі, напрыклад аддзел або праграма. Яе кіраўнікі чытаюць кожны праект у ёй.", + "The name of the portfolio": "Назва партфеля", + "What the portfolio holds and who runs it": "Што ўваходзіць у партфель і хто ім кіруе", + "A colour for the portfolio's section in project lists": "Колер раздзела партфеля ў спісах праектаў", + "Managers": "Кіраўнікі", + "Nextcloud user UIDs of the people who run the portfolio": "Ідэнтыфікатары карыстальнікаў Nextcloud, якія кіруюць партфелем", + "Order": "Парадак", + "Where the portfolio comes in project lists, lowest first": "Месца партфеля ў спісах праектаў, спачатку найменшы", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Уласная шкала рызык гэтага партфеля ў фармаце JSON, у той жа форме, што і шкала ўсёй праграмы. Пуста азначае шкалу праграмы.", + "Portfolio readers": "Чытачы партфеля", + "Nextcloud user UIDs of the managers of the project's portfolio": "Ідэнтыфікатары карыстальнікаў Nextcloud кіраўнікоў партфеля праекта", + "The portfolio this project belongs to": "Партфель, да якога належыць праект", + "No portfolio": "Без партфеля", + "All portfolios": "Усе партфелі", + "You read this project as a manager of its portfolio. Only its members change it.": "Вы чытаеце гэты праект як кіраўнік яго партфеля. Змяняюць яго толькі ўдзельнікі.", + "Portfolios": "Партфелі" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/be.json b/l10n/be.json index 94fcd963..453e3541 100644 --- a/l10n/be.json +++ b/l10n/be.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Няма прапановы: у праекта няма бюджэту.", "No suggestion: no costs are recorded for this project yet.": "Няма прапановы: па гэтым праекце яшчэ не ўлічаны выдаткі.", "The report was not saved. Only the project owner can write status reports.": "Справаздача не захавана. Справаздачы пра стан можа пісаць толькі ўладальнік праекта.", - "No report": "Няма справаздачы" + "No report": "Няма справаздачы", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група праектаў, якімі кіруюць адны і тыя ж людзі, напрыклад аддзел або праграма. Яе кіраўнікі чытаюць кожны праект у ёй.", + "The name of the portfolio": "Назва партфеля", + "What the portfolio holds and who runs it": "Што ўваходзіць у партфель і хто ім кіруе", + "A colour for the portfolio's section in project lists": "Колер раздзела партфеля ў спісах праектаў", + "Managers": "Кіраўнікі", + "Nextcloud user UIDs of the people who run the portfolio": "Ідэнтыфікатары карыстальнікаў Nextcloud, якія кіруюць партфелем", + "Order": "Парадак", + "Where the portfolio comes in project lists, lowest first": "Месца партфеля ў спісах праектаў, спачатку найменшы", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Уласная шкала рызык гэтага партфеля ў фармаце JSON, у той жа форме, што і шкала ўсёй праграмы. Пуста азначае шкалу праграмы.", + "Portfolio readers": "Чытачы партфеля", + "Nextcloud user UIDs of the managers of the project's portfolio": "Ідэнтыфікатары карыстальнікаў Nextcloud кіраўнікоў партфеля праекта", + "The portfolio this project belongs to": "Партфель, да якога належыць праект", + "No portfolio": "Без партфеля", + "All portfolios": "Усе партфелі", + "You read this project as a manager of its portfolio. Only its members change it.": "Вы чытаеце гэты праект як кіраўнік яго партфеля. Змяняюць яго толькі ўдзельнікі.", + "Portfolios": "Партфелі" }, "plurals": {} } diff --git a/l10n/bg.js b/l10n/bg.js index 3b476b7e..4c6c1ba3 100644 --- a/l10n/bg.js +++ b/l10n/bg.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Няма предложение: проектът няма бюджет.", "No suggestion: no costs are recorded for this project yet.": "Няма предложение: все още няма записани разходи за този проект.", "The report was not saved. Only the project owner can write status reports.": "Докладът не е запазен. Само собственикът на проекта може да пише доклади за състоянието.", - "No report": "Няма доклад" + "No report": "Няма доклад", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група проекти, водени от едни и същи хора, например отдел или програма. Нейните ръководители четат всеки проект в нея.", + "The name of the portfolio": "Името на портфолиото", + "What the portfolio holds and who runs it": "Какво съдържа портфолиото и кой го води", + "A colour for the portfolio's section in project lists": "Цвят за раздела на портфолиото в списъците с проекти", + "Managers": "Ръководители", + "Nextcloud user UIDs of the people who run the portfolio": "Потребителски ID в Nextcloud на хората, които водят портфолиото", + "Order": "Ред", + "Where the portfolio comes in project lists, lowest first": "Къде е портфолиото в списъците с проекти, най-ниското първо", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Собствената скала за риск на това портфолио като JSON, в същата форма като скалата за цялото приложение. Празно означава скалата на приложението.", + "Portfolio readers": "Читатели на портфолиото", + "Nextcloud user UIDs of the managers of the project's portfolio": "Потребителски ID в Nextcloud на ръководителите на портфолиото на проекта", + "The portfolio this project belongs to": "Портфолиото, към което принадлежи проектът", + "No portfolio": "Без портфолио", + "All portfolios": "Всички портфолиа", + "You read this project as a manager of its portfolio. Only its members change it.": "Четете този проект като ръководител на неговото портфолио. Само членовете му го променят.", + "Portfolios": "Портфолиа" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/bg.json b/l10n/bg.json index 734dd96c..1a9db449 100644 --- a/l10n/bg.json +++ b/l10n/bg.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Няма предложение: проектът няма бюджет.", "No suggestion: no costs are recorded for this project yet.": "Няма предложение: все още няма записани разходи за този проект.", "The report was not saved. Only the project owner can write status reports.": "Докладът не е запазен. Само собственикът на проекта може да пише доклади за състоянието.", - "No report": "Няма доклад" + "No report": "Няма доклад", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група проекти, водени от едни и същи хора, например отдел или програма. Нейните ръководители четат всеки проект в нея.", + "The name of the portfolio": "Името на портфолиото", + "What the portfolio holds and who runs it": "Какво съдържа портфолиото и кой го води", + "A colour for the portfolio's section in project lists": "Цвят за раздела на портфолиото в списъците с проекти", + "Managers": "Ръководители", + "Nextcloud user UIDs of the people who run the portfolio": "Потребителски ID в Nextcloud на хората, които водят портфолиото", + "Order": "Ред", + "Where the portfolio comes in project lists, lowest first": "Къде е портфолиото в списъците с проекти, най-ниското първо", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Собствената скала за риск на това портфолио като JSON, в същата форма като скалата за цялото приложение. Празно означава скалата на приложението.", + "Portfolio readers": "Читатели на портфолиото", + "Nextcloud user UIDs of the managers of the project's portfolio": "Потребителски ID в Nextcloud на ръководителите на портфолиото на проекта", + "The portfolio this project belongs to": "Портфолиото, към което принадлежи проектът", + "No portfolio": "Без портфолио", + "All portfolios": "Всички портфолиа", + "You read this project as a manager of its portfolio. Only its members change it.": "Четете този проект като ръководител на неговото портфолио. Само членовете му го променят.", + "Portfolios": "Портфолиа" }, "plurals": null } diff --git a/l10n/bs.js b/l10n/bs.js index c271193d..a434fde5 100644 --- a/l10n/bs.js +++ b/l10n/bs.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Nema prijedloga: projekt nema budžet.", "No suggestion: no costs are recorded for this project yet.": "Nema prijedloga: za ovaj projekt još nisu zabilježeni troškovi.", "The report was not saved. Only the project owner can write status reports.": "Izvještaj nije spremljen. Izvještaje o stanju može pisati samo vlasnik projekta.", - "No report": "Nema izvještaja" + "No report": "Nema izvještaja", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Grupa projekata koje vode isti ljudi, na primjer odjel ili program. Njeni voditelji čitaju svaki projekt u njoj.", + "The name of the portfolio": "Naziv portfolija", + "What the portfolio holds and who runs it": "Šta portfolio sadrži i ko ga vodi", + "A colour for the portfolio's section in project lists": "Boja odjeljka portfolija u listama projekata", + "Managers": "Voditelji", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud korisnički ID-ovi ljudi koji vode portfolio", + "Order": "Redoslijed", + "Where the portfolio comes in project lists, lowest first": "Gdje je portfolio u listama projekata, najniži prvi", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Vlastita skala rizika ovog portfolija kao JSON, u istom obliku kao skala cijele aplikacije. Prazno znači skalu aplikacije.", + "Portfolio readers": "Čitaoci portfolija", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud korisnički ID-ovi voditelja portfolija projekta", + "The portfolio this project belongs to": "Portfolio kojem ovaj projekt pripada", + "No portfolio": "Bez portfolija", + "All portfolios": "Svi portfoliji", + "You read this project as a manager of its portfolio. Only its members change it.": "Ovaj projekt čitate kao voditelj njegovog portfolija. Mijenjaju ga samo njegovi članovi.", + "Portfolios": "Portfoliji" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/bs.json b/l10n/bs.json index 0a1e78fa..e122411e 100644 --- a/l10n/bs.json +++ b/l10n/bs.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Nema prijedloga: projekt nema budžet.", "No suggestion: no costs are recorded for this project yet.": "Nema prijedloga: za ovaj projekt još nisu zabilježeni troškovi.", "The report was not saved. Only the project owner can write status reports.": "Izvještaj nije spremljen. Izvještaje o stanju može pisati samo vlasnik projekta.", - "No report": "Nema izvještaja" + "No report": "Nema izvještaja", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Grupa projekata koje vode isti ljudi, na primjer odjel ili program. Njeni voditelji čitaju svaki projekt u njoj.", + "The name of the portfolio": "Naziv portfolija", + "What the portfolio holds and who runs it": "Šta portfolio sadrži i ko ga vodi", + "A colour for the portfolio's section in project lists": "Boja odjeljka portfolija u listama projekata", + "Managers": "Voditelji", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud korisnički ID-ovi ljudi koji vode portfolio", + "Order": "Redoslijed", + "Where the portfolio comes in project lists, lowest first": "Gdje je portfolio u listama projekata, najniži prvi", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Vlastita skala rizika ovog portfolija kao JSON, u istom obliku kao skala cijele aplikacije. Prazno znači skalu aplikacije.", + "Portfolio readers": "Čitaoci portfolija", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud korisnički ID-ovi voditelja portfolija projekta", + "The portfolio this project belongs to": "Portfolio kojem ovaj projekt pripada", + "No portfolio": "Bez portfolija", + "All portfolios": "Svi portfoliji", + "You read this project as a manager of its portfolio. Only its members change it.": "Ovaj projekt čitate kao voditelj njegovog portfolija. Mijenjaju ga samo njegovi članovi.", + "Portfolios": "Portfoliji" }, "plurals": null } diff --git a/l10n/ca.js b/l10n/ca.js index bc79c272..225d0b9f 100644 --- a/l10n/ca.js +++ b/l10n/ca.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Sense suggeriment: el projecte no té pressupost.", "No suggestion: no costs are recorded for this project yet.": "Sense suggeriment: encara no hi ha costos registrats per a aquest projecte.", "The report was not saved. Only the project owner can write status reports.": "L'informe no s'ha desat. Només el propietari del projecte pot escriure informes d'estat.", - "No report": "Sense informe" + "No report": "Sense informe", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un grup de projectes portats per les mateixes persones, com un departament o un programa. Els seus responsables llegeixen cada projecte que conté.", + "The name of the portfolio": "El nom de la cartera", + "What the portfolio holds and who runs it": "Què conté la cartera i qui la porta", + "A colour for the portfolio's section in project lists": "Un color per a la secció de la cartera a les llistes de projectes", + "Managers": "Responsables", + "Nextcloud user UIDs of the people who run the portfolio": "Identificadors d'usuari de Nextcloud de les persones que porten la cartera", + "Order": "Ordre", + "Where the portfolio comes in project lists, lowest first": "On apareix la cartera a les llistes de projectes, primer el més baix", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "L'escala de risc pròpia d'aquesta cartera en JSON, amb la mateixa forma que la de tota l'aplicació. Buit vol dir l'escala de l'aplicació.", + "Portfolio readers": "Lectors de la cartera", + "Nextcloud user UIDs of the managers of the project's portfolio": "Identificadors d'usuari de Nextcloud dels responsables de la cartera del projecte", + "The portfolio this project belongs to": "La cartera a la qual pertany aquest projecte", + "No portfolio": "Sense cartera", + "All portfolios": "Totes les carteres", + "You read this project as a manager of its portfolio. Only its members change it.": "Llegiu aquest projecte com a responsable de la seva cartera. Només els membres el canvien.", + "Portfolios": "Carteres" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ca.json b/l10n/ca.json index 65b6260d..75b8a140 100644 --- a/l10n/ca.json +++ b/l10n/ca.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Sense suggeriment: el projecte no té pressupost.", "No suggestion: no costs are recorded for this project yet.": "Sense suggeriment: encara no hi ha costos registrats per a aquest projecte.", "The report was not saved. Only the project owner can write status reports.": "L'informe no s'ha desat. Només el propietari del projecte pot escriure informes d'estat.", - "No report": "Sense informe" + "No report": "Sense informe", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un grup de projectes portats per les mateixes persones, com un departament o un programa. Els seus responsables llegeixen cada projecte que conté.", + "The name of the portfolio": "El nom de la cartera", + "What the portfolio holds and who runs it": "Què conté la cartera i qui la porta", + "A colour for the portfolio's section in project lists": "Un color per a la secció de la cartera a les llistes de projectes", + "Managers": "Responsables", + "Nextcloud user UIDs of the people who run the portfolio": "Identificadors d'usuari de Nextcloud de les persones que porten la cartera", + "Order": "Ordre", + "Where the portfolio comes in project lists, lowest first": "On apareix la cartera a les llistes de projectes, primer el més baix", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "L'escala de risc pròpia d'aquesta cartera en JSON, amb la mateixa forma que la de tota l'aplicació. Buit vol dir l'escala de l'aplicació.", + "Portfolio readers": "Lectors de la cartera", + "Nextcloud user UIDs of the managers of the project's portfolio": "Identificadors d'usuari de Nextcloud dels responsables de la cartera del projecte", + "The portfolio this project belongs to": "La cartera a la qual pertany aquest projecte", + "No portfolio": "Sense cartera", + "All portfolios": "Totes les carteres", + "You read this project as a manager of its portfolio. Only its members change it.": "Llegiu aquest projecte com a responsable de la seva cartera. Només els membres el canvien.", + "Portfolios": "Carteres" }, "plurals": null } diff --git a/l10n/cs.js b/l10n/cs.js index da6eecb8..b0164d71 100644 --- a/l10n/cs.js +++ b/l10n/cs.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Bez návrhu: projekt nemá rozpočet.", "No suggestion: no costs are recorded for this project yet.": "Bez návrhu: pro tento projekt zatím nejsou zaznamenány žádné náklady.", "The report was not saved. Only the project owner can write status reports.": "Zpráva nebyla uložena. Zprávy o stavu může psát jen vlastník projektu.", - "No report": "Žádná zpráva" + "No report": "Žádná zpráva", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Skupina projektů vedených stejnými lidmi, například oddělení nebo program. Její správci čtou každý projekt v ní.", + "The name of the portfolio": "Název portfolia", + "What the portfolio holds and who runs it": "Co portfolio obsahuje a kdo ho vede", + "A colour for the portfolio's section in project lists": "Barva sekce portfolia v seznamech projektů", + "Managers": "Správci", + "Nextcloud user UIDs of the people who run the portfolio": "ID uživatelů Nextcloud lidí, kteří portfolio vedou", + "Order": "Pořadí", + "Where the portfolio comes in project lists, lowest first": "Kde je portfolio v seznamech projektů, od nejnižšího", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Vlastní škála rizik tohoto portfolia jako JSON, ve stejném tvaru jako škála celé aplikace. Prázdné znamená škálu aplikace.", + "Portfolio readers": "Čtenáři portfolia", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID uživatelů Nextcloud správců portfolia projektu", + "The portfolio this project belongs to": "Portfolio, do kterého projekt patří", + "No portfolio": "Bez portfolia", + "All portfolios": "Všechna portfolia", + "You read this project as a manager of its portfolio. Only its members change it.": "Tento projekt čtete jako správce jeho portfolia. Mění ho jen jeho členové.", + "Portfolios": "Portfolia" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/cs.json b/l10n/cs.json index c24fdb99..dddd0bcd 100644 --- a/l10n/cs.json +++ b/l10n/cs.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Bez návrhu: projekt nemá rozpočet.", "No suggestion: no costs are recorded for this project yet.": "Bez návrhu: pro tento projekt zatím nejsou zaznamenány žádné náklady.", "The report was not saved. Only the project owner can write status reports.": "Zpráva nebyla uložena. Zprávy o stavu může psát jen vlastník projektu.", - "No report": "Žádná zpráva" + "No report": "Žádná zpráva", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Skupina projektů vedených stejnými lidmi, například oddělení nebo program. Její správci čtou každý projekt v ní.", + "The name of the portfolio": "Název portfolia", + "What the portfolio holds and who runs it": "Co portfolio obsahuje a kdo ho vede", + "A colour for the portfolio's section in project lists": "Barva sekce portfolia v seznamech projektů", + "Managers": "Správci", + "Nextcloud user UIDs of the people who run the portfolio": "ID uživatelů Nextcloud lidí, kteří portfolio vedou", + "Order": "Pořadí", + "Where the portfolio comes in project lists, lowest first": "Kde je portfolio v seznamech projektů, od nejnižšího", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Vlastní škála rizik tohoto portfolia jako JSON, ve stejném tvaru jako škála celé aplikace. Prázdné znamená škálu aplikace.", + "Portfolio readers": "Čtenáři portfolia", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID uživatelů Nextcloud správců portfolia projektu", + "The portfolio this project belongs to": "Portfolio, do kterého projekt patří", + "No portfolio": "Bez portfolia", + "All portfolios": "Všechna portfolia", + "You read this project as a manager of its portfolio. Only its members change it.": "Tento projekt čtete jako správce jeho portfolia. Mění ho jen jeho členové.", + "Portfolios": "Portfolia" }, "plurals": null } diff --git a/l10n/da.js b/l10n/da.js index 2008c44b..517f6dcf 100644 --- a/l10n/da.js +++ b/l10n/da.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Intet forslag: projektet har intet budget.", "No suggestion: no costs are recorded for this project yet.": "Intet forslag: der er endnu ikke registreret omkostninger for dette projekt.", "The report was not saved. Only the project owner can write status reports.": "Rapporten blev ikke gemt. Kun projektets ejer kan skrive statusrapporter.", - "No report": "Ingen rapport" + "No report": "Ingen rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "En gruppe projekter, der drives af de samme personer, for eksempel en afdeling eller et program. Dens ansvarlige læser alle projekter i den.", + "The name of the portfolio": "Porteføljens navn", + "What the portfolio holds and who runs it": "Hvad porteføljen rummer, og hvem der driver den", + "A colour for the portfolio's section in project lists": "En farve til porteføljens afsnit i projektlister", + "Managers": "Ansvarlige", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-bruger-id'er for de personer, der driver porteføljen", + "Order": "Rækkefølge", + "Where the portfolio comes in project lists, lowest first": "Hvor porteføljen står i projektlister, laveste først", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Porteføljens egen risikoskala som JSON, i samme form som den for hele appen. Tom betyder skalaen for hele appen.", + "Portfolio readers": "Porteføljelæsere", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-bruger-id'er for de ansvarlige for projektets portefølje", + "The portfolio this project belongs to": "Den portefølje, projektet hører til", + "No portfolio": "Ingen portefølje", + "All portfolios": "Alle porteføljer", + "You read this project as a manager of its portfolio. Only its members change it.": "Du læser dette projekt som ansvarlig for dets portefølje. Kun medlemmerne ændrer det.", + "Portfolios": "Porteføljer" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/da.json b/l10n/da.json index 637dee17..1664491c 100644 --- a/l10n/da.json +++ b/l10n/da.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Intet forslag: projektet har intet budget.", "No suggestion: no costs are recorded for this project yet.": "Intet forslag: der er endnu ikke registreret omkostninger for dette projekt.", "The report was not saved. Only the project owner can write status reports.": "Rapporten blev ikke gemt. Kun projektets ejer kan skrive statusrapporter.", - "No report": "Ingen rapport" + "No report": "Ingen rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "En gruppe projekter, der drives af de samme personer, for eksempel en afdeling eller et program. Dens ansvarlige læser alle projekter i den.", + "The name of the portfolio": "Porteføljens navn", + "What the portfolio holds and who runs it": "Hvad porteføljen rummer, og hvem der driver den", + "A colour for the portfolio's section in project lists": "En farve til porteføljens afsnit i projektlister", + "Managers": "Ansvarlige", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-bruger-id'er for de personer, der driver porteføljen", + "Order": "Rækkefølge", + "Where the portfolio comes in project lists, lowest first": "Hvor porteføljen står i projektlister, laveste først", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Porteføljens egen risikoskala som JSON, i samme form som den for hele appen. Tom betyder skalaen for hele appen.", + "Portfolio readers": "Porteføljelæsere", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-bruger-id'er for de ansvarlige for projektets portefølje", + "The portfolio this project belongs to": "Den portefølje, projektet hører til", + "No portfolio": "Ingen portefølje", + "All portfolios": "Alle porteføljer", + "You read this project as a manager of its portfolio. Only its members change it.": "Du læser dette projekt som ansvarlig for dets portefølje. Kun medlemmerne ændrer det.", + "Portfolios": "Porteføljer" }, "plurals": null } diff --git a/l10n/de.js b/l10n/de.js index 46752c6c..5d15a254 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Kein Vorschlag: Das Projekt hat kein Budget.", "No suggestion: no costs are recorded for this project yet.": "Kein Vorschlag: Für dieses Projekt sind noch keine Kosten erfasst.", "The report was not saved. Only the project owner can write status reports.": "Der Bericht wurde nicht gespeichert. Nur der Projekteigentümer kann Statusberichte schreiben.", - "No report": "Kein Bericht" + "No report": "Kein Bericht", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Eine Gruppe von Projekten, die von denselben Personen geführt wird, etwa eine Abteilung oder ein Programm. Die Verantwortlichen lesen jedes Projekt darin.", + "The name of the portfolio": "Der Name des Portfolios", + "What the portfolio holds and who runs it": "Was das Portfolio enthält und wer es führt", + "A colour for the portfolio's section in project lists": "Eine Farbe für den Abschnitt des Portfolios in Projektlisten", + "Managers": "Verantwortliche", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-Benutzer-IDs der Personen, die das Portfolio führen", + "Order": "Reihenfolge", + "Where the portfolio comes in project lists, lowest first": "Wo das Portfolio in Projektlisten steht, niedrigste zuerst", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Die eigene Risikoskala dieses Portfolios als JSON, in derselben Form wie die appweite. Leer bedeutet die appweite Skala.", + "Portfolio readers": "Portfolio-Leser", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-Benutzer-IDs der Verantwortlichen des Portfolios des Projekts", + "The portfolio this project belongs to": "Das Portfolio, zu dem dieses Projekt gehört", + "No portfolio": "Kein Portfolio", + "All portfolios": "Alle Portfolios", + "You read this project as a manager of its portfolio. Only its members change it.": "Sie lesen dieses Projekt als Verantwortliche des Portfolios. Nur seine Mitglieder ändern es.", + "Portfolios": "Portfolios" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/de.json b/l10n/de.json index 2cb053d6..bed2e561 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Kein Vorschlag: Das Projekt hat kein Budget.", "No suggestion: no costs are recorded for this project yet.": "Kein Vorschlag: Für dieses Projekt sind noch keine Kosten erfasst.", "The report was not saved. Only the project owner can write status reports.": "Der Bericht wurde nicht gespeichert. Nur der Projekteigentümer kann Statusberichte schreiben.", - "No report": "Kein Bericht" + "No report": "Kein Bericht", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Eine Gruppe von Projekten, die von denselben Personen geführt wird, etwa eine Abteilung oder ein Programm. Die Verantwortlichen lesen jedes Projekt darin.", + "The name of the portfolio": "Der Name des Portfolios", + "What the portfolio holds and who runs it": "Was das Portfolio enthält und wer es führt", + "A colour for the portfolio's section in project lists": "Eine Farbe für den Abschnitt des Portfolios in Projektlisten", + "Managers": "Verantwortliche", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-Benutzer-IDs der Personen, die das Portfolio führen", + "Order": "Reihenfolge", + "Where the portfolio comes in project lists, lowest first": "Wo das Portfolio in Projektlisten steht, niedrigste zuerst", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Die eigene Risikoskala dieses Portfolios als JSON, in derselben Form wie die appweite. Leer bedeutet die appweite Skala.", + "Portfolio readers": "Portfolio-Leser", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-Benutzer-IDs der Verantwortlichen des Portfolios des Projekts", + "The portfolio this project belongs to": "Das Portfolio, zu dem dieses Projekt gehört", + "No portfolio": "Kein Portfolio", + "All portfolios": "Alle Portfolios", + "You read this project as a manager of its portfolio. Only its members change it.": "Sie lesen dieses Projekt als Verantwortliche des Portfolios. Nur seine Mitglieder ändern es.", + "Portfolios": "Portfolios" }, "plurals": null } diff --git a/l10n/el.js b/l10n/el.js index 47baa3ad..e67d9b50 100644 --- a/l10n/el.js +++ b/l10n/el.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Καμία πρόταση: το έργο δεν έχει προϋπολογισμό.", "No suggestion: no costs are recorded for this project yet.": "Καμία πρόταση: δεν έχει καταγραφεί ακόμη κόστος για αυτό το έργο.", "The report was not saved. Only the project owner can write status reports.": "Η αναφορά δεν αποθηκεύτηκε. Μόνο ο κάτοχος του έργου μπορεί να γράφει αναφορές κατάστασης.", - "No report": "Καμία αναφορά" + "No report": "Καμία αναφορά", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Ομάδα έργων που διευθύνουν τα ίδια άτομα, όπως ένα τμήμα ή ένα πρόγραμμα. Οι υπεύθυνοί της διαβάζουν κάθε έργο της.", + "The name of the portfolio": "Το όνομα του χαρτοφυλακίου", + "What the portfolio holds and who runs it": "Τι περιέχει το χαρτοφυλάκιο και ποιος το διευθύνει", + "A colour for the portfolio's section in project lists": "Ένα χρώμα για την ενότητα του χαρτοφυλακίου στις λίστες έργων", + "Managers": "Υπεύθυνοι", + "Nextcloud user UIDs of the people who run the portfolio": "Αναγνωριστικά χρηστών Nextcloud των ατόμων που διευθύνουν το χαρτοφυλάκιο", + "Order": "Σειρά", + "Where the portfolio comes in project lists, lowest first": "Η θέση του χαρτοφυλακίου στις λίστες έργων, πρώτα το χαμηλότερο", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Η δική του κλίμακα κινδύνου αυτού του χαρτοφυλακίου σε JSON, στην ίδια μορφή με την κλίμακα όλης της εφαρμογής. Κενό σημαίνει την κλίμακα της εφαρμογής.", + "Portfolio readers": "Αναγνώστες χαρτοφυλακίου", + "Nextcloud user UIDs of the managers of the project's portfolio": "Αναγνωριστικά χρηστών Nextcloud των υπευθύνων του χαρτοφυλακίου του έργου", + "The portfolio this project belongs to": "Το χαρτοφυλάκιο στο οποίο ανήκει το έργο", + "No portfolio": "Χωρίς χαρτοφυλάκιο", + "All portfolios": "Όλα τα χαρτοφυλάκια", + "You read this project as a manager of its portfolio. Only its members change it.": "Διαβάζετε αυτό το έργο ως υπεύθυνος του χαρτοφυλακίου του. Μόνο τα μέλη του το αλλάζουν.", + "Portfolios": "Χαρτοφυλάκια" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/el.json b/l10n/el.json index fe290e42..e39b61a7 100644 --- a/l10n/el.json +++ b/l10n/el.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Καμία πρόταση: το έργο δεν έχει προϋπολογισμό.", "No suggestion: no costs are recorded for this project yet.": "Καμία πρόταση: δεν έχει καταγραφεί ακόμη κόστος για αυτό το έργο.", "The report was not saved. Only the project owner can write status reports.": "Η αναφορά δεν αποθηκεύτηκε. Μόνο ο κάτοχος του έργου μπορεί να γράφει αναφορές κατάστασης.", - "No report": "Καμία αναφορά" + "No report": "Καμία αναφορά", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Ομάδα έργων που διευθύνουν τα ίδια άτομα, όπως ένα τμήμα ή ένα πρόγραμμα. Οι υπεύθυνοί της διαβάζουν κάθε έργο της.", + "The name of the portfolio": "Το όνομα του χαρτοφυλακίου", + "What the portfolio holds and who runs it": "Τι περιέχει το χαρτοφυλάκιο και ποιος το διευθύνει", + "A colour for the portfolio's section in project lists": "Ένα χρώμα για την ενότητα του χαρτοφυλακίου στις λίστες έργων", + "Managers": "Υπεύθυνοι", + "Nextcloud user UIDs of the people who run the portfolio": "Αναγνωριστικά χρηστών Nextcloud των ατόμων που διευθύνουν το χαρτοφυλάκιο", + "Order": "Σειρά", + "Where the portfolio comes in project lists, lowest first": "Η θέση του χαρτοφυλακίου στις λίστες έργων, πρώτα το χαμηλότερο", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Η δική του κλίμακα κινδύνου αυτού του χαρτοφυλακίου σε JSON, στην ίδια μορφή με την κλίμακα όλης της εφαρμογής. Κενό σημαίνει την κλίμακα της εφαρμογής.", + "Portfolio readers": "Αναγνώστες χαρτοφυλακίου", + "Nextcloud user UIDs of the managers of the project's portfolio": "Αναγνωριστικά χρηστών Nextcloud των υπευθύνων του χαρτοφυλακίου του έργου", + "The portfolio this project belongs to": "Το χαρτοφυλάκιο στο οποίο ανήκει το έργο", + "No portfolio": "Χωρίς χαρτοφυλάκιο", + "All portfolios": "Όλα τα χαρτοφυλάκια", + "You read this project as a manager of its portfolio. Only its members change it.": "Διαβάζετε αυτό το έργο ως υπεύθυνος του χαρτοφυλακίου του. Μόνο τα μέλη του το αλλάζουν.", + "Portfolios": "Χαρτοφυλάκια" }, "plurals": null } diff --git a/l10n/en.js b/l10n/en.js index 48e3631d..3bc04b4a 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -452,7 +452,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "No suggestion: the project has no budget.", "No suggestion: no costs are recorded for this project yet.": "No suggestion: no costs are recorded for this project yet.", "The report was not saved. Only the project owner can write status reports.": "The report was not saved. Only the project owner can write status reports.", - "No report": "No report" + "No report": "No report", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.", + "The name of the portfolio": "The name of the portfolio", + "What the portfolio holds and who runs it": "What the portfolio holds and who runs it", + "A colour for the portfolio's section in project lists": "A colour for the portfolio's section in project lists", + "Managers": "Managers", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud user UIDs of the people who run the portfolio", + "Order": "Order", + "Where the portfolio comes in project lists, lowest first": "Where the portfolio comes in project lists, lowest first", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.", + "Portfolio readers": "Portfolio readers", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud user UIDs of the managers of the project's portfolio", + "The portfolio this project belongs to": "The portfolio this project belongs to", + "No portfolio": "No portfolio", + "All portfolios": "All portfolios", + "You read this project as a manager of its portfolio. Only its members change it.": "You read this project as a manager of its portfolio. Only its members change it.", + "Portfolios": "Portfolios" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/en.json b/l10n/en.json index 296988e2..ac721d2e 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -451,7 +451,23 @@ "No suggestion: the project has no budget.": "No suggestion: the project has no budget.", "No suggestion: no costs are recorded for this project yet.": "No suggestion: no costs are recorded for this project yet.", "The report was not saved. Only the project owner can write status reports.": "The report was not saved. Only the project owner can write status reports.", - "No report": "No report" + "No report": "No report", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.", + "The name of the portfolio": "The name of the portfolio", + "What the portfolio holds and who runs it": "What the portfolio holds and who runs it", + "A colour for the portfolio's section in project lists": "A colour for the portfolio's section in project lists", + "Managers": "Managers", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud user UIDs of the people who run the portfolio", + "Order": "Order", + "Where the portfolio comes in project lists, lowest first": "Where the portfolio comes in project lists, lowest first", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.", + "Portfolio readers": "Portfolio readers", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud user UIDs of the managers of the project's portfolio", + "The portfolio this project belongs to": "The portfolio this project belongs to", + "No portfolio": "No portfolio", + "All portfolios": "All portfolios", + "You read this project as a manager of its portfolio. Only its members change it.": "You read this project as a manager of its portfolio. Only its members change it.", + "Portfolios": "Portfolios" }, "plurals": {}, "pluralForm": "nplurals=2; plural=(n != 1);" diff --git a/l10n/es.js b/l10n/es.js index db456e4b..7129ce5c 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Sin sugerencia: el proyecto no tiene presupuesto.", "No suggestion: no costs are recorded for this project yet.": "Sin sugerencia: aún no hay costes registrados para este proyecto.", "The report was not saved. Only the project owner can write status reports.": "El informe no se ha guardado. Solo el propietario del proyecto puede escribir informes de estado.", - "No report": "Sin informe" + "No report": "Sin informe", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un grupo de proyectos dirigidos por las mismas personas, como un departamento o un programa. Sus responsables leen cada proyecto que contiene.", + "The name of the portfolio": "El nombre de la cartera", + "What the portfolio holds and who runs it": "Qué contiene la cartera y quién la dirige", + "A colour for the portfolio's section in project lists": "Un color para la sección de la cartera en las listas de proyectos", + "Managers": "Responsables", + "Nextcloud user UIDs of the people who run the portfolio": "ID de usuario de Nextcloud de las personas que dirigen la cartera", + "Order": "Orden", + "Where the portfolio comes in project lists, lowest first": "Dónde aparece la cartera en las listas de proyectos, primero el más bajo", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "La escala de riesgo propia de esta cartera en JSON, con la misma forma que la de toda la aplicación. Vacío significa la escala de la aplicación.", + "Portfolio readers": "Lectores de la cartera", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID de usuario de Nextcloud de los responsables de la cartera del proyecto", + "The portfolio this project belongs to": "La cartera a la que pertenece este proyecto", + "No portfolio": "Sin cartera", + "All portfolios": "Todas las carteras", + "You read this project as a manager of its portfolio. Only its members change it.": "Lee este proyecto como responsable de su cartera. Solo sus miembros lo cambian.", + "Portfolios": "Carteras" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/es.json b/l10n/es.json index f2f023d0..0f321e5c 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Sin sugerencia: el proyecto no tiene presupuesto.", "No suggestion: no costs are recorded for this project yet.": "Sin sugerencia: aún no hay costes registrados para este proyecto.", "The report was not saved. Only the project owner can write status reports.": "El informe no se ha guardado. Solo el propietario del proyecto puede escribir informes de estado.", - "No report": "Sin informe" + "No report": "Sin informe", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un grupo de proyectos dirigidos por las mismas personas, como un departamento o un programa. Sus responsables leen cada proyecto que contiene.", + "The name of the portfolio": "El nombre de la cartera", + "What the portfolio holds and who runs it": "Qué contiene la cartera y quién la dirige", + "A colour for the portfolio's section in project lists": "Un color para la sección de la cartera en las listas de proyectos", + "Managers": "Responsables", + "Nextcloud user UIDs of the people who run the portfolio": "ID de usuario de Nextcloud de las personas que dirigen la cartera", + "Order": "Orden", + "Where the portfolio comes in project lists, lowest first": "Dónde aparece la cartera en las listas de proyectos, primero el más bajo", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "La escala de riesgo propia de esta cartera en JSON, con la misma forma que la de toda la aplicación. Vacío significa la escala de la aplicación.", + "Portfolio readers": "Lectores de la cartera", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID de usuario de Nextcloud de los responsables de la cartera del proyecto", + "The portfolio this project belongs to": "La cartera a la que pertenece este proyecto", + "No portfolio": "Sin cartera", + "All portfolios": "Todas las carteras", + "You read this project as a manager of its portfolio. Only its members change it.": "Lee este proyecto como responsable de su cartera. Solo sus miembros lo cambian.", + "Portfolios": "Carteras" }, "plurals": null } diff --git a/l10n/et.js b/l10n/et.js index bd7cf2f3..51276a19 100644 --- a/l10n/et.js +++ b/l10n/et.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Soovitust pole: projektil pole eelarvet.", "No suggestion: no costs are recorded for this project yet.": "Soovitust pole: sellele projektile pole veel kulusid kirjendatud.", "The report was not saved. Only the project owner can write status reports.": "Aruannet ei salvestatud. Olukorra aruandeid saab kirjutada ainult projekti omanik.", - "No report": "Aruanne puudub" + "No report": "Aruanne puudub", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Samade inimeste juhitavate projektide rühm, näiteks osakond või programm. Selle juhid loevad iga selles olevat projekti.", + "The name of the portfolio": "Portfelli nimi", + "What the portfolio holds and who runs it": "Mida portfell sisaldab ja kes seda juhib", + "A colour for the portfolio's section in project lists": "Portfelli jaotise värv projektiloendites", + "Managers": "Juhid", + "Nextcloud user UIDs of the people who run the portfolio": "Portfelli juhtivate inimeste Nextcloudi kasutajatunnused", + "Order": "Järjekord", + "Where the portfolio comes in project lists, lowest first": "Portfelli koht projektiloendites, väikseim eespool", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Selle portfelli oma riskiskaala JSON-ina, samas kujus nagu kogu rakenduse skaala. Tühi tähendab rakenduse skaalat.", + "Portfolio readers": "Portfelli lugejad", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projekti portfelli juhtide Nextcloudi kasutajatunnused", + "The portfolio this project belongs to": "Portfell, kuhu see projekt kuulub", + "No portfolio": "Portfell puudub", + "All portfolios": "Kõik portfellid", + "You read this project as a manager of its portfolio. Only its members change it.": "Loed seda projekti selle portfelli juhina. Muudavad seda ainult selle liikmed.", + "Portfolios": "Portfellid" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/et.json b/l10n/et.json index c8749b32..d071cec2 100644 --- a/l10n/et.json +++ b/l10n/et.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Soovitust pole: projektil pole eelarvet.", "No suggestion: no costs are recorded for this project yet.": "Soovitust pole: sellele projektile pole veel kulusid kirjendatud.", "The report was not saved. Only the project owner can write status reports.": "Aruannet ei salvestatud. Olukorra aruandeid saab kirjutada ainult projekti omanik.", - "No report": "Aruanne puudub" + "No report": "Aruanne puudub", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Samade inimeste juhitavate projektide rühm, näiteks osakond või programm. Selle juhid loevad iga selles olevat projekti.", + "The name of the portfolio": "Portfelli nimi", + "What the portfolio holds and who runs it": "Mida portfell sisaldab ja kes seda juhib", + "A colour for the portfolio's section in project lists": "Portfelli jaotise värv projektiloendites", + "Managers": "Juhid", + "Nextcloud user UIDs of the people who run the portfolio": "Portfelli juhtivate inimeste Nextcloudi kasutajatunnused", + "Order": "Järjekord", + "Where the portfolio comes in project lists, lowest first": "Portfelli koht projektiloendites, väikseim eespool", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Selle portfelli oma riskiskaala JSON-ina, samas kujus nagu kogu rakenduse skaala. Tühi tähendab rakenduse skaalat.", + "Portfolio readers": "Portfelli lugejad", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projekti portfelli juhtide Nextcloudi kasutajatunnused", + "The portfolio this project belongs to": "Portfell, kuhu see projekt kuulub", + "No portfolio": "Portfell puudub", + "All portfolios": "Kõik portfellid", + "You read this project as a manager of its portfolio. Only its members change it.": "Loed seda projekti selle portfelli juhina. Muudavad seda ainult selle liikmed.", + "Portfolios": "Portfellid" }, "plurals": null } diff --git a/l10n/fi.js b/l10n/fi.js index 5c70abe6..5cafc18e 100644 --- a/l10n/fi.js +++ b/l10n/fi.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Ei ehdotusta: projektilla ei ole budjettia.", "No suggestion: no costs are recorded for this project yet.": "Ei ehdotusta: projektille ei ole vielä kirjattu kustannuksia.", "The report was not saved. Only the project owner can write status reports.": "Raporttia ei tallennettu. Vain projektin omistaja voi kirjoittaa tilaraportteja.", - "No report": "Ei raporttia" + "No report": "Ei raporttia", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Samojen ihmisten johtamien projektien ryhmä, kuten osasto tai ohjelma. Sen vastuuhenkilöt lukevat jokaista siihen kuuluvaa projektia.", + "The name of the portfolio": "Salkun nimi", + "What the portfolio holds and who runs it": "Mitä salkku sisältää ja kuka sitä johtaa", + "A colour for the portfolio's section in project lists": "Väri salkun osiolle projektiluetteloissa", + "Managers": "Vastuuhenkilöt", + "Nextcloud user UIDs of the people who run the portfolio": "Salkkua johtavien ihmisten Nextcloud-käyttäjätunnukset", + "Order": "Järjestys", + "Where the portfolio comes in project lists, lowest first": "Salkun paikka projektiluetteloissa, pienin ensin", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Tämän salkun oma riskiasteikko JSON-muodossa, samassa muodossa kuin koko sovelluksen asteikko. Tyhjä tarkoittaa koko sovelluksen asteikkoa.", + "Portfolio readers": "Salkun lukijat", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projektin salkun vastuuhenkilöiden Nextcloud-käyttäjätunnukset", + "The portfolio this project belongs to": "Salkku, johon tämä projekti kuuluu", + "No portfolio": "Ei salkkua", + "All portfolios": "Kaikki salkut", + "You read this project as a manager of its portfolio. Only its members change it.": "Luet tätä projektia sen salkun vastuuhenkilönä. Vain sen jäsenet muuttavat sitä.", + "Portfolios": "Salkut" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/fi.json b/l10n/fi.json index 02d4b1a5..610a7be8 100644 --- a/l10n/fi.json +++ b/l10n/fi.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Ei ehdotusta: projektilla ei ole budjettia.", "No suggestion: no costs are recorded for this project yet.": "Ei ehdotusta: projektille ei ole vielä kirjattu kustannuksia.", "The report was not saved. Only the project owner can write status reports.": "Raporttia ei tallennettu. Vain projektin omistaja voi kirjoittaa tilaraportteja.", - "No report": "Ei raporttia" + "No report": "Ei raporttia", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Samojen ihmisten johtamien projektien ryhmä, kuten osasto tai ohjelma. Sen vastuuhenkilöt lukevat jokaista siihen kuuluvaa projektia.", + "The name of the portfolio": "Salkun nimi", + "What the portfolio holds and who runs it": "Mitä salkku sisältää ja kuka sitä johtaa", + "A colour for the portfolio's section in project lists": "Väri salkun osiolle projektiluetteloissa", + "Managers": "Vastuuhenkilöt", + "Nextcloud user UIDs of the people who run the portfolio": "Salkkua johtavien ihmisten Nextcloud-käyttäjätunnukset", + "Order": "Järjestys", + "Where the portfolio comes in project lists, lowest first": "Salkun paikka projektiluetteloissa, pienin ensin", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Tämän salkun oma riskiasteikko JSON-muodossa, samassa muodossa kuin koko sovelluksen asteikko. Tyhjä tarkoittaa koko sovelluksen asteikkoa.", + "Portfolio readers": "Salkun lukijat", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projektin salkun vastuuhenkilöiden Nextcloud-käyttäjätunnukset", + "The portfolio this project belongs to": "Salkku, johon tämä projekti kuuluu", + "No portfolio": "Ei salkkua", + "All portfolios": "Kaikki salkut", + "You read this project as a manager of its portfolio. Only its members change it.": "Luet tätä projektia sen salkun vastuuhenkilönä. Vain sen jäsenet muuttavat sitä.", + "Portfolios": "Salkut" }, "plurals": null } diff --git a/l10n/fr.js b/l10n/fr.js index 0a61b65f..67b33013 100644 --- a/l10n/fr.js +++ b/l10n/fr.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Pas de suggestion : le projet n'a pas de budget.", "No suggestion: no costs are recorded for this project yet.": "Pas de suggestion : aucun coût n'est encore enregistré pour ce projet.", "The report was not saved. Only the project owner can write status reports.": "Le rapport n'a pas été enregistré. Seul le propriétaire du projet peut rédiger des rapports d'état.", - "No report": "Aucun rapport" + "No report": "Aucun rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un groupe de projets menés par les mêmes personnes, comme un service ou un programme. Ses responsables lisent chaque projet qu'il contient.", + "The name of the portfolio": "Le nom du portefeuille", + "What the portfolio holds and who runs it": "Ce que contient le portefeuille et qui le pilote", + "A colour for the portfolio's section in project lists": "Une couleur pour la section du portefeuille dans les listes de projets", + "Managers": "Responsables", + "Nextcloud user UIDs of the people who run the portfolio": "Identifiants Nextcloud des personnes qui pilotent le portefeuille", + "Order": "Ordre", + "Where the portfolio comes in project lists, lowest first": "La place du portefeuille dans les listes de projets, du plus bas au plus haut", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "L'échelle de risque propre à ce portefeuille, en JSON, de même forme que celle de l'application. Vide signifie l'échelle de l'application.", + "Portfolio readers": "Lecteurs du portefeuille", + "Nextcloud user UIDs of the managers of the project's portfolio": "Identifiants Nextcloud des responsables du portefeuille du projet", + "The portfolio this project belongs to": "Le portefeuille auquel appartient ce projet", + "No portfolio": "Aucun portefeuille", + "All portfolios": "Tous les portefeuilles", + "You read this project as a manager of its portfolio. Only its members change it.": "Vous lisez ce projet en tant que responsable de son portefeuille. Seuls ses membres le modifient.", + "Portfolios": "Portefeuilles" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/fr.json b/l10n/fr.json index f9d28d2d..937cb332 100644 --- a/l10n/fr.json +++ b/l10n/fr.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Pas de suggestion : le projet n'a pas de budget.", "No suggestion: no costs are recorded for this project yet.": "Pas de suggestion : aucun coût n'est encore enregistré pour ce projet.", "The report was not saved. Only the project owner can write status reports.": "Le rapport n'a pas été enregistré. Seul le propriétaire du projet peut rédiger des rapports d'état.", - "No report": "Aucun rapport" + "No report": "Aucun rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un groupe de projets menés par les mêmes personnes, comme un service ou un programme. Ses responsables lisent chaque projet qu'il contient.", + "The name of the portfolio": "Le nom du portefeuille", + "What the portfolio holds and who runs it": "Ce que contient le portefeuille et qui le pilote", + "A colour for the portfolio's section in project lists": "Une couleur pour la section du portefeuille dans les listes de projets", + "Managers": "Responsables", + "Nextcloud user UIDs of the people who run the portfolio": "Identifiants Nextcloud des personnes qui pilotent le portefeuille", + "Order": "Ordre", + "Where the portfolio comes in project lists, lowest first": "La place du portefeuille dans les listes de projets, du plus bas au plus haut", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "L'échelle de risque propre à ce portefeuille, en JSON, de même forme que celle de l'application. Vide signifie l'échelle de l'application.", + "Portfolio readers": "Lecteurs du portefeuille", + "Nextcloud user UIDs of the managers of the project's portfolio": "Identifiants Nextcloud des responsables du portefeuille du projet", + "The portfolio this project belongs to": "Le portefeuille auquel appartient ce projet", + "No portfolio": "Aucun portefeuille", + "All portfolios": "Tous les portefeuilles", + "You read this project as a manager of its portfolio. Only its members change it.": "Vous lisez ce projet en tant que responsable de son portefeuille. Seuls ses membres le modifient.", + "Portfolios": "Portefeuilles" }, "plurals": null } diff --git a/l10n/ga.js b/l10n/ga.js index 54725098..c43b5230 100644 --- a/l10n/ga.js +++ b/l10n/ga.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Gan mholadh: níl buiséad ag an tionscadal.", "No suggestion: no costs are recorded for this project yet.": "Gan mholadh: níl aon chostas taifeadta don tionscadal seo fós.", "The report was not saved. Only the project owner can write status reports.": "Níor sábháladh an tuarascáil. Ní féidir ach le húinéir an tionscadail tuarascálacha stádais a scríobh.", - "No report": "Gan tuarascáil" + "No report": "Gan tuarascáil", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Grúpa tionscadal a reáchtálann na daoine céanna, mar roinn nó clár. Léann a bhainisteoirí gach tionscadal ann.", + "The name of the portfolio": "Ainm na punainne", + "What the portfolio holds and who runs it": "Cad atá sa phunann agus cé a reáchtálann í", + "A colour for the portfolio's section in project lists": "Dath do chuid na punainne i liostaí tionscadal", + "Managers": "Bainisteoirí", + "Nextcloud user UIDs of the people who run the portfolio": "Aitheantais úsáideora Nextcloud na ndaoine a reáchtálann an phunann", + "Order": "Ord", + "Where the portfolio comes in project lists, lowest first": "Áit na punainne i liostaí tionscadal, an ceann is ísle ar dtús", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Scála riosca na punainne seo féin mar JSON, sa chruth céanna le scála an aip ar fad. Ciallaíonn folamh scála an aip.", + "Portfolio readers": "Léitheoirí na punainne", + "Nextcloud user UIDs of the managers of the project's portfolio": "Aitheantais úsáideora Nextcloud bhainisteoirí phunann an tionscadail", + "The portfolio this project belongs to": "An phunann lena mbaineann an tionscadal seo", + "No portfolio": "Gan punann", + "All portfolios": "Gach punann", + "You read this project as a manager of its portfolio. Only its members change it.": "Léann tú an tionscadal seo mar bhainisteoir ar a phunann. Ní athraíonn ach a bhaill é.", + "Portfolios": "Punanna" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ga.json b/l10n/ga.json index 30c55025..8578fa44 100644 --- a/l10n/ga.json +++ b/l10n/ga.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Gan mholadh: níl buiséad ag an tionscadal.", "No suggestion: no costs are recorded for this project yet.": "Gan mholadh: níl aon chostas taifeadta don tionscadal seo fós.", "The report was not saved. Only the project owner can write status reports.": "Níor sábháladh an tuarascáil. Ní féidir ach le húinéir an tionscadail tuarascálacha stádais a scríobh.", - "No report": "Gan tuarascáil" + "No report": "Gan tuarascáil", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Grúpa tionscadal a reáchtálann na daoine céanna, mar roinn nó clár. Léann a bhainisteoirí gach tionscadal ann.", + "The name of the portfolio": "Ainm na punainne", + "What the portfolio holds and who runs it": "Cad atá sa phunann agus cé a reáchtálann í", + "A colour for the portfolio's section in project lists": "Dath do chuid na punainne i liostaí tionscadal", + "Managers": "Bainisteoirí", + "Nextcloud user UIDs of the people who run the portfolio": "Aitheantais úsáideora Nextcloud na ndaoine a reáchtálann an phunann", + "Order": "Ord", + "Where the portfolio comes in project lists, lowest first": "Áit na punainne i liostaí tionscadal, an ceann is ísle ar dtús", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Scála riosca na punainne seo féin mar JSON, sa chruth céanna le scála an aip ar fad. Ciallaíonn folamh scála an aip.", + "Portfolio readers": "Léitheoirí na punainne", + "Nextcloud user UIDs of the managers of the project's portfolio": "Aitheantais úsáideora Nextcloud bhainisteoirí phunann an tionscadail", + "The portfolio this project belongs to": "An phunann lena mbaineann an tionscadal seo", + "No portfolio": "Gan punann", + "All portfolios": "Gach punann", + "You read this project as a manager of its portfolio. Only its members change it.": "Léann tú an tionscadal seo mar bhainisteoir ar a phunann. Ní athraíonn ach a bhaill é.", + "Portfolios": "Punanna" }, "plurals": null } diff --git a/l10n/hr.js b/l10n/hr.js index fb689b69..e6346752 100644 --- a/l10n/hr.js +++ b/l10n/hr.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Nema prijedloga: projekt nema proračun.", "No suggestion: no costs are recorded for this project yet.": "Nema prijedloga: za ovaj projekt još nisu zabilježeni troškovi.", "The report was not saved. Only the project owner can write status reports.": "Izvješće nije spremljeno. Izvješća o stanju može pisati samo vlasnik projekta.", - "No report": "Nema izvješća" + "No report": "Nema izvješća", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Skupina projekata koje vode isti ljudi, na primjer odjel ili program. Njezini voditelji čitaju svaki projekt u njoj.", + "The name of the portfolio": "Naziv portfelja", + "What the portfolio holds and who runs it": "Što portfelj sadrži i tko ga vodi", + "A colour for the portfolio's section in project lists": "Boja odjeljka portfelja u popisima projekata", + "Managers": "Voditelji", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud korisnički ID-ovi ljudi koji vode portfelj", + "Order": "Redoslijed", + "Where the portfolio comes in project lists, lowest first": "Gdje je portfelj u popisima projekata, najniži prvi", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Vlastita ljestvica rizika ovog portfelja kao JSON, u istom obliku kao ljestvica cijele aplikacije. Prazno znači ljestvicu aplikacije.", + "Portfolio readers": "Čitatelji portfelja", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud korisnički ID-ovi voditelja portfelja projekta", + "The portfolio this project belongs to": "Portfelj kojem ovaj projekt pripada", + "No portfolio": "Bez portfelja", + "All portfolios": "Svi portfelji", + "You read this project as a manager of its portfolio. Only its members change it.": "Ovaj projekt čitate kao voditelj njegova portfelja. Mijenjaju ga samo njegovi članovi.", + "Portfolios": "Portfelji" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/hr.json b/l10n/hr.json index f6ee3dca..a77e7d7b 100644 --- a/l10n/hr.json +++ b/l10n/hr.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Nema prijedloga: projekt nema proračun.", "No suggestion: no costs are recorded for this project yet.": "Nema prijedloga: za ovaj projekt još nisu zabilježeni troškovi.", "The report was not saved. Only the project owner can write status reports.": "Izvješće nije spremljeno. Izvješća o stanju može pisati samo vlasnik projekta.", - "No report": "Nema izvješća" + "No report": "Nema izvješća", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Skupina projekata koje vode isti ljudi, na primjer odjel ili program. Njezini voditelji čitaju svaki projekt u njoj.", + "The name of the portfolio": "Naziv portfelja", + "What the portfolio holds and who runs it": "Što portfelj sadrži i tko ga vodi", + "A colour for the portfolio's section in project lists": "Boja odjeljka portfelja u popisima projekata", + "Managers": "Voditelji", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud korisnički ID-ovi ljudi koji vode portfelj", + "Order": "Redoslijed", + "Where the portfolio comes in project lists, lowest first": "Gdje je portfelj u popisima projekata, najniži prvi", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Vlastita ljestvica rizika ovog portfelja kao JSON, u istom obliku kao ljestvica cijele aplikacije. Prazno znači ljestvicu aplikacije.", + "Portfolio readers": "Čitatelji portfelja", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud korisnički ID-ovi voditelja portfelja projekta", + "The portfolio this project belongs to": "Portfelj kojem ovaj projekt pripada", + "No portfolio": "Bez portfelja", + "All portfolios": "Svi portfelji", + "You read this project as a manager of its portfolio. Only its members change it.": "Ovaj projekt čitate kao voditelj njegova portfelja. Mijenjaju ga samo njegovi članovi.", + "Portfolios": "Portfelji" }, "plurals": null } diff --git a/l10n/hu.js b/l10n/hu.js index 07205723..26943415 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Nincs javaslat: a projektnek nincs költségvetése.", "No suggestion: no costs are recorded for this project yet.": "Nincs javaslat: ehhez a projekthez még nincs rögzített költség.", "The report was not saved. Only the project owner can write status reports.": "A jelentés nem lett mentve. Állapotjelentést csak a projekt tulajdonosa írhat.", - "No report": "Nincs jelentés" + "No report": "Nincs jelentés", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Ugyanazon emberek által vezetett projektek csoportja, például egy osztály vagy egy program. A felelősei minden benne lévő projektet olvasnak.", + "The name of the portfolio": "A portfólió neve", + "What the portfolio holds and who runs it": "Mit tartalmaz a portfólió, és ki vezeti", + "A colour for the portfolio's section in project lists": "A portfólió szakaszának színe a projektlistákban", + "Managers": "Felelősök", + "Nextcloud user UIDs of the people who run the portfolio": "A portfóliót vezető személyek Nextcloud-felhasználóazonosítói", + "Order": "Sorrend", + "Where the portfolio comes in project lists, lowest first": "A portfólió helye a projektlistákban, a legkisebb elöl", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "A portfólió saját kockázati skálája JSON-ként, az egész alkalmazás skálájával azonos formában. Üresen az alkalmazás skáláját jelenti.", + "Portfolio readers": "Portfólió-olvasók", + "Nextcloud user UIDs of the managers of the project's portfolio": "A projekt portfóliója felelőseinek Nextcloud-felhasználóazonosítói", + "The portfolio this project belongs to": "A portfólió, amelyhez a projekt tartozik", + "No portfolio": "Nincs portfólió", + "All portfolios": "Összes portfólió", + "You read this project as a manager of its portfolio. Only its members change it.": "Ezt a projektet a portfóliója felelőseként olvassa. Csak a tagjai módosítják.", + "Portfolios": "Portfóliók" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/hu.json b/l10n/hu.json index c3d523db..5f4ba90b 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Nincs javaslat: a projektnek nincs költségvetése.", "No suggestion: no costs are recorded for this project yet.": "Nincs javaslat: ehhez a projekthez még nincs rögzített költség.", "The report was not saved. Only the project owner can write status reports.": "A jelentés nem lett mentve. Állapotjelentést csak a projekt tulajdonosa írhat.", - "No report": "Nincs jelentés" + "No report": "Nincs jelentés", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Ugyanazon emberek által vezetett projektek csoportja, például egy osztály vagy egy program. A felelősei minden benne lévő projektet olvasnak.", + "The name of the portfolio": "A portfólió neve", + "What the portfolio holds and who runs it": "Mit tartalmaz a portfólió, és ki vezeti", + "A colour for the portfolio's section in project lists": "A portfólió szakaszának színe a projektlistákban", + "Managers": "Felelősök", + "Nextcloud user UIDs of the people who run the portfolio": "A portfóliót vezető személyek Nextcloud-felhasználóazonosítói", + "Order": "Sorrend", + "Where the portfolio comes in project lists, lowest first": "A portfólió helye a projektlistákban, a legkisebb elöl", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "A portfólió saját kockázati skálája JSON-ként, az egész alkalmazás skálájával azonos formában. Üresen az alkalmazás skáláját jelenti.", + "Portfolio readers": "Portfólió-olvasók", + "Nextcloud user UIDs of the managers of the project's portfolio": "A projekt portfóliója felelőseinek Nextcloud-felhasználóazonosítói", + "The portfolio this project belongs to": "A portfólió, amelyhez a projekt tartozik", + "No portfolio": "Nincs portfólió", + "All portfolios": "Összes portfólió", + "You read this project as a manager of its portfolio. Only its members change it.": "Ezt a projektet a portfóliója felelőseként olvassa. Csak a tagjai módosítják.", + "Portfolios": "Portfóliók" }, "plurals": null } diff --git a/l10n/is.js b/l10n/is.js index a80eef96..acc623bd 100644 --- a/l10n/is.js +++ b/l10n/is.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Engin tillaga: verkefnið hefur enga fjárhagsáætlun.", "No suggestion: no costs are recorded for this project yet.": "Engin tillaga: enginn kostnaður hefur enn verið skráður á þetta verkefni.", "The report was not saved. Only the project owner can write status reports.": "Skýrslan var ekki vistuð. Aðeins eigandi verkefnisins getur skrifað stöðuskýrslur.", - "No report": "Engin skýrsla" + "No report": "Engin skýrsla", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Hópur verkefna sem sama fólkið stýrir, til dæmis deild eða áætlun. Umsjónarmenn hans lesa hvert verkefni í honum.", + "The name of the portfolio": "Heiti safnsins", + "What the portfolio holds and who runs it": "Hvað safnið inniheldur og hver stýrir því", + "A colour for the portfolio's section in project lists": "Litur fyrir hluta safnsins í verkefnalistum", + "Managers": "Umsjónarmenn", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-notendaauðkenni fólksins sem stýrir safninu", + "Order": "Röð", + "Where the portfolio comes in project lists, lowest first": "Hvar safnið kemur í verkefnalistum, lægst fyrst", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Eigin áhættukvarði þessa safns sem JSON, á sama formi og kvarðinn fyrir allt forritið. Tómt þýðir kvarðinn fyrir allt forritið.", + "Portfolio readers": "Lesendur safns", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-notendaauðkenni umsjónarmanna safns verkefnisins", + "The portfolio this project belongs to": "Safnið sem þetta verkefni tilheyrir", + "No portfolio": "Ekkert safn", + "All portfolios": "Öll söfn", + "You read this project as a manager of its portfolio. Only its members change it.": "Þú lest þetta verkefni sem umsjónarmaður safns þess. Aðeins meðlimir þess breyta því.", + "Portfolios": "Söfn" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/is.json b/l10n/is.json index 3d8d2e6f..12e14dc4 100644 --- a/l10n/is.json +++ b/l10n/is.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Engin tillaga: verkefnið hefur enga fjárhagsáætlun.", "No suggestion: no costs are recorded for this project yet.": "Engin tillaga: enginn kostnaður hefur enn verið skráður á þetta verkefni.", "The report was not saved. Only the project owner can write status reports.": "Skýrslan var ekki vistuð. Aðeins eigandi verkefnisins getur skrifað stöðuskýrslur.", - "No report": "Engin skýrsla" + "No report": "Engin skýrsla", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Hópur verkefna sem sama fólkið stýrir, til dæmis deild eða áætlun. Umsjónarmenn hans lesa hvert verkefni í honum.", + "The name of the portfolio": "Heiti safnsins", + "What the portfolio holds and who runs it": "Hvað safnið inniheldur og hver stýrir því", + "A colour for the portfolio's section in project lists": "Litur fyrir hluta safnsins í verkefnalistum", + "Managers": "Umsjónarmenn", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-notendaauðkenni fólksins sem stýrir safninu", + "Order": "Röð", + "Where the portfolio comes in project lists, lowest first": "Hvar safnið kemur í verkefnalistum, lægst fyrst", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Eigin áhættukvarði þessa safns sem JSON, á sama formi og kvarðinn fyrir allt forritið. Tómt þýðir kvarðinn fyrir allt forritið.", + "Portfolio readers": "Lesendur safns", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-notendaauðkenni umsjónarmanna safns verkefnisins", + "The portfolio this project belongs to": "Safnið sem þetta verkefni tilheyrir", + "No portfolio": "Ekkert safn", + "All portfolios": "Öll söfn", + "You read this project as a manager of its portfolio. Only its members change it.": "Þú lest þetta verkefni sem umsjónarmaður safns þess. Aðeins meðlimir þess breyta því.", + "Portfolios": "Söfn" }, "plurals": {} } diff --git a/l10n/it.js b/l10n/it.js index 4da4f4ce..31bf4a36 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Nessun suggerimento: il progetto non ha un budget.", "No suggestion: no costs are recorded for this project yet.": "Nessun suggerimento: per questo progetto non sono ancora registrati costi.", "The report was not saved. Only the project owner can write status reports.": "Il rapporto non è stato salvato. Solo il proprietario del progetto può scrivere rapporti di stato.", - "No report": "Nessun rapporto" + "No report": "Nessun rapporto", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un gruppo di progetti gestiti dalle stesse persone, come un reparto o un programma. I suoi responsabili leggono ogni progetto che contiene.", + "The name of the portfolio": "Il nome del portafoglio", + "What the portfolio holds and who runs it": "Cosa contiene il portafoglio e chi lo gestisce", + "A colour for the portfolio's section in project lists": "Un colore per la sezione del portafoglio negli elenchi dei progetti", + "Managers": "Responsabili", + "Nextcloud user UIDs of the people who run the portfolio": "ID utente Nextcloud delle persone che gestiscono il portafoglio", + "Order": "Ordine", + "Where the portfolio comes in project lists, lowest first": "La posizione del portafoglio negli elenchi dei progetti, dal più basso", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "La scala di rischio propria di questo portafoglio in JSON, nella stessa forma di quella dell'app. Vuoto significa la scala dell'app.", + "Portfolio readers": "Lettori del portafoglio", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID utente Nextcloud dei responsabili del portafoglio del progetto", + "The portfolio this project belongs to": "Il portafoglio a cui appartiene questo progetto", + "No portfolio": "Nessun portafoglio", + "All portfolios": "Tutti i portafogli", + "You read this project as a manager of its portfolio. Only its members change it.": "Leggi questo progetto come responsabile del suo portafoglio. Solo i membri lo modificano.", + "Portfolios": "Portafogli" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/it.json b/l10n/it.json index 52b3f06c..0bcf4923 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Nessun suggerimento: il progetto non ha un budget.", "No suggestion: no costs are recorded for this project yet.": "Nessun suggerimento: per questo progetto non sono ancora registrati costi.", "The report was not saved. Only the project owner can write status reports.": "Il rapporto non è stato salvato. Solo il proprietario del progetto può scrivere rapporti di stato.", - "No report": "Nessun rapporto" + "No report": "Nessun rapporto", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un gruppo di progetti gestiti dalle stesse persone, come un reparto o un programma. I suoi responsabili leggono ogni progetto che contiene.", + "The name of the portfolio": "Il nome del portafoglio", + "What the portfolio holds and who runs it": "Cosa contiene il portafoglio e chi lo gestisce", + "A colour for the portfolio's section in project lists": "Un colore per la sezione del portafoglio negli elenchi dei progetti", + "Managers": "Responsabili", + "Nextcloud user UIDs of the people who run the portfolio": "ID utente Nextcloud delle persone che gestiscono il portafoglio", + "Order": "Ordine", + "Where the portfolio comes in project lists, lowest first": "La posizione del portafoglio negli elenchi dei progetti, dal più basso", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "La scala di rischio propria di questo portafoglio in JSON, nella stessa forma di quella dell'app. Vuoto significa la scala dell'app.", + "Portfolio readers": "Lettori del portafoglio", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID utente Nextcloud dei responsabili del portafoglio del progetto", + "The portfolio this project belongs to": "Il portafoglio a cui appartiene questo progetto", + "No portfolio": "Nessun portafoglio", + "All portfolios": "Tutti i portafogli", + "You read this project as a manager of its portfolio. Only its members change it.": "Leggi questo progetto come responsabile del suo portafoglio. Solo i membri lo modificano.", + "Portfolios": "Portafogli" }, "plurals": null } diff --git a/l10n/lb.js b/l10n/lb.js index 411cc28b..ffe127c0 100644 --- a/l10n/lb.js +++ b/l10n/lb.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Kee Virschlag: de Projet huet kee Budget.", "No suggestion: no costs are recorded for this project yet.": "Kee Virschlag: fir dëse Projet sinn nach keng Käschten erfaasst.", "The report was not saved. Only the project owner can write status reports.": "De Bericht gouf net gespäichert. Nëmmen de Proprietär vum Projet kann Statusberichter schreiwen.", - "No report": "Kee Bericht" + "No report": "Kee Bericht", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Eng Grupp vu Projeten, déi vun deene selwechte Leit gefouert ginn, zum Beispill eng Abteilung oder e Programm. Seng Responsabel liesen all Projet dran.", + "The name of the portfolio": "Den Numm vum Portfolio", + "What the portfolio holds and who runs it": "Wat am Portfolio ass a wien et féiert", + "A colour for the portfolio's section in project lists": "Eng Faarf fir d'Sektioun vum Portfolio a Projetlëschten", + "Managers": "Responsabel", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-Benotzer-IDe vun de Leit, déi de Portfolio féieren", + "Order": "Reiefolleg", + "Where the portfolio comes in project lists, lowest first": "Wou de Portfolio a Projetlëschte steet, déi niddregst als éischt", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Déi eege Risikoskala vun dësem Portfolio als JSON, an der selwechter Form wéi déi fir déi ganz App. Eidel heescht d'Skala vun der App.", + "Portfolio readers": "Portfolio-Lieser", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-Benotzer-IDe vun de Responsabele vum Portfolio vum Projet", + "The portfolio this project belongs to": "De Portfolio, zu deem dëse Projet gehéiert", + "No portfolio": "Kee Portfolio", + "All portfolios": "All Portfolioen", + "You read this project as a manager of its portfolio. Only its members change it.": "Dir liest dëse Projet als Responsabel vu sengem Portfolio. Nëmme seng Memberen änneren en.", + "Portfolios": "Portfolioen" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lb.json b/l10n/lb.json index fb5144f6..ec8ad94d 100644 --- a/l10n/lb.json +++ b/l10n/lb.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Kee Virschlag: de Projet huet kee Budget.", "No suggestion: no costs are recorded for this project yet.": "Kee Virschlag: fir dëse Projet sinn nach keng Käschten erfaasst.", "The report was not saved. Only the project owner can write status reports.": "De Bericht gouf net gespäichert. Nëmmen de Proprietär vum Projet kann Statusberichter schreiwen.", - "No report": "Kee Bericht" + "No report": "Kee Bericht", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Eng Grupp vu Projeten, déi vun deene selwechte Leit gefouert ginn, zum Beispill eng Abteilung oder e Programm. Seng Responsabel liesen all Projet dran.", + "The name of the portfolio": "Den Numm vum Portfolio", + "What the portfolio holds and who runs it": "Wat am Portfolio ass a wien et féiert", + "A colour for the portfolio's section in project lists": "Eng Faarf fir d'Sektioun vum Portfolio a Projetlëschten", + "Managers": "Responsabel", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-Benotzer-IDe vun de Leit, déi de Portfolio féieren", + "Order": "Reiefolleg", + "Where the portfolio comes in project lists, lowest first": "Wou de Portfolio a Projetlëschte steet, déi niddregst als éischt", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Déi eege Risikoskala vun dësem Portfolio als JSON, an der selwechter Form wéi déi fir déi ganz App. Eidel heescht d'Skala vun der App.", + "Portfolio readers": "Portfolio-Lieser", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-Benotzer-IDe vun de Responsabele vum Portfolio vum Projet", + "The portfolio this project belongs to": "De Portfolio, zu deem dëse Projet gehéiert", + "No portfolio": "Kee Portfolio", + "All portfolios": "All Portfolioen", + "You read this project as a manager of its portfolio. Only its members change it.": "Dir liest dëse Projet als Responsabel vu sengem Portfolio. Nëmme seng Memberen änneren en.", + "Portfolios": "Portfolioen" }, "plurals": null } diff --git a/l10n/lt.js b/l10n/lt.js index 182fd045..546eb7b5 100644 --- a/l10n/lt.js +++ b/l10n/lt.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Pasiūlymo nėra: projektas neturi biudžeto.", "No suggestion: no costs are recorded for this project yet.": "Pasiūlymo nėra: šiam projektui dar neužregistruota išlaidų.", "The report was not saved. Only the project owner can write status reports.": "Ataskaita neišsaugota. Būklės ataskaitas gali rašyti tik projekto savininkas.", - "No report": "Ataskaitos nėra" + "No report": "Ataskaitos nėra", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Projektų grupė, kuriai vadovauja tie patys žmonės, pavyzdžiui, skyrius ar programa. Jos vadovai skaito kiekvieną joje esantį projektą.", + "The name of the portfolio": "Portfelio pavadinimas", + "What the portfolio holds and who runs it": "Ką apima portfelis ir kas jam vadovauja", + "A colour for the portfolio's section in project lists": "Portfelio skilties spalva projektų sąrašuose", + "Managers": "Vadovai", + "Nextcloud user UIDs of the people who run the portfolio": "Portfeliui vadovaujančių žmonių Nextcloud naudotojų ID", + "Order": "Tvarka", + "Where the portfolio comes in project lists, lowest first": "Portfelio vieta projektų sąrašuose, mažiausias pirmas", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Šio portfelio nuosava rizikos skalė JSON formatu, tokios pat formos kaip visos programos skalė. Tuščia reiškia programos skalę.", + "Portfolio readers": "Portfelio skaitytojai", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projekto portfelio vadovų Nextcloud naudotojų ID", + "The portfolio this project belongs to": "Portfelis, kuriam priklauso šis projektas", + "No portfolio": "Be portfelio", + "All portfolios": "Visi portfeliai", + "You read this project as a manager of its portfolio. Only its members change it.": "Šį projektą skaitote kaip jo portfelio vadovas. Jį keičia tik jo nariai.", + "Portfolios": "Portfeliai" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lt.json b/l10n/lt.json index 94339cf1..73d08c54 100644 --- a/l10n/lt.json +++ b/l10n/lt.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Pasiūlymo nėra: projektas neturi biudžeto.", "No suggestion: no costs are recorded for this project yet.": "Pasiūlymo nėra: šiam projektui dar neužregistruota išlaidų.", "The report was not saved. Only the project owner can write status reports.": "Ataskaita neišsaugota. Būklės ataskaitas gali rašyti tik projekto savininkas.", - "No report": "Ataskaitos nėra" + "No report": "Ataskaitos nėra", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Projektų grupė, kuriai vadovauja tie patys žmonės, pavyzdžiui, skyrius ar programa. Jos vadovai skaito kiekvieną joje esantį projektą.", + "The name of the portfolio": "Portfelio pavadinimas", + "What the portfolio holds and who runs it": "Ką apima portfelis ir kas jam vadovauja", + "A colour for the portfolio's section in project lists": "Portfelio skilties spalva projektų sąrašuose", + "Managers": "Vadovai", + "Nextcloud user UIDs of the people who run the portfolio": "Portfeliui vadovaujančių žmonių Nextcloud naudotojų ID", + "Order": "Tvarka", + "Where the portfolio comes in project lists, lowest first": "Portfelio vieta projektų sąrašuose, mažiausias pirmas", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Šio portfelio nuosava rizikos skalė JSON formatu, tokios pat formos kaip visos programos skalė. Tuščia reiškia programos skalę.", + "Portfolio readers": "Portfelio skaitytojai", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projekto portfelio vadovų Nextcloud naudotojų ID", + "The portfolio this project belongs to": "Portfelis, kuriam priklauso šis projektas", + "No portfolio": "Be portfelio", + "All portfolios": "Visi portfeliai", + "You read this project as a manager of its portfolio. Only its members change it.": "Šį projektą skaitote kaip jo portfelio vadovas. Jį keičia tik jo nariai.", + "Portfolios": "Portfeliai" }, "plurals": null } diff --git a/l10n/lv.js b/l10n/lv.js index 3ddd9ad2..56cf03a0 100644 --- a/l10n/lv.js +++ b/l10n/lv.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Nav ieteikuma: projektam nav budžeta.", "No suggestion: no costs are recorded for this project yet.": "Nav ieteikuma: šim projektam vēl nav reģistrētu izmaksu.", "The report was not saved. Only the project owner can write status reports.": "Ziņojums netika saglabāts. Statusa ziņojumus var rakstīt tikai projekta īpašnieks.", - "No report": "Nav ziņojuma" + "No report": "Nav ziņojuma", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Projektu grupa, ko vada tie paši cilvēki, piemēram, nodaļa vai programma. Tās vadītāji lasa katru tajā esošo projektu.", + "The name of the portfolio": "Portfeļa nosaukums", + "What the portfolio holds and who runs it": "Ko portfelis ietver un kas to vada", + "A colour for the portfolio's section in project lists": "Krāsa portfeļa sadaļai projektu sarakstos", + "Managers": "Vadītāji", + "Nextcloud user UIDs of the people who run the portfolio": "To cilvēku Nextcloud lietotāju ID, kuri vada portfeli", + "Order": "Secība", + "Where the portfolio comes in project lists, lowest first": "Portfeļa vieta projektu sarakstos, mazākais vispirms", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Šī portfeļa paša riska skala JSON formātā, tādā pašā formā kā visas lietotnes skala. Tukšs nozīmē lietotnes skalu.", + "Portfolio readers": "Portfeļa lasītāji", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projekta portfeļa vadītāju Nextcloud lietotāju ID", + "The portfolio this project belongs to": "Portfelis, kuram pieder šis projekts", + "No portfolio": "Nav portfeļa", + "All portfolios": "Visi portfeļi", + "You read this project as a manager of its portfolio. Only its members change it.": "Jūs lasāt šo projektu kā tā portfeļa vadītājs. To maina tikai tā dalībnieki.", + "Portfolios": "Portfeļi" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/lv.json b/l10n/lv.json index 0b3b74d4..806274e3 100644 --- a/l10n/lv.json +++ b/l10n/lv.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Nav ieteikuma: projektam nav budžeta.", "No suggestion: no costs are recorded for this project yet.": "Nav ieteikuma: šim projektam vēl nav reģistrētu izmaksu.", "The report was not saved. Only the project owner can write status reports.": "Ziņojums netika saglabāts. Statusa ziņojumus var rakstīt tikai projekta īpašnieks.", - "No report": "Nav ziņojuma" + "No report": "Nav ziņojuma", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Projektu grupa, ko vada tie paši cilvēki, piemēram, nodaļa vai programma. Tās vadītāji lasa katru tajā esošo projektu.", + "The name of the portfolio": "Portfeļa nosaukums", + "What the portfolio holds and who runs it": "Ko portfelis ietver un kas to vada", + "A colour for the portfolio's section in project lists": "Krāsa portfeļa sadaļai projektu sarakstos", + "Managers": "Vadītāji", + "Nextcloud user UIDs of the people who run the portfolio": "To cilvēku Nextcloud lietotāju ID, kuri vada portfeli", + "Order": "Secība", + "Where the portfolio comes in project lists, lowest first": "Portfeļa vieta projektu sarakstos, mazākais vispirms", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Šī portfeļa paša riska skala JSON formātā, tādā pašā formā kā visas lietotnes skala. Tukšs nozīmē lietotnes skalu.", + "Portfolio readers": "Portfeļa lasītāji", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projekta portfeļa vadītāju Nextcloud lietotāju ID", + "The portfolio this project belongs to": "Portfelis, kuram pieder šis projekts", + "No portfolio": "Nav portfeļa", + "All portfolios": "Visi portfeļi", + "You read this project as a manager of its portfolio. Only its members change it.": "Jūs lasāt šo projektu kā tā portfeļa vadītājs. To maina tikai tā dalībnieki.", + "Portfolios": "Portfeļi" }, "plurals": null } diff --git a/l10n/mk.js b/l10n/mk.js index 1cb34a09..33cc65ad 100644 --- a/l10n/mk.js +++ b/l10n/mk.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Нема предлог: проектот нема буџет.", "No suggestion: no costs are recorded for this project yet.": "Нема предлог: за овој проект сè уште нема евидентирани трошоци.", "The report was not saved. Only the project owner can write status reports.": "Извештајот не е зачуван. Само сопственикот на проектот може да пишува извештаи за состојбата.", - "No report": "Нема извештај" + "No report": "Нема извештај", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група проекти што ги водат истите луѓе, на пример оддел или програма. Нејзините раководители го читаат секој проект во неа.", + "The name of the portfolio": "Име на портфолиото", + "What the portfolio holds and who runs it": "Што содржи портфолиото и кој го води", + "A colour for the portfolio's section in project lists": "Боја за делот на портфолиото во листите на проекти", + "Managers": "Раководители", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud кориснички ID на луѓето што го водат портфолиото", + "Order": "Редослед", + "Where the portfolio comes in project lists, lowest first": "Каде е портфолиото во листите на проекти, најнискиот прв", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Сопствената скала на ризик на ова портфолио како JSON, во истата форма како скалата на целата апликација. Празно значи скалата на апликацијата.", + "Portfolio readers": "Читатели на портфолиото", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud кориснички ID на раководителите на портфолиото на проектот", + "The portfolio this project belongs to": "Портфолиото на кое му припаѓа проектот", + "No portfolio": "Без портфолио", + "All portfolios": "Сите портфолија", + "You read this project as a manager of its portfolio. Only its members change it.": "Го читате овој проект како раководител на неговото портфолио. Го менуваат само неговите членови.", + "Portfolios": "Портфолија" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/mk.json b/l10n/mk.json index ca3df54e..970ea43a 100644 --- a/l10n/mk.json +++ b/l10n/mk.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Нема предлог: проектот нема буџет.", "No suggestion: no costs are recorded for this project yet.": "Нема предлог: за овој проект сè уште нема евидентирани трошоци.", "The report was not saved. Only the project owner can write status reports.": "Извештајот не е зачуван. Само сопственикот на проектот може да пишува извештаи за состојбата.", - "No report": "Нема извештај" + "No report": "Нема извештај", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група проекти што ги водат истите луѓе, на пример оддел или програма. Нејзините раководители го читаат секој проект во неа.", + "The name of the portfolio": "Име на портфолиото", + "What the portfolio holds and who runs it": "Што содржи портфолиото и кој го води", + "A colour for the portfolio's section in project lists": "Боја за делот на портфолиото во листите на проекти", + "Managers": "Раководители", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud кориснички ID на луѓето што го водат портфолиото", + "Order": "Редослед", + "Where the portfolio comes in project lists, lowest first": "Каде е портфолиото во листите на проекти, најнискиот прв", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Сопствената скала на ризик на ова портфолио како JSON, во истата форма како скалата на целата апликација. Празно значи скалата на апликацијата.", + "Portfolio readers": "Читатели на портфолиото", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud кориснички ID на раководителите на портфолиото на проектот", + "The portfolio this project belongs to": "Портфолиото на кое му припаѓа проектот", + "No portfolio": "Без портфолио", + "All portfolios": "Сите портфолија", + "You read this project as a manager of its portfolio. Only its members change it.": "Го читате овој проект како раководител на неговото портфолио. Го менуваат само неговите членови.", + "Portfolios": "Портфолија" }, "plurals": null } diff --git a/l10n/mt.js b/l10n/mt.js index 97ec6968..758ba8eb 100644 --- a/l10n/mt.js +++ b/l10n/mt.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "L-ebda suġġeriment: il-proġett m'għandux baġit.", "No suggestion: no costs are recorded for this project yet.": "L-ebda suġġeriment: għad m'hemm l-ebda spiża rreġistrata għal dan il-proġett.", "The report was not saved. Only the project owner can write status reports.": "Ir-rapport ma ġiex issejvjat. Is-sid tal-proġett biss jista' jikteb rapporti tal-istat.", - "No report": "L-ebda rapport" + "No report": "L-ebda rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Grupp ta' proġetti mmexxija mill-istess nies, bħal dipartiment jew programm. Il-maniġers tiegħu jaqraw kull proġett fih.", + "The name of the portfolio": "L-isem tal-portafoll", + "What the portfolio holds and who runs it": "X'fih il-portafoll u min imexxih", + "A colour for the portfolio's section in project lists": "Kulur għas-sezzjoni tal-portafoll fil-listi tal-proġetti", + "Managers": "Maniġers", + "Nextcloud user UIDs of the people who run the portfolio": "L-IDs tal-utenti ta' Nextcloud tan-nies li jmexxu l-portafoll", + "Order": "Ordni", + "Where the portfolio comes in project lists, lowest first": "Fejn jidher il-portafoll fil-listi tal-proġetti, l-iżgħar l-ewwel", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "L-iskala tar-riskju proprja ta' dan il-portafoll bħala JSON, fl-istess forma bħal dik tal-app kollha. Vojt ifisser l-iskala tal-app.", + "Portfolio readers": "Qarrejja tal-portafoll", + "Nextcloud user UIDs of the managers of the project's portfolio": "L-IDs tal-utenti ta' Nextcloud tal-maniġers tal-portafoll tal-proġett", + "The portfolio this project belongs to": "Il-portafoll li jappartjeni għalih dan il-proġett", + "No portfolio": "L-ebda portafoll", + "All portfolios": "Il-portafolli kollha", + "You read this project as a manager of its portfolio. Only its members change it.": "Qed taqra dan il-proġett bħala maniġer tal-portafoll tiegħu. Il-membri tiegħu biss ibiddluh.", + "Portfolios": "Portafolli" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/mt.json b/l10n/mt.json index bb7328ff..4a88b281 100644 --- a/l10n/mt.json +++ b/l10n/mt.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "L-ebda suġġeriment: il-proġett m'għandux baġit.", "No suggestion: no costs are recorded for this project yet.": "L-ebda suġġeriment: għad m'hemm l-ebda spiża rreġistrata għal dan il-proġett.", "The report was not saved. Only the project owner can write status reports.": "Ir-rapport ma ġiex issejvjat. Is-sid tal-proġett biss jista' jikteb rapporti tal-istat.", - "No report": "L-ebda rapport" + "No report": "L-ebda rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Grupp ta' proġetti mmexxija mill-istess nies, bħal dipartiment jew programm. Il-maniġers tiegħu jaqraw kull proġett fih.", + "The name of the portfolio": "L-isem tal-portafoll", + "What the portfolio holds and who runs it": "X'fih il-portafoll u min imexxih", + "A colour for the portfolio's section in project lists": "Kulur għas-sezzjoni tal-portafoll fil-listi tal-proġetti", + "Managers": "Maniġers", + "Nextcloud user UIDs of the people who run the portfolio": "L-IDs tal-utenti ta' Nextcloud tan-nies li jmexxu l-portafoll", + "Order": "Ordni", + "Where the portfolio comes in project lists, lowest first": "Fejn jidher il-portafoll fil-listi tal-proġetti, l-iżgħar l-ewwel", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "L-iskala tar-riskju proprja ta' dan il-portafoll bħala JSON, fl-istess forma bħal dik tal-app kollha. Vojt ifisser l-iskala tal-app.", + "Portfolio readers": "Qarrejja tal-portafoll", + "Nextcloud user UIDs of the managers of the project's portfolio": "L-IDs tal-utenti ta' Nextcloud tal-maniġers tal-portafoll tal-proġett", + "The portfolio this project belongs to": "Il-portafoll li jappartjeni għalih dan il-proġett", + "No portfolio": "L-ebda portafoll", + "All portfolios": "Il-portafolli kollha", + "You read this project as a manager of its portfolio. Only its members change it.": "Qed taqra dan il-proġett bħala maniġer tal-portafoll tiegħu. Il-membri tiegħu biss ibiddluh.", + "Portfolios": "Portafolli" }, "plurals": {} } diff --git a/l10n/nb.js b/l10n/nb.js index 9224c28f..fda96eb2 100644 --- a/l10n/nb.js +++ b/l10n/nb.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Ikke noe forslag: prosjektet har ikke budsjett.", "No suggestion: no costs are recorded for this project yet.": "Ikke noe forslag: det er ennå ikke registrert kostnader for dette prosjektet.", "The report was not saved. Only the project owner can write status reports.": "Rapporten ble ikke lagret. Bare prosjekteieren kan skrive statusrapporter.", - "No report": "Ingen rapport" + "No report": "Ingen rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "En gruppe prosjekter som drives av de samme personene, for eksempel en avdeling eller et program. De ansvarlige leser hvert prosjekt i den.", + "The name of the portfolio": "Navnet på porteføljen", + "What the portfolio holds and who runs it": "Hva porteføljen inneholder og hvem som driver den", + "A colour for the portfolio's section in project lists": "En farge for porteføljens del i prosjektlister", + "Managers": "Ansvarlige", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-bruker-ID-er for personene som driver porteføljen", + "Order": "Rekkefølge", + "Where the portfolio comes in project lists, lowest first": "Hvor porteføljen står i prosjektlister, laveste først", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Porteføljens egen risikoskala som JSON, i samme form som skalaen for hele appen. Tom betyr skalaen for hele appen.", + "Portfolio readers": "Porteføljelesere", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-bruker-ID-er for de ansvarlige for prosjektets portefølje", + "The portfolio this project belongs to": "Porteføljen dette prosjektet hører til", + "No portfolio": "Ingen portefølje", + "All portfolios": "Alle porteføljer", + "You read this project as a manager of its portfolio. Only its members change it.": "Du leser dette prosjektet som ansvarlig for porteføljen. Bare medlemmene endrer det.", + "Portfolios": "Porteføljer" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nb.json b/l10n/nb.json index b0ff2dc9..8401af54 100644 --- a/l10n/nb.json +++ b/l10n/nb.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Ikke noe forslag: prosjektet har ikke budsjett.", "No suggestion: no costs are recorded for this project yet.": "Ikke noe forslag: det er ennå ikke registrert kostnader for dette prosjektet.", "The report was not saved. Only the project owner can write status reports.": "Rapporten ble ikke lagret. Bare prosjekteieren kan skrive statusrapporter.", - "No report": "Ingen rapport" + "No report": "Ingen rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "En gruppe prosjekter som drives av de samme personene, for eksempel en avdeling eller et program. De ansvarlige leser hvert prosjekt i den.", + "The name of the portfolio": "Navnet på porteføljen", + "What the portfolio holds and who runs it": "Hva porteføljen inneholder og hvem som driver den", + "A colour for the portfolio's section in project lists": "En farge for porteføljens del i prosjektlister", + "Managers": "Ansvarlige", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-bruker-ID-er for personene som driver porteføljen", + "Order": "Rekkefølge", + "Where the portfolio comes in project lists, lowest first": "Hvor porteføljen står i prosjektlister, laveste først", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Porteføljens egen risikoskala som JSON, i samme form som skalaen for hele appen. Tom betyr skalaen for hele appen.", + "Portfolio readers": "Porteføljelesere", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-bruker-ID-er for de ansvarlige for prosjektets portefølje", + "The portfolio this project belongs to": "Porteføljen dette prosjektet hører til", + "No portfolio": "Ingen portefølje", + "All portfolios": "Alle porteføljer", + "You read this project as a manager of its portfolio. Only its members change it.": "Du leser dette prosjektet som ansvarlig for porteføljen. Bare medlemmene endrer det.", + "Portfolios": "Porteføljer" }, "plurals": {} } diff --git a/l10n/nl.js b/l10n/nl.js index e4572e16..44cec213 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -497,7 +497,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Geen voorstel: het project heeft geen budget.", "No suggestion: no costs are recorded for this project yet.": "Geen voorstel: er zijn nog geen kosten geboekt voor dit project.", "The report was not saved. Only the project owner can write status reports.": "De rapportage is niet opgeslagen. Alleen de projecteigenaar kan statusrapportages schrijven.", - "No report": "Geen rapportage" + "No report": "Geen rapportage", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Een groep projecten die door dezelfde mensen wordt gerund, zoals een afdeling of een programma. De beheerders lezen elk project erin.", + "The name of the portfolio": "De naam van het portfolio", + "What the portfolio holds and who runs it": "Wat het portfolio bevat en wie het runt", + "A colour for the portfolio's section in project lists": "Een kleur voor de sectie van het portfolio in projectlijsten", + "Managers": "Beheerders", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-gebruikers-ID's van de mensen die het portfolio runnen", + "Order": "Volgorde", + "Where the portfolio comes in project lists, lowest first": "Waar het portfolio in projectlijsten staat, laagste eerst", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "De eigen risicoschaal van dit portfolio als JSON, in dezelfde vorm als de schaal voor de hele app. Leeg betekent de schaal voor de hele app.", + "Portfolio readers": "Portfoliolezers", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-gebruikers-ID's van de beheerders van het portfolio van het project", + "The portfolio this project belongs to": "Het portfolio waar dit project bij hoort", + "No portfolio": "Geen portfolio", + "All portfolios": "Alle portfolio's", + "You read this project as a manager of its portfolio. Only its members change it.": "Je leest dit project als beheerder van het portfolio. Alleen de leden wijzigen het.", + "Portfolios": "Portfolio's" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nl.json b/l10n/nl.json index 796ce02a..5f289f09 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -496,7 +496,23 @@ "No suggestion: the project has no budget.": "Geen voorstel: het project heeft geen budget.", "No suggestion: no costs are recorded for this project yet.": "Geen voorstel: er zijn nog geen kosten geboekt voor dit project.", "The report was not saved. Only the project owner can write status reports.": "De rapportage is niet opgeslagen. Alleen de projecteigenaar kan statusrapportages schrijven.", - "No report": "Geen rapportage" + "No report": "Geen rapportage", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Een groep projecten die door dezelfde mensen wordt gerund, zoals een afdeling of een programma. De beheerders lezen elk project erin.", + "The name of the portfolio": "De naam van het portfolio", + "What the portfolio holds and who runs it": "Wat het portfolio bevat en wie het runt", + "A colour for the portfolio's section in project lists": "Een kleur voor de sectie van het portfolio in projectlijsten", + "Managers": "Beheerders", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-gebruikers-ID's van de mensen die het portfolio runnen", + "Order": "Volgorde", + "Where the portfolio comes in project lists, lowest first": "Waar het portfolio in projectlijsten staat, laagste eerst", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "De eigen risicoschaal van dit portfolio als JSON, in dezelfde vorm als de schaal voor de hele app. Leeg betekent de schaal voor de hele app.", + "Portfolio readers": "Portfoliolezers", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-gebruikers-ID's van de beheerders van het portfolio van het project", + "The portfolio this project belongs to": "Het portfolio waar dit project bij hoort", + "No portfolio": "Geen portfolio", + "All portfolios": "Alle portfolio's", + "You read this project as a manager of its portfolio. Only its members change it.": "Je leest dit project als beheerder van het portfolio. Alleen de leden wijzigen het.", + "Portfolios": "Portfolio's" }, "plurals": {}, "pluralForm": "nplurals=2; plural=(n != 1);" diff --git a/l10n/pl.js b/l10n/pl.js index 7a4b2b06..f3e6a385 100644 --- a/l10n/pl.js +++ b/l10n/pl.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Brak sugestii: projekt nie ma budżetu.", "No suggestion: no costs are recorded for this project yet.": "Brak sugestii: dla tego projektu nie zarejestrowano jeszcze kosztów.", "The report was not saved. Only the project owner can write status reports.": "Raport nie został zapisany. Tylko właściciel projektu może pisać raporty stanu.", - "No report": "Brak raportu" + "No report": "Brak raportu", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Grupa projektów prowadzonych przez te same osoby, na przykład dział lub program. Jej kierownicy czytają każdy projekt w niej.", + "The name of the portfolio": "Nazwa portfela", + "What the portfolio holds and who runs it": "Co zawiera portfel i kto go prowadzi", + "A colour for the portfolio's section in project lists": "Kolor sekcji portfela na listach projektów", + "Managers": "Kierownicy", + "Nextcloud user UIDs of the people who run the portfolio": "Identyfikatory użytkowników Nextcloud osób prowadzących portfel", + "Order": "Kolejność", + "Where the portfolio comes in project lists, lowest first": "Miejsce portfela na listach projektów, od najniższego", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Własna skala ryzyka tego portfela w formacie JSON, w tej samej postaci co skala całej aplikacji. Puste oznacza skalę aplikacji.", + "Portfolio readers": "Czytelnicy portfela", + "Nextcloud user UIDs of the managers of the project's portfolio": "Identyfikatory użytkowników Nextcloud kierowników portfela projektu", + "The portfolio this project belongs to": "Portfel, do którego należy ten projekt", + "No portfolio": "Bez portfela", + "All portfolios": "Wszystkie portfele", + "You read this project as a manager of its portfolio. Only its members change it.": "Czytasz ten projekt jako kierownik jego portfela. Zmieniają go tylko jego członkowie.", + "Portfolios": "Portfele" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/pl.json b/l10n/pl.json index f5840bf3..25707fa8 100644 --- a/l10n/pl.json +++ b/l10n/pl.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Brak sugestii: projekt nie ma budżetu.", "No suggestion: no costs are recorded for this project yet.": "Brak sugestii: dla tego projektu nie zarejestrowano jeszcze kosztów.", "The report was not saved. Only the project owner can write status reports.": "Raport nie został zapisany. Tylko właściciel projektu może pisać raporty stanu.", - "No report": "Brak raportu" + "No report": "Brak raportu", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Grupa projektów prowadzonych przez te same osoby, na przykład dział lub program. Jej kierownicy czytają każdy projekt w niej.", + "The name of the portfolio": "Nazwa portfela", + "What the portfolio holds and who runs it": "Co zawiera portfel i kto go prowadzi", + "A colour for the portfolio's section in project lists": "Kolor sekcji portfela na listach projektów", + "Managers": "Kierownicy", + "Nextcloud user UIDs of the people who run the portfolio": "Identyfikatory użytkowników Nextcloud osób prowadzących portfel", + "Order": "Kolejność", + "Where the portfolio comes in project lists, lowest first": "Miejsce portfela na listach projektów, od najniższego", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Własna skala ryzyka tego portfela w formacie JSON, w tej samej postaci co skala całej aplikacji. Puste oznacza skalę aplikacji.", + "Portfolio readers": "Czytelnicy portfela", + "Nextcloud user UIDs of the managers of the project's portfolio": "Identyfikatory użytkowników Nextcloud kierowników portfela projektu", + "The portfolio this project belongs to": "Portfel, do którego należy ten projekt", + "No portfolio": "Bez portfela", + "All portfolios": "Wszystkie portfele", + "You read this project as a manager of its portfolio. Only its members change it.": "Czytasz ten projekt jako kierownik jego portfela. Zmieniają go tylko jego członkowie.", + "Portfolios": "Portfele" }, "plurals": {} } diff --git a/l10n/pt.js b/l10n/pt.js index 483dd060..b6833fe7 100644 --- a/l10n/pt.js +++ b/l10n/pt.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Sem sugestão: o projeto não tem orçamento.", "No suggestion: no costs are recorded for this project yet.": "Sem sugestão: ainda não há custos registados para este projeto.", "The report was not saved. Only the project owner can write status reports.": "O relatório não foi guardado. Só o proprietário do projeto pode escrever relatórios de estado.", - "No report": "Sem relatório" + "No report": "Sem relatório", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Um grupo de projetos geridos pelas mesmas pessoas, como um departamento ou um programa. Os seus gestores leem todos os projetos que contém.", + "The name of the portfolio": "O nome do portefólio", + "What the portfolio holds and who runs it": "O que o portefólio contém e quem o gere", + "A colour for the portfolio's section in project lists": "Uma cor para a secção do portefólio nas listas de projetos", + "Managers": "Gestores", + "Nextcloud user UIDs of the people who run the portfolio": "IDs de utilizador Nextcloud das pessoas que gerem o portefólio", + "Order": "Ordem", + "Where the portfolio comes in project lists, lowest first": "Onde o portefólio aparece nas listas de projetos, o mais baixo primeiro", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "A escala de risco própria deste portefólio em JSON, com a mesma forma da escala da aplicação. Vazio significa a escala da aplicação.", + "Portfolio readers": "Leitores do portefólio", + "Nextcloud user UIDs of the managers of the project's portfolio": "IDs de utilizador Nextcloud dos gestores do portefólio do projeto", + "The portfolio this project belongs to": "O portefólio a que este projeto pertence", + "No portfolio": "Sem portefólio", + "All portfolios": "Todos os portefólios", + "You read this project as a manager of its portfolio. Only its members change it.": "Lê este projeto como gestor do seu portefólio. Só os membros o alteram.", + "Portfolios": "Portefólios" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/pt.json b/l10n/pt.json index 149435d2..e912118b 100644 --- a/l10n/pt.json +++ b/l10n/pt.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Sem sugestão: o projeto não tem orçamento.", "No suggestion: no costs are recorded for this project yet.": "Sem sugestão: ainda não há custos registados para este projeto.", "The report was not saved. Only the project owner can write status reports.": "O relatório não foi guardado. Só o proprietário do projeto pode escrever relatórios de estado.", - "No report": "Sem relatório" + "No report": "Sem relatório", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Um grupo de projetos geridos pelas mesmas pessoas, como um departamento ou um programa. Os seus gestores leem todos os projetos que contém.", + "The name of the portfolio": "O nome do portefólio", + "What the portfolio holds and who runs it": "O que o portefólio contém e quem o gere", + "A colour for the portfolio's section in project lists": "Uma cor para a secção do portefólio nas listas de projetos", + "Managers": "Gestores", + "Nextcloud user UIDs of the people who run the portfolio": "IDs de utilizador Nextcloud das pessoas que gerem o portefólio", + "Order": "Ordem", + "Where the portfolio comes in project lists, lowest first": "Onde o portefólio aparece nas listas de projetos, o mais baixo primeiro", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "A escala de risco própria deste portefólio em JSON, com a mesma forma da escala da aplicação. Vazio significa a escala da aplicação.", + "Portfolio readers": "Leitores do portefólio", + "Nextcloud user UIDs of the managers of the project's portfolio": "IDs de utilizador Nextcloud dos gestores do portefólio do projeto", + "The portfolio this project belongs to": "O portefólio a que este projeto pertence", + "No portfolio": "Sem portefólio", + "All portfolios": "Todos os portefólios", + "You read this project as a manager of its portfolio. Only its members change it.": "Lê este projeto como gestor do seu portefólio. Só os membros o alteram.", + "Portfolios": "Portefólios" }, "plurals": {} } diff --git a/l10n/rm.js b/l10n/rm.js index 0b6a7d16..a846e1ce 100644 --- a/l10n/rm.js +++ b/l10n/rm.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Nagina proposta: il project n'ha nagin budget.", "No suggestion: no costs are recorded for this project yet.": "Nagina proposta: per quest project n'èn anc nagins custs registrads.", "The report was not saved. Only the project owner can write status reports.": "Il rapport n'è betg vegnì memorisà. Mo il possessur dal project po scriver rapports da status.", - "No report": "Nagin rapport" + "No report": "Nagin rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Ina gruppa da projects manads da las medemas persunas, per exempel in departament u in program. Ses responsabels legian mintga project en ella.", + "The name of the portfolio": "Il num dal portfolio", + "What the portfolio holds and who runs it": "Tge ch'il portfolio cuntegna e tgi ch'al maina", + "A colour for the portfolio's section in project lists": "Ina colur per la secziun dal portfolio en glistas da projects", + "Managers": "Responsabels", + "Nextcloud user UIDs of the people who run the portfolio": "IDs d'utilisaders Nextcloud da las persunas che mainan il portfolio", + "Order": "Urden", + "Where the portfolio comes in project lists, lowest first": "Nua ch'il portfolio stat en glistas da projects, il pli bass l'emprim", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "L'atgna scala da ristgs da quest portfolio sco JSON, en la medema furma sco quella da l'entira app. Vid significa la scala da l'app.", + "Portfolio readers": "Lecturs dal portfolio", + "Nextcloud user UIDs of the managers of the project's portfolio": "IDs d'utilisaders Nextcloud dals responsabels dal portfolio dal project", + "The portfolio this project belongs to": "Il portfolio al qual quest project appartegna", + "No portfolio": "Nagin portfolio", + "All portfolios": "Tut ils portfolios", + "You read this project as a manager of its portfolio. Only its members change it.": "Vus legis quest project sco responsabel da ses portfolio. Mo ses commembers al midan.", + "Portfolios": "Portfolios" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/rm.json b/l10n/rm.json index 9f999153..61da8bd2 100644 --- a/l10n/rm.json +++ b/l10n/rm.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Nagina proposta: il project n'ha nagin budget.", "No suggestion: no costs are recorded for this project yet.": "Nagina proposta: per quest project n'èn anc nagins custs registrads.", "The report was not saved. Only the project owner can write status reports.": "Il rapport n'è betg vegnì memorisà. Mo il possessur dal project po scriver rapports da status.", - "No report": "Nagin rapport" + "No report": "Nagin rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Ina gruppa da projects manads da las medemas persunas, per exempel in departament u in program. Ses responsabels legian mintga project en ella.", + "The name of the portfolio": "Il num dal portfolio", + "What the portfolio holds and who runs it": "Tge ch'il portfolio cuntegna e tgi ch'al maina", + "A colour for the portfolio's section in project lists": "Ina colur per la secziun dal portfolio en glistas da projects", + "Managers": "Responsabels", + "Nextcloud user UIDs of the people who run the portfolio": "IDs d'utilisaders Nextcloud da las persunas che mainan il portfolio", + "Order": "Urden", + "Where the portfolio comes in project lists, lowest first": "Nua ch'il portfolio stat en glistas da projects, il pli bass l'emprim", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "L'atgna scala da ristgs da quest portfolio sco JSON, en la medema furma sco quella da l'entira app. Vid significa la scala da l'app.", + "Portfolio readers": "Lecturs dal portfolio", + "Nextcloud user UIDs of the managers of the project's portfolio": "IDs d'utilisaders Nextcloud dals responsabels dal portfolio dal project", + "The portfolio this project belongs to": "Il portfolio al qual quest project appartegna", + "No portfolio": "Nagin portfolio", + "All portfolios": "Tut ils portfolios", + "You read this project as a manager of its portfolio. Only its members change it.": "Vus legis quest project sco responsabel da ses portfolio. Mo ses commembers al midan.", + "Portfolios": "Portfolios" }, "plurals": null } diff --git a/l10n/ro.js b/l10n/ro.js index 1498f3e3..bd1e6f57 100644 --- a/l10n/ro.js +++ b/l10n/ro.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Nicio sugestie: proiectul nu are buget.", "No suggestion: no costs are recorded for this project yet.": "Nicio sugestie: nu există încă costuri înregistrate pentru acest proiect.", "The report was not saved. Only the project owner can write status reports.": "Raportul nu a fost salvat. Doar proprietarul proiectului poate scrie rapoarte de stare.", - "No report": "Niciun raport" + "No report": "Niciun raport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un grup de proiecte conduse de aceleași persoane, cum ar fi un departament sau un program. Responsabilii lui citesc fiecare proiect din el.", + "The name of the portfolio": "Numele portofoliului", + "What the portfolio holds and who runs it": "Ce conține portofoliul și cine îl conduce", + "A colour for the portfolio's section in project lists": "O culoare pentru secțiunea portofoliului în listele de proiecte", + "Managers": "Responsabili", + "Nextcloud user UIDs of the people who run the portfolio": "ID-urile de utilizator Nextcloud ale persoanelor care conduc portofoliul", + "Order": "Ordine", + "Where the portfolio comes in project lists, lowest first": "Locul portofoliului în listele de proiecte, cel mai mic primul", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Scala de risc proprie a acestui portofoliu în JSON, în aceeași formă ca scala întregii aplicații. Gol înseamnă scala aplicației.", + "Portfolio readers": "Cititorii portofoliului", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID-urile de utilizator Nextcloud ale responsabililor portofoliului proiectului", + "The portfolio this project belongs to": "Portofoliul din care face parte proiectul", + "No portfolio": "Fără portofoliu", + "All portfolios": "Toate portofoliile", + "You read this project as a manager of its portfolio. Only its members change it.": "Citiți acest proiect ca responsabil al portofoliului său. Doar membrii îl modifică.", + "Portfolios": "Portofolii" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ro.json b/l10n/ro.json index 026bf352..790b1700 100644 --- a/l10n/ro.json +++ b/l10n/ro.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Nicio sugestie: proiectul nu are buget.", "No suggestion: no costs are recorded for this project yet.": "Nicio sugestie: nu există încă costuri înregistrate pentru acest proiect.", "The report was not saved. Only the project owner can write status reports.": "Raportul nu a fost salvat. Doar proprietarul proiectului poate scrie rapoarte de stare.", - "No report": "Niciun raport" + "No report": "Niciun raport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Un grup de proiecte conduse de aceleași persoane, cum ar fi un departament sau un program. Responsabilii lui citesc fiecare proiect din el.", + "The name of the portfolio": "Numele portofoliului", + "What the portfolio holds and who runs it": "Ce conține portofoliul și cine îl conduce", + "A colour for the portfolio's section in project lists": "O culoare pentru secțiunea portofoliului în listele de proiecte", + "Managers": "Responsabili", + "Nextcloud user UIDs of the people who run the portfolio": "ID-urile de utilizator Nextcloud ale persoanelor care conduc portofoliul", + "Order": "Ordine", + "Where the portfolio comes in project lists, lowest first": "Locul portofoliului în listele de proiecte, cel mai mic primul", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Scala de risc proprie a acestui portofoliu în JSON, în aceeași formă ca scala întregii aplicații. Gol înseamnă scala aplicației.", + "Portfolio readers": "Cititorii portofoliului", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID-urile de utilizator Nextcloud ale responsabililor portofoliului proiectului", + "The portfolio this project belongs to": "Portofoliul din care face parte proiectul", + "No portfolio": "Fără portofoliu", + "All portfolios": "Toate portofoliile", + "You read this project as a manager of its portfolio. Only its members change it.": "Citiți acest proiect ca responsabil al portofoliului său. Doar membrii îl modifică.", + "Portfolios": "Portofolii" }, "plurals": {} } diff --git a/l10n/ru.js b/l10n/ru.js index 4d52d10b..9c39a1ec 100644 --- a/l10n/ru.js +++ b/l10n/ru.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Нет предложения: у проекта нет бюджета.", "No suggestion: no costs are recorded for this project yet.": "Нет предложения: по этому проекту ещё не учтены затраты.", "The report was not saved. Only the project owner can write status reports.": "Отчёт не сохранён. Отчёты о состоянии может писать только владелец проекта.", - "No report": "Нет отчёта" + "No report": "Нет отчёта", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Группа проектов, которыми руководят одни и те же люди, например отдел или программа. Её руководители читают каждый проект в ней.", + "The name of the portfolio": "Название портфеля", + "What the portfolio holds and who runs it": "Что входит в портфель и кто им руководит", + "A colour for the portfolio's section in project lists": "Цвет раздела портфеля в списках проектов", + "Managers": "Руководители", + "Nextcloud user UIDs of the people who run the portfolio": "Идентификаторы пользователей Nextcloud, которые руководят портфелем", + "Order": "Порядок", + "Where the portfolio comes in project lists, lowest first": "Место портфеля в списках проектов, сначала наименьший", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Собственная шкала рисков этого портфеля в формате JSON, в той же форме, что и шкала всего приложения. Пусто означает шкалу приложения.", + "Portfolio readers": "Читатели портфеля", + "Nextcloud user UIDs of the managers of the project's portfolio": "Идентификаторы пользователей Nextcloud руководителей портфеля проекта", + "The portfolio this project belongs to": "Портфель, к которому относится проект", + "No portfolio": "Без портфеля", + "All portfolios": "Все портфели", + "You read this project as a manager of its portfolio. Only its members change it.": "Вы читаете этот проект как руководитель его портфеля. Изменяют его только участники.", + "Portfolios": "Портфели" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/ru.json b/l10n/ru.json index 920bc598..1bac92d5 100644 --- a/l10n/ru.json +++ b/l10n/ru.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Нет предложения: у проекта нет бюджета.", "No suggestion: no costs are recorded for this project yet.": "Нет предложения: по этому проекту ещё не учтены затраты.", "The report was not saved. Only the project owner can write status reports.": "Отчёт не сохранён. Отчёты о состоянии может писать только владелец проекта.", - "No report": "Нет отчёта" + "No report": "Нет отчёта", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Группа проектов, которыми руководят одни и те же люди, например отдел или программа. Её руководители читают каждый проект в ней.", + "The name of the portfolio": "Название портфеля", + "What the portfolio holds and who runs it": "Что входит в портфель и кто им руководит", + "A colour for the portfolio's section in project lists": "Цвет раздела портфеля в списках проектов", + "Managers": "Руководители", + "Nextcloud user UIDs of the people who run the portfolio": "Идентификаторы пользователей Nextcloud, которые руководят портфелем", + "Order": "Порядок", + "Where the portfolio comes in project lists, lowest first": "Место портфеля в списках проектов, сначала наименьший", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Собственная шкала рисков этого портфеля в формате JSON, в той же форме, что и шкала всего приложения. Пусто означает шкалу приложения.", + "Portfolio readers": "Читатели портфеля", + "Nextcloud user UIDs of the managers of the project's portfolio": "Идентификаторы пользователей Nextcloud руководителей портфеля проекта", + "The portfolio this project belongs to": "Портфель, к которому относится проект", + "No portfolio": "Без портфеля", + "All portfolios": "Все портфели", + "You read this project as a manager of its portfolio. Only its members change it.": "Вы читаете этот проект как руководитель его портфеля. Изменяют его только участники.", + "Portfolios": "Портфели" }, "plurals": {} } diff --git a/l10n/sk.js b/l10n/sk.js index 6341cc46..6b881821 100644 --- a/l10n/sk.js +++ b/l10n/sk.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Bez návrhu: projekt nemá rozpočet.", "No suggestion: no costs are recorded for this project yet.": "Bez návrhu: pre tento projekt zatiaľ nie sú zaznamenané žiadne náklady.", "The report was not saved. Only the project owner can write status reports.": "Správa nebola uložená. Správy o stave môže písať iba vlastník projektu.", - "No report": "Žiadna správa" + "No report": "Žiadna správa", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Skupina projektov vedených tými istými ľuďmi, napríklad oddelenie alebo program. Jej správcovia čítajú každý projekt v nej.", + "The name of the portfolio": "Názov portfólia", + "What the portfolio holds and who runs it": "Čo portfólio obsahuje a kto ho vedie", + "A colour for the portfolio's section in project lists": "Farba sekcie portfólia v zoznamoch projektov", + "Managers": "Správcovia", + "Nextcloud user UIDs of the people who run the portfolio": "ID používateľov Nextcloud ľudí, ktorí portfólio vedú", + "Order": "Poradie", + "Where the portfolio comes in project lists, lowest first": "Kde je portfólio v zoznamoch projektov, od najnižšieho", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Vlastná škála rizík tohto portfólia ako JSON, v rovnakom tvare ako škála celej aplikácie. Prázdne znamená škálu aplikácie.", + "Portfolio readers": "Čitatelia portfólia", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID používateľov Nextcloud správcov portfólia projektu", + "The portfolio this project belongs to": "Portfólio, do ktorého projekt patrí", + "No portfolio": "Bez portfólia", + "All portfolios": "Všetky portfóliá", + "You read this project as a manager of its portfolio. Only its members change it.": "Tento projekt čítate ako správca jeho portfólia. Menia ho iba jeho členovia.", + "Portfolios": "Portfóliá" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sk.json b/l10n/sk.json index c563ac1d..9006ba7f 100644 --- a/l10n/sk.json +++ b/l10n/sk.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Bez návrhu: projekt nemá rozpočet.", "No suggestion: no costs are recorded for this project yet.": "Bez návrhu: pre tento projekt zatiaľ nie sú zaznamenané žiadne náklady.", "The report was not saved. Only the project owner can write status reports.": "Správa nebola uložená. Správy o stave môže písať iba vlastník projektu.", - "No report": "Žiadna správa" + "No report": "Žiadna správa", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Skupina projektov vedených tými istými ľuďmi, napríklad oddelenie alebo program. Jej správcovia čítajú každý projekt v nej.", + "The name of the portfolio": "Názov portfólia", + "What the portfolio holds and who runs it": "Čo portfólio obsahuje a kto ho vedie", + "A colour for the portfolio's section in project lists": "Farba sekcie portfólia v zoznamoch projektov", + "Managers": "Správcovia", + "Nextcloud user UIDs of the people who run the portfolio": "ID používateľov Nextcloud ľudí, ktorí portfólio vedú", + "Order": "Poradie", + "Where the portfolio comes in project lists, lowest first": "Kde je portfólio v zoznamoch projektov, od najnižšieho", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Vlastná škála rizík tohto portfólia ako JSON, v rovnakom tvare ako škála celej aplikácie. Prázdne znamená škálu aplikácie.", + "Portfolio readers": "Čitatelia portfólia", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID používateľov Nextcloud správcov portfólia projektu", + "The portfolio this project belongs to": "Portfólio, do ktorého projekt patrí", + "No portfolio": "Bez portfólia", + "All portfolios": "Všetky portfóliá", + "You read this project as a manager of its portfolio. Only its members change it.": "Tento projekt čítate ako správca jeho portfólia. Menia ho iba jeho členovia.", + "Portfolios": "Portfóliá" }, "plurals": null } diff --git a/l10n/sl.js b/l10n/sl.js index ad1bf84f..6ec552c0 100644 --- a/l10n/sl.js +++ b/l10n/sl.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Brez predloga: projekt nima proračuna.", "No suggestion: no costs are recorded for this project yet.": "Brez predloga: za ta projekt še ni zabeleženih stroškov.", "The report was not saved. Only the project owner can write status reports.": "Poročilo ni bilo shranjeno. Poročila o stanju lahko piše samo lastnik projekta.", - "No report": "Ni poročila" + "No report": "Ni poročila", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Skupina projektov, ki jih vodijo isti ljudje, na primer oddelek ali program. Njeni upravitelji berejo vsak projekt v njej.", + "The name of the portfolio": "Ime portfelja", + "What the portfolio holds and who runs it": "Kaj portfelj vsebuje in kdo ga vodi", + "A colour for the portfolio's section in project lists": "Barva razdelka portfelja v seznamih projektov", + "Managers": "Upravitelji", + "Nextcloud user UIDs of the people who run the portfolio": "Uporabniški ID-ji Nextcloud ljudi, ki vodijo portfelj", + "Order": "Vrstni red", + "Where the portfolio comes in project lists, lowest first": "Kje je portfelj v seznamih projektov, najnižji najprej", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Lastna lestvica tveganj tega portfelja v obliki JSON, v enaki obliki kot lestvica celotne aplikacije. Prazno pomeni lestvico aplikacije.", + "Portfolio readers": "Bralci portfelja", + "Nextcloud user UIDs of the managers of the project's portfolio": "Uporabniški ID-ji Nextcloud upraviteljev portfelja projekta", + "The portfolio this project belongs to": "Portfelj, kateremu projekt pripada", + "No portfolio": "Brez portfelja", + "All portfolios": "Vsi portfelji", + "You read this project as a manager of its portfolio. Only its members change it.": "Ta projekt berete kot upravitelj njegovega portfelja. Spreminjajo ga le njegovi člani.", + "Portfolios": "Portfelji" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sl.json b/l10n/sl.json index e55d308c..b0f9aad7 100644 --- a/l10n/sl.json +++ b/l10n/sl.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Brez predloga: projekt nima proračuna.", "No suggestion: no costs are recorded for this project yet.": "Brez predloga: za ta projekt še ni zabeleženih stroškov.", "The report was not saved. Only the project owner can write status reports.": "Poročilo ni bilo shranjeno. Poročila o stanju lahko piše samo lastnik projekta.", - "No report": "Ni poročila" + "No report": "Ni poročila", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Skupina projektov, ki jih vodijo isti ljudje, na primer oddelek ali program. Njeni upravitelji berejo vsak projekt v njej.", + "The name of the portfolio": "Ime portfelja", + "What the portfolio holds and who runs it": "Kaj portfelj vsebuje in kdo ga vodi", + "A colour for the portfolio's section in project lists": "Barva razdelka portfelja v seznamih projektov", + "Managers": "Upravitelji", + "Nextcloud user UIDs of the people who run the portfolio": "Uporabniški ID-ji Nextcloud ljudi, ki vodijo portfelj", + "Order": "Vrstni red", + "Where the portfolio comes in project lists, lowest first": "Kje je portfelj v seznamih projektov, najnižji najprej", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Lastna lestvica tveganj tega portfelja v obliki JSON, v enaki obliki kot lestvica celotne aplikacije. Prazno pomeni lestvico aplikacije.", + "Portfolio readers": "Bralci portfelja", + "Nextcloud user UIDs of the managers of the project's portfolio": "Uporabniški ID-ji Nextcloud upraviteljev portfelja projekta", + "The portfolio this project belongs to": "Portfelj, kateremu projekt pripada", + "No portfolio": "Brez portfelja", + "All portfolios": "Vsi portfelji", + "You read this project as a manager of its portfolio. Only its members change it.": "Ta projekt berete kot upravitelj njegovega portfelja. Spreminjajo ga le njegovi člani.", + "Portfolios": "Portfelji" }, "plurals": null } diff --git a/l10n/sq.js b/l10n/sq.js index 3c25dac1..3fbed4e1 100644 --- a/l10n/sq.js +++ b/l10n/sq.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Asnjë sugjerim: projekti nuk ka buxhet.", "No suggestion: no costs are recorded for this project yet.": "Asnjë sugjerim: ende nuk janë regjistruar kosto për këtë projekt.", "The report was not saved. Only the project owner can write status reports.": "Raporti nuk u ruajt. Vetëm pronari i projektit mund të shkruajë raporte gjendjeje.", - "No report": "Asnjë raport" + "No report": "Asnjë raport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Një grup projektesh që drejtohen nga të njëjtët njerëz, si një departament ose një program. Drejtuesit e tij lexojnë çdo projekt në të.", + "The name of the portfolio": "Emri i portofolit", + "What the portfolio holds and who runs it": "Çfarë përmban portofoli dhe kush e drejton", + "A colour for the portfolio's section in project lists": "Një ngjyrë për seksionin e portofolit në listat e projekteve", + "Managers": "Drejtues", + "Nextcloud user UIDs of the people who run the portfolio": "ID-të e përdoruesve Nextcloud të njerëzve që drejtojnë portofolin", + "Order": "Renditja", + "Where the portfolio comes in project lists, lowest first": "Vendi i portofolit në listat e projekteve, më i ulëti i pari", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Shkalla e vet e rrezikut e këtij portofoli si JSON, në të njëjtën formë si ajo e gjithë aplikacionit. Bosh do të thotë shkalla e aplikacionit.", + "Portfolio readers": "Lexuesit e portofolit", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID-të e përdoruesve Nextcloud të drejtuesve të portofolit të projektit", + "The portfolio this project belongs to": "Portofoli të cilit i përket ky projekt", + "No portfolio": "Pa portofol", + "All portfolios": "Të gjithë portofolet", + "You read this project as a manager of its portfolio. Only its members change it.": "E lexoni këtë projekt si drejtues i portofolit të tij. Vetëm anëtarët e tij e ndryshojnë.", + "Portfolios": "Portofole" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sq.json b/l10n/sq.json index 61fd5710..d54d7136 100644 --- a/l10n/sq.json +++ b/l10n/sq.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Asnjë sugjerim: projekti nuk ka buxhet.", "No suggestion: no costs are recorded for this project yet.": "Asnjë sugjerim: ende nuk janë regjistruar kosto për këtë projekt.", "The report was not saved. Only the project owner can write status reports.": "Raporti nuk u ruajt. Vetëm pronari i projektit mund të shkruajë raporte gjendjeje.", - "No report": "Asnjë raport" + "No report": "Asnjë raport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Një grup projektesh që drejtohen nga të njëjtët njerëz, si një departament ose një program. Drejtuesit e tij lexojnë çdo projekt në të.", + "The name of the portfolio": "Emri i portofolit", + "What the portfolio holds and who runs it": "Çfarë përmban portofoli dhe kush e drejton", + "A colour for the portfolio's section in project lists": "Një ngjyrë për seksionin e portofolit në listat e projekteve", + "Managers": "Drejtues", + "Nextcloud user UIDs of the people who run the portfolio": "ID-të e përdoruesve Nextcloud të njerëzve që drejtojnë portofolin", + "Order": "Renditja", + "Where the portfolio comes in project lists, lowest first": "Vendi i portofolit në listat e projekteve, më i ulëti i pari", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Shkalla e vet e rrezikut e këtij portofoli si JSON, në të njëjtën formë si ajo e gjithë aplikacionit. Bosh do të thotë shkalla e aplikacionit.", + "Portfolio readers": "Lexuesit e portofolit", + "Nextcloud user UIDs of the managers of the project's portfolio": "ID-të e përdoruesve Nextcloud të drejtuesve të portofolit të projektit", + "The portfolio this project belongs to": "Portofoli të cilit i përket ky projekt", + "No portfolio": "Pa portofol", + "All portfolios": "Të gjithë portofolet", + "You read this project as a manager of its portfolio. Only its members change it.": "E lexoni këtë projekt si drejtues i portofolit të tij. Vetëm anëtarët e tij e ndryshojnë.", + "Portfolios": "Portofole" }, "plurals": {} } diff --git a/l10n/sr.js b/l10n/sr.js index a36019ab..9d567422 100644 --- a/l10n/sr.js +++ b/l10n/sr.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Нема предлога: пројекат нема буџет.", "No suggestion: no costs are recorded for this project yet.": "Нема предлога: за овај пројекат још нису евидентирани трошкови.", "The report was not saved. Only the project owner can write status reports.": "Извештај није сачуван. Извештаје о стању може писати само власник пројекта.", - "No report": "Нема извештаја" + "No report": "Нема извештаја", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група пројеката које воде исти људи, на пример одељење или програм. Њени руководиоци читају сваки пројекат у њој.", + "The name of the portfolio": "Назив портфолија", + "What the portfolio holds and who runs it": "Шта портфолио садржи и ко га води", + "A colour for the portfolio's section in project lists": "Боја одељка портфолија у листама пројеката", + "Managers": "Руководиоци", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud кориснички ID-јеви људи који воде портфолио", + "Order": "Редослед", + "Where the portfolio comes in project lists, lowest first": "Где је портфолио у листама пројеката, најнижи први", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Сопствена скала ризика овог портфолија као JSON, у истом облику као скала целе апликације. Празно значи скалу апликације.", + "Portfolio readers": "Читаоци портфолија", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud кориснички ID-јеви руководилаца портфолија пројекта", + "The portfolio this project belongs to": "Портфолио коме овај пројекат припада", + "No portfolio": "Без портфолија", + "All portfolios": "Сви портфолији", + "You read this project as a manager of its portfolio. Only its members change it.": "Овај пројекат читате као руководилац његовог портфолија. Мењају га само његови чланови.", + "Portfolios": "Портфолији" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sr.json b/l10n/sr.json index c4fbe018..a4ee48e6 100644 --- a/l10n/sr.json +++ b/l10n/sr.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Нема предлога: пројекат нема буџет.", "No suggestion: no costs are recorded for this project yet.": "Нема предлога: за овај пројекат још нису евидентирани трошкови.", "The report was not saved. Only the project owner can write status reports.": "Извештај није сачуван. Извештаје о стању може писати само власник пројекта.", - "No report": "Нема извештаја" + "No report": "Нема извештаја", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група пројеката које воде исти људи, на пример одељење или програм. Њени руководиоци читају сваки пројекат у њој.", + "The name of the portfolio": "Назив портфолија", + "What the portfolio holds and who runs it": "Шта портфолио садржи и ко га води", + "A colour for the portfolio's section in project lists": "Боја одељка портфолија у листама пројеката", + "Managers": "Руководиоци", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud кориснички ID-јеви људи који воде портфолио", + "Order": "Редослед", + "Where the portfolio comes in project lists, lowest first": "Где је портфолио у листама пројеката, најнижи први", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Сопствена скала ризика овог портфолија као JSON, у истом облику као скала целе апликације. Празно значи скалу апликације.", + "Portfolio readers": "Читаоци портфолија", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud кориснички ID-јеви руководилаца портфолија пројекта", + "The portfolio this project belongs to": "Портфолио коме овај пројекат припада", + "No portfolio": "Без портфолија", + "All portfolios": "Сви портфолији", + "You read this project as a manager of its portfolio. Only its members change it.": "Овај пројекат читате као руководилац његовог портфолија. Мењају га само његови чланови.", + "Portfolios": "Портфолији" }, "plurals": null } diff --git a/l10n/sv.js b/l10n/sv.js index c98b798b..8a1d62d5 100644 --- a/l10n/sv.js +++ b/l10n/sv.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Inget förslag: projektet har ingen budget.", "No suggestion: no costs are recorded for this project yet.": "Inget förslag: inga kostnader har registrerats för projektet än.", "The report was not saved. Only the project owner can write status reports.": "Rapporten sparades inte. Endast projektägaren kan skriva statusrapporter.", - "No report": "Ingen rapport" + "No report": "Ingen rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "En grupp projekt som drivs av samma personer, till exempel en avdelning eller ett program. Dess ansvariga läser varje projekt i den.", + "The name of the portfolio": "Portföljens namn", + "What the portfolio holds and who runs it": "Vad portföljen innehåller och vem som driver den", + "A colour for the portfolio's section in project lists": "En färg för portföljens avsnitt i projektlistor", + "Managers": "Ansvariga", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-användar-id för de personer som driver portföljen", + "Order": "Ordning", + "Where the portfolio comes in project lists, lowest first": "Var portföljen står i projektlistor, lägst först", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Portföljens egen riskskala som JSON, i samma form som den för hela appen. Tom betyder skalan för hela appen.", + "Portfolio readers": "Portföljläsare", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-användar-id för de ansvariga för projektets portfölj", + "The portfolio this project belongs to": "Portföljen som projektet hör till", + "No portfolio": "Ingen portfölj", + "All portfolios": "Alla portföljer", + "You read this project as a manager of its portfolio. Only its members change it.": "Du läser det här projektet som ansvarig för dess portfölj. Endast medlemmarna ändrar det.", + "Portfolios": "Portföljer" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/sv.json b/l10n/sv.json index 38772ab5..cf85c305 100644 --- a/l10n/sv.json +++ b/l10n/sv.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Inget förslag: projektet har ingen budget.", "No suggestion: no costs are recorded for this project yet.": "Inget förslag: inga kostnader har registrerats för projektet än.", "The report was not saved. Only the project owner can write status reports.": "Rapporten sparades inte. Endast projektägaren kan skriva statusrapporter.", - "No report": "Ingen rapport" + "No report": "Ingen rapport", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "En grupp projekt som drivs av samma personer, till exempel en avdelning eller ett program. Dess ansvariga läser varje projekt i den.", + "The name of the portfolio": "Portföljens namn", + "What the portfolio holds and who runs it": "Vad portföljen innehåller och vem som driver den", + "A colour for the portfolio's section in project lists": "En färg för portföljens avsnitt i projektlistor", + "Managers": "Ansvariga", + "Nextcloud user UIDs of the people who run the portfolio": "Nextcloud-användar-id för de personer som driver portföljen", + "Order": "Ordning", + "Where the portfolio comes in project lists, lowest first": "Var portföljen står i projektlistor, lägst först", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Portföljens egen riskskala som JSON, i samma form som den för hela appen. Tom betyder skalan för hela appen.", + "Portfolio readers": "Portföljläsare", + "Nextcloud user UIDs of the managers of the project's portfolio": "Nextcloud-användar-id för de ansvariga för projektets portfölj", + "The portfolio this project belongs to": "Portföljen som projektet hör till", + "No portfolio": "Ingen portfölj", + "All portfolios": "Alla portföljer", + "You read this project as a manager of its portfolio. Only its members change it.": "Du läser det här projektet som ansvarig för dess portfölj. Endast medlemmarna ändrar det.", + "Portfolios": "Portföljer" }, "plurals": null } diff --git a/l10n/tr.js b/l10n/tr.js index 039e73c3..54ddef23 100644 --- a/l10n/tr.js +++ b/l10n/tr.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Öneri yok: projenin bütçesi yok.", "No suggestion: no costs are recorded for this project yet.": "Öneri yok: bu proje için henüz maliyet kaydedilmedi.", "The report was not saved. Only the project owner can write status reports.": "Rapor kaydedilmedi. Durum raporlarını yalnızca proje sahibi yazabilir.", - "No report": "Rapor yok" + "No report": "Rapor yok", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Aynı kişilerin yürüttüğü proje grubu, örneğin bir birim veya bir program. Sorumluları içindeki her projeyi okur.", + "The name of the portfolio": "Portföyün adı", + "What the portfolio holds and who runs it": "Portföyün neleri içerdiği ve kimin yürüttüğü", + "A colour for the portfolio's section in project lists": "Proje listelerinde portföy bölümünün rengi", + "Managers": "Sorumlular", + "Nextcloud user UIDs of the people who run the portfolio": "Portföyü yürüten kişilerin Nextcloud kullanıcı kimlikleri", + "Order": "Sıra", + "Where the portfolio comes in project lists, lowest first": "Portföyün proje listelerindeki yeri, en küçük önce", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Bu portföyün JSON biçiminde kendi risk ölçeği, uygulama geneli ölçekle aynı biçimde. Boş, uygulama geneli ölçek demektir.", + "Portfolio readers": "Portföy okuyucuları", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projenin portföy sorumlularının Nextcloud kullanıcı kimlikleri", + "The portfolio this project belongs to": "Bu projenin ait olduğu portföy", + "No portfolio": "Portföy yok", + "All portfolios": "Tüm portföyler", + "You read this project as a manager of its portfolio. Only its members change it.": "Bu projeyi portföyünün sorumlusu olarak okuyorsunuz. Yalnızca üyeleri değiştirir.", + "Portfolios": "Portföyler" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/tr.json b/l10n/tr.json index 206bb4b2..f90a570b 100644 --- a/l10n/tr.json +++ b/l10n/tr.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Öneri yok: projenin bütçesi yok.", "No suggestion: no costs are recorded for this project yet.": "Öneri yok: bu proje için henüz maliyet kaydedilmedi.", "The report was not saved. Only the project owner can write status reports.": "Rapor kaydedilmedi. Durum raporlarını yalnızca proje sahibi yazabilir.", - "No report": "Rapor yok" + "No report": "Rapor yok", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Aynı kişilerin yürüttüğü proje grubu, örneğin bir birim veya bir program. Sorumluları içindeki her projeyi okur.", + "The name of the portfolio": "Portföyün adı", + "What the portfolio holds and who runs it": "Portföyün neleri içerdiği ve kimin yürüttüğü", + "A colour for the portfolio's section in project lists": "Proje listelerinde portföy bölümünün rengi", + "Managers": "Sorumlular", + "Nextcloud user UIDs of the people who run the portfolio": "Portföyü yürüten kişilerin Nextcloud kullanıcı kimlikleri", + "Order": "Sıra", + "Where the portfolio comes in project lists, lowest first": "Portföyün proje listelerindeki yeri, en küçük önce", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Bu portföyün JSON biçiminde kendi risk ölçeği, uygulama geneli ölçekle aynı biçimde. Boş, uygulama geneli ölçek demektir.", + "Portfolio readers": "Portföy okuyucuları", + "Nextcloud user UIDs of the managers of the project's portfolio": "Projenin portföy sorumlularının Nextcloud kullanıcı kimlikleri", + "The portfolio this project belongs to": "Bu projenin ait olduğu portföy", + "No portfolio": "Portföy yok", + "All portfolios": "Tüm portföyler", + "You read this project as a manager of its portfolio. Only its members change it.": "Bu projeyi portföyünün sorumlusu olarak okuyorsunuz. Yalnızca üyeleri değiştirir.", + "Portfolios": "Portföyler" }, "plurals": {} } diff --git a/l10n/uk.js b/l10n/uk.js index 3867a825..a7742ddc 100644 --- a/l10n/uk.js +++ b/l10n/uk.js @@ -476,7 +476,23 @@ OC.L10N.register( "No suggestion: the project has no budget.": "Немає пропозиції: у проєкту немає бюджету.", "No suggestion: no costs are recorded for this project yet.": "Немає пропозиції: для цього проєкту ще не обліковано витрат.", "The report was not saved. Only the project owner can write status reports.": "Звіт не збережено. Звіти про стан може писати лише власник проєкту.", - "No report": "Немає звіту" + "No report": "Немає звіту", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група проєктів, якими керують ті самі люди, наприклад відділ або програма. Її керівники читають кожен проєкт у ній.", + "The name of the portfolio": "Назва портфеля", + "What the portfolio holds and who runs it": "Що входить до портфеля і хто ним керує", + "A colour for the portfolio's section in project lists": "Колір розділу портфеля у списках проєктів", + "Managers": "Керівники", + "Nextcloud user UIDs of the people who run the portfolio": "Ідентифікатори користувачів Nextcloud, які керують портфелем", + "Order": "Порядок", + "Where the portfolio comes in project lists, lowest first": "Місце портфеля у списках проєктів, спершу найменший", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Власна шкала ризиків цього портфеля у форматі JSON, у тій самій формі, що й шкала всього застосунку. Порожньо означає шкалу застосунку.", + "Portfolio readers": "Читачі портфеля", + "Nextcloud user UIDs of the managers of the project's portfolio": "Ідентифікатори користувачів Nextcloud керівників портфеля проєкту", + "The portfolio this project belongs to": "Портфель, до якого належить проєкт", + "No portfolio": "Без портфеля", + "All portfolios": "Усі портфелі", + "You read this project as a manager of its portfolio. Only its members change it.": "Ви читаєте цей проєкт як керівник його портфеля. Змінюють його лише учасники.", + "Portfolios": "Портфелі" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/uk.json b/l10n/uk.json index 090af162..99bbde6f 100644 --- a/l10n/uk.json +++ b/l10n/uk.json @@ -475,7 +475,23 @@ "No suggestion: the project has no budget.": "Немає пропозиції: у проєкту немає бюджету.", "No suggestion: no costs are recorded for this project yet.": "Немає пропозиції: для цього проєкту ще не обліковано витрат.", "The report was not saved. Only the project owner can write status reports.": "Звіт не збережено. Звіти про стан може писати лише власник проєкту.", - "No report": "Немає звіту" + "No report": "Немає звіту", + "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.": "Група проєктів, якими керують ті самі люди, наприклад відділ або програма. Її керівники читають кожен проєкт у ній.", + "The name of the portfolio": "Назва портфеля", + "What the portfolio holds and who runs it": "Що входить до портфеля і хто ним керує", + "A colour for the portfolio's section in project lists": "Колір розділу портфеля у списках проєктів", + "Managers": "Керівники", + "Nextcloud user UIDs of the people who run the portfolio": "Ідентифікатори користувачів Nextcloud, які керують портфелем", + "Order": "Порядок", + "Where the portfolio comes in project lists, lowest first": "Місце портфеля у списках проєктів, спершу найменший", + "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale.": "Власна шкала ризиків цього портфеля у форматі JSON, у тій самій формі, що й шкала всього застосунку. Порожньо означає шкалу застосунку.", + "Portfolio readers": "Читачі портфеля", + "Nextcloud user UIDs of the managers of the project's portfolio": "Ідентифікатори користувачів Nextcloud керівників портфеля проєкту", + "The portfolio this project belongs to": "Портфель, до якого належить проєкт", + "No portfolio": "Без портфеля", + "All portfolios": "Усі портфелі", + "You read this project as a manager of its portfolio. Only its members change it.": "Ви читаєте цей проєкт як керівник його портфеля. Змінюють його лише учасники.", + "Portfolios": "Портфелі" }, "plurals": {} } diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 8868e71e..38d42d69 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -650,6 +650,19 @@ private function registerMembershipListeners(IEventDispatcher $dispatcher): void ); } + // Portfolios: a project's readers are the managers of its portfolio, + // derived on every project write and pushed out when a portfolio's + // managers change or it is deleted (projects-grouping-hierarchy-fields). + foreach (['ObjectCreatingEvent', 'ObjectUpdatingEvent', 'ObjectDeletingEvent'] as $event) { + $this->registerFilteredObjectListener( + dispatcher: $dispatcher, + event: 'OCA\\OpenRegister\\Event\\' . $event, + listener: 'OCA\\Planninq\\Listener\\ProjectHierarchyGuardListener', + registers: ['planninq'], + schemas: ['project', 'projectPortfolio'] + ); + } + // Status reports: owner-only writes, and the newest report copied onto // its project inside the report's own save (portfolio-status-overview). foreach (['ObjectCreatingEvent', 'ObjectUpdatingEvent', 'ObjectDeletingEvent'] as $event) { diff --git a/lib/Listener/ProjectHierarchyGuardListener.php b/lib/Listener/ProjectHierarchyGuardListener.php new file mode 100644 index 00000000..bc484646 --- /dev/null +++ b/lib/Listener/ProjectHierarchyGuardListener.php @@ -0,0 +1,299 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * @version GIT: + * + * @link https://conduction.nl + * + * @spec openspec/changes/projects-grouping-hierarchy-fields/tasks.md#task-1.3 + * + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + */ + +declare(strict_types=1); + +namespace OCA\Planninq\Listener; + +use OCA\OpenRegister\Event\ObjectCreatingEvent; +use OCA\OpenRegister\Event\ObjectDeletingEvent; +use OCA\OpenRegister\Event\ObjectUpdatingEvent; +use OCA\Planninq\Service\ProjectMembershipService; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Container\ContainerInterface; +use Psr\Log\LoggerInterface; + +/** + * Derives portfolio readers on projects and keeps them in step with the portfolio. + * + * @template-implements IEventListener + * + * @spec openspec/changes/projects-grouping-hierarchy-fields/tasks.md#task-1.3 + */ +class ProjectHierarchyGuardListener implements IEventListener { + + /** + * Slug of the portfolio schema. + * + * @var string + */ + public const PORTFOLIO_SCHEMA = 'projectPortfolio'; + + /** + * OpenRegister's system-operation scope, by name. + * + * @var string + */ + private const OR_SYSTEM_CONTEXT = 'OCA\\OpenRegister\\Service\\SystemOperationContext'; + + /** + * Constructor. + * + * @param ProjectMembershipService $membership Reads portfolios and projects, writes as the system. + * @param TaskScopeResolver $scopeResolver Tells a planninq schema from any other object. + * @param ContainerInterface $container Resolves OpenRegister's ObjectService for the system write. + * @param LoggerInterface $logger The logger. + */ + public function __construct( + private ProjectMembershipService $membership, + private TaskScopeResolver $scopeResolver, + private ContainerInterface $container, + private LoggerInterface $logger, + ) { + }//end __construct() + + /** + * Route a pre-save or pre-delete event. + * + * @param Event $event The event. + * + * @return void + * + * @spec openspec/changes/projects-grouping-hierarchy-fields/tasks.md#task-1.3 + */ + public function handle(Event $event): void { + if ($this->isSystemOperation() === true) { + return; + } + + if ($event instanceof ObjectUpdatingEvent === true) { + $oldData = []; + $old = $event->getOldObject(); + if ($old !== null) { + $oldData = (array)$old->getObject(); + } + + $this->route(event: $event, object: $event->getNewObject(), oldData: $oldData); + return; + } + + if ($event instanceof ObjectCreatingEvent === true || $event instanceof ObjectDeletingEvent === true) { + $this->route(event: $event, object: $event->getObject(), oldData: null); + } + }//end handle() + + /** + * Send a project write to the reader derivation and a portfolio write to the fan-out. + * + * @param ObjectCreatingEvent|ObjectUpdatingEvent|ObjectDeletingEvent $event The event. + * @param object $object The object. + * @param array|null $oldData The stored object on an update. + * + * @return void + */ + private function route(ObjectCreatingEvent|ObjectUpdatingEvent|ObjectDeletingEvent $event, object $object, ?array $oldData): void { + $slug = $this->scopeResolver->planninqSchemaSlug( + registerId: (string)($object->getRegister() ?? ''), + schemaId: (string)($object->getSchema() ?? '') + ); + $data = (array)$object->getObject(); + + if ($slug === ProjectMembershipService::PROJECT_SCHEMA && $event instanceof ObjectDeletingEvent === false) { + $this->deriveReaders(event: $event, data: $data); + return; + } + + if ($slug !== self::PORTFOLIO_SCHEMA) { + return; + } + + $portfolioId = (string)($object->getUuid() ?? ''); + if ($event instanceof ObjectDeletingEvent === true) { + $this->releaseProjects(portfolioId: $portfolioId); + return; + } + + if ($oldData !== null) { + $this->onManagersChange(portfolioId: $portfolioId, data: $data, oldData: $oldData); + } + }//end route() + + /** + * Set a project's readers to the managers of its portfolio. + * + * @param ObjectCreatingEvent|ObjectUpdatingEvent $event The event. + * @param array $data The project's new data. + * + * @return void + */ + private function deriveReaders(ObjectCreatingEvent|ObjectUpdatingEvent $event, array $data): void { + $readers = $this->managersOf(portfolioId: $this->referenceId(value: ($data['portfolio'] ?? null))); + if ($this->membership->normalise(members: ($data[ProjectMembershipService::READERS_FIELD] ?? [])) === $readers) { + return; + } + + $event->setModifiedData(array_merge($event->getModifiedData(), [ProjectMembershipService::READERS_FIELD => $readers])); + }//end deriveReaders() + + /** + * Hand a portfolio's new managers to every project in it and to their objects. + * + * @param string $portfolioId The portfolio UUID. + * @param array $data The portfolio's new data. + * @param array $oldData The stored portfolio. + * + * @return void + */ + private function onManagersChange(string $portfolioId, array $data, array $oldData): void { + $managers = $this->membership->normalise(members: ($data['managers'] ?? [])); + if ($this->membership->normalise(members: ($oldData['managers'] ?? [])) === $managers) { + return; + } + + foreach ($this->membership->rows(schema: ProjectMembershipService::PROJECT_SCHEMA, filters: ['portfolio' => $portfolioId]) as $project) { + $this->writeProject(projectId: $project['id'], data: array_merge($project['data'], [ProjectMembershipService::READERS_FIELD => $managers])); + } + }//end onManagersChange() + + /** + * Take every project out of a portfolio that is being deleted. + * + * @param string $portfolioId The portfolio UUID. + * + * @return void + */ + private function releaseProjects(string $portfolioId): void { + foreach ($this->membership->rows(schema: ProjectMembershipService::PROJECT_SCHEMA, filters: ['portfolio' => $portfolioId]) as $project) { + $this->writeProject( + projectId: $project['id'], + data: array_merge($project['data'], ['portfolio' => null, ProjectMembershipService::READERS_FIELD => []]) + ); + } + }//end releaseProjects() + + /** + * Write a project as the system and copy its readers onto its objects. + * + * The system write raises no post-event, so the objects are brought in step here. + * + * @param string $projectId The project UUID. + * @param array $data The project's new data. + * + * @return void + */ + private function writeProject(string $projectId, array $data): void { + $class = self::OR_SYSTEM_CONTEXT; + if (class_exists($class) === false) { + $this->logger->warning('Planninq: OpenRegister has no system-operation scope; portfolio readers were not updated', ['project' => $projectId]); + return; + } + + try { + $objectService = $this->container->get('OCA\\OpenRegister\\Service\\ObjectService'); + $class::run( + static fn () => $objectService->saveObject( + object: $data, + register: ProjectMembershipService::REGISTER, + schema: ProjectMembershipService::PROJECT_SCHEMA, + uuid: $projectId, + _rbac: false, + _multitenancy: false, + silent: true, + _validation: false + ) + ); + } catch (\Throwable $e) { + $this->logger->error('Planninq: could not update the portfolio readers of a project', ['project' => $projectId, 'exception' => $e->getMessage()]); + return; + } + + $field = ProjectMembershipService::READERS_FIELD; + $written = $this->membership->syncProjectMembers(projectId: $projectId, members: ($data[$field] ?? []), field: $field); + $this->logger->info('Planninq: portfolio readers updated on a project and its objects', ['project' => $projectId, 'written' => $written]); + }//end writeProject() + + /** + * The managers of a portfolio, sorted and distinct; empty for none or an unknown portfolio. + * + * @param string $portfolioId The portfolio UUID. + * + * @return array + */ + private function managersOf(string $portfolioId): array { + $portfolio = $this->membership->objectData(schema: self::PORTFOLIO_SCHEMA, id: $portfolioId); + + return $this->membership->normalise(members: ($portfolio['managers'] ?? [])); + }//end managersOf() + + /** + * A reference value as a UUID string. + * + * @param mixed $value A UUID string or a resolved object. + * + * @return string + */ + private function referenceId(mixed $value): string { + if (is_array($value) === true) { + $value = ($value['id'] ?? ($value['@self']['id'] ?? '')); + } + + if (is_string($value) === false) { + return ''; + } + + return trim($value); + }//end referenceId() + + /** + * Whether OpenRegister runs this write as a trusted system operation. + * + * @return bool + */ + private function isSystemOperation(): bool { + $class = self::OR_SYSTEM_CONTEXT; + + return (class_exists($class) === true && $class::isActive() === true); + }//end isSystemOperation() +}//end class diff --git a/lib/Listener/ProjectMemberAccessListener.php b/lib/Listener/ProjectMemberAccessListener.php index 56eae543..8cd5ab22 100644 --- a/lib/Listener/ProjectMemberAccessListener.php +++ b/lib/Listener/ProjectMemberAccessListener.php @@ -3,8 +3,9 @@ /** * Planninq ProjectMemberAccessListener * - * Stamps a project's members on every task, column, phase and planned time - * entry as it is written, and refuses a create or a move into a project the + * Stamps a project's members, and the managers of its portfolio, on every + * task, column, phase, planned time entry, log entry, risk and status report + * as it is written, and refuses a create or a move into a project the * caller is not a member of. * * WHY (planninq#681) @@ -174,17 +175,44 @@ private function apply(ObjectCreatingEvent|ObjectUpdatingEvent $event, object $o return; } - $members = ($members ?? []); - if (array_key_exists('members', $data) === true && $this->membership->normalise(members: $data['members']) === $members) { - // Already in step. Leaving the data untouched matters beyond speed: - // OpenRegister's batched soft delete falls back to a full-row save - // for every object a pre-update hook modifies. - return; + $lists = [ + 'members' => ($members ?? []), + ProjectMembershipService::READERS_FIELD => $this->readersOf(projectId: $projectId), + ]; + $changes = []; + foreach ($lists as $field => $values) { + // Leave a list that is already in step untouched: OpenRegister's + // batched soft delete falls back to a full-row save for every + // object a pre-update hook modifies. A missing readers list reads + // as empty, so objects outside any portfolio are never rewritten. + $inStep = $this->membership->normalise(members: ($data[$field] ?? null)) === $values; + if ($field === 'members') { + $inStep = (array_key_exists('members', $data) === true && $inStep === true); + } + + if ($inStep === false) { + $changes[$field] = $values; + } } - $event->setModifiedData(array_merge($event->getModifiedData(), ['members' => $members])); + if ($changes !== []) { + $event->setModifiedData(array_merge($event->getModifiedData(), $changes)); + } }//end apply() + /** + * The managers of the project's portfolio, as the project carries them. + * + * @param string $projectId The project UUID. + * + * @return array + */ + private function readersOf(string $projectId): array { + $project = $this->membership->objectData(schema: ProjectMembershipService::PROJECT_SCHEMA, id: $projectId); + + return $this->membership->normalise(members: ($project[ProjectMembershipService::READERS_FIELD] ?? [])); + }//end readersOf() + /** * Whether the acting user may put this object in its project. * diff --git a/lib/Listener/ProjectMembershipSyncListener.php b/lib/Listener/ProjectMembershipSyncListener.php index 53afb143..449655fa 100644 --- a/lib/Listener/ProjectMembershipSyncListener.php +++ b/lib/Listener/ProjectMembershipSyncListener.php @@ -93,18 +93,28 @@ public function handle(Event $event): void { return; } - $members = $this->membership->membersFromProject(project: (array)$project->getObject()); - $old = $event->getOldObject(); - if ($old !== null && $this->membership->membersFromProject(project: (array)$old->getObject()) === $members) { - return; + $data = (array)$project->getObject(); + $old = $event->getOldObject(); + $oldData = null; + $projectId = (string)($project->getUuid() ?? ''); + if ($old !== null) { + $oldData = (array)$old->getObject(); } - $projectId = (string)($project->getUuid() ?? ''); - $written = $this->membership->syncProjectMembers(projectId: $projectId, members: $members); - $this->logger->info( - 'Planninq: project membership changed; members list updated on its objects', - ['project' => $projectId, 'written' => $written] - ); + $members = $this->membership->membersFromProject(project: $data); + if ($oldData === null || $this->membership->membersFromProject(project: $oldData) !== $members) { + $written = $this->membership->syncProjectMembers(projectId: $projectId, members: $members); + $this->logger->info( + 'Planninq: project membership changed; members list updated on its objects', + ['project' => $projectId, 'written' => $written] + ); + } + + $field = ProjectMembershipService::READERS_FIELD; + $readers = $this->membership->normalise(members: ($data[$field] ?? [])); + if ($oldData === null || $this->membership->normalise(members: ($oldData[$field] ?? [])) !== $readers) { + $this->membership->syncProjectMembers(projectId: $projectId, members: $readers, field: $field); + } } catch (\Throwable $e) { // The project write already happened and must not turn into an // error. The objects keep the previous list until the next change diff --git a/lib/Service/ProjectHealthService.php b/lib/Service/ProjectHealthService.php index 6705b042..9da69ebc 100644 --- a/lib/Service/ProjectHealthService.php +++ b/lib/Service/ProjectHealthService.php @@ -193,7 +193,7 @@ public function newestReport(string $projectId, string $reportId, ?array $saving * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 */ public function refresh(string $projectId, string $reportId, ?array $saving): bool { - $project = $this->membership->projectData(projectId: $projectId); + $project = $this->membership->objectData(schema: ProjectMembershipService::PROJECT_SCHEMA, id: $projectId); if ($project === null) { return false; } diff --git a/lib/Service/ProjectMembershipService.php b/lib/Service/ProjectMembershipService.php index 529b92c1..3e9e2b70 100644 --- a/lib/Service/ProjectMembershipService.php +++ b/lib/Service/ProjectMembershipService.php @@ -71,6 +71,13 @@ class ProjectMembershipService { */ public const PROJECT_SCHEMA = 'project'; + /** + * The field holding the managers of a project's portfolio, copied like `members`. + * + * @var string + */ + public const READERS_FIELD = 'portfolioReaders'; + /** * Schemas that carry a copy of their project's members. * @@ -92,6 +99,7 @@ class ProjectMembershipService { */ private const IN_CHUNK = 500; + /** * Members per project id for this request; null for a project that did not resolve. * @@ -249,21 +257,22 @@ public function ownerOfProject(string $projectId): ?string { }//end ownerOfProject() /** - * A project's stored data, read as the system, or null when it does not resolve. + * One planninq object's stored data, read as the system, or null when it does not resolve. * - * @param string $projectId The project UUID. + * @param string $schema The schema slug. + * @param string $id The UUID. * * @return array|null * - * @spec openspec/changes/portfolio-status-overview/tasks.md#task-1.2 + * @spec openspec/changes/projects-grouping-hierarchy-fields/tasks.md#task-1.3 */ - public function projectData(string $projectId): ?array { - if ($projectId === '') { + public function objectData(string $schema, string $id): ?array { + if ($id === '') { return null; } - return $this->findData(schema: self::PROJECT_SCHEMA, id: $projectId); - }//end projectData() + return $this->findData(schema: $schema, id: $id); + }//end objectData() /** * Every planninq object of one schema matching the filters, read with RBAC off. @@ -287,33 +296,52 @@ public function rows(string $schema, array $filters): array { * acting user changed the project, and only `members` changes on the object. * One object that refuses the write is logged and does not stop the rest. * - * @param string $projectId The project uuid. - * @param array $members The project's members list, from membersFromProject(). + * The same writes keep `portfolioReaders` in step: pass that field and the + * managers of the project's portfolio. + * + * @param string $projectId The project uuid. + * @param array $members The list, for `members` from membersFromProject(). + * @param string $field `members`, or READERS_FIELD for the portfolio readers. * * @return int The number of objects written. * * @spec openspec/specs/projects.md */ - public function syncProjectMembers(string $projectId, array $members): int { + public function syncProjectMembers(string $projectId, array $members, string $field = 'members'): int { + $members = $this->normalise(members: $members); + if ($field === 'members') { + $this->membersCache[$projectId] = $members; + } + + return $this->syncField(projectId: $projectId, field: $field, values: $members); + }//end syncProjectMembers() + + /** + * Write one access list to every object of a project that is out of step. + * + * @param string $projectId The project uuid. + * @param string $field `members` or `portfolioReaders`. + * @param array $values The normalised list. + * + * @return int The number of objects written. + */ + private function syncField(string $projectId, string $field, array $values): int { if ($projectId === '') { return 0; } - $members = $this->normalise(members: $members); - $this->membersCache[$projectId] = $members; - $objectService = $this->objectService(); $written = 0; foreach ($this->childrenOf(objectService: $objectService, projectId: $projectId) as $child) { - if ($this->normalise(members: ($child['data']['members'] ?? null)) === $members) { + if ($this->normalise(members: ($child['data'][$field] ?? null)) === $values) { continue; } - $written += $this->writeMembers(objectService: $objectService, child: $child, members: $members); + $written += $this->writeField(objectService: $objectService, child: $child, field: $field, values: $values); } return $written; - }//end syncProjectMembers() + }//end syncField() /** * Bring every project's objects in step: the upgrade back-fill. @@ -335,6 +363,11 @@ public function syncAll(): array { projectId: $project['id'], members: $this->membersFromProject(project: $project['data']) ); + $written += $this->syncProjectMembers( + projectId: $project['id'], + members: $this->normalise(members: ($project['data'][self::READERS_FIELD] ?? [])), + field: self::READERS_FIELD + ); } return ['projects' => $projects, 'written' => $written]; @@ -374,17 +407,18 @@ private function childrenOf(object $objectService, string $projectId): array { }//end childrenOf() /** - * Write one object's members list. + * Write one access list onto one object. * - * @param object $objectService OpenRegister's ObjectService. - * @param array{schema: string, id: string, data: array} $child The object. - * @param array $members The members list. + * @param object $objectService OpenRegister's ObjectService. + * @param array{schema: string, id: string, data: array} $child The object. + * @param string $field `members` or `portfolioReaders`. + * @param array $values The list. * * @return int 1 when written, 0 when the write was refused. */ - private function writeMembers(object $objectService, array $child, array $members): int { + private function writeField(object $objectService, array $child, string $field, array $values): int { $data = $child['data']; - $data['members'] = $members; + $data[$field] = $values; try { $objectService->saveObject( @@ -399,14 +433,14 @@ private function writeMembers(object $objectService, array $child, array $member ); } catch (\Throwable $e) { $this->logger->error( - 'Planninq: could not write the members list of a project object; members may not see it', - ['schema' => $child['schema'], 'object' => $child['id'], 'exception' => $e->getMessage()] + 'Planninq: could not write an access list of a project object; people may not see it', + ['schema' => $child['schema'], 'object' => $child['id'], 'field' => $field, 'exception' => $e->getMessage()] ); return 0; } return 1; - }//end writeMembers() + }//end writeField() /** * Search one planninq schema as the system. diff --git a/lib/Settings/planninq_mock_register.json b/lib/Settings/planninq_mock_register.json index 2dc3b8da..dbdd94fa 100644 --- a/lib/Settings/planninq_mock_register.json +++ b/lib/Settings/planninq_mock_register.json @@ -590,6 +590,45 @@ "statusRisk": "offTrack", "noteRisk": "Voorbeeld Risk note 3", "overall": "offTrack" + }, + { + "@self": { + "register": "planninq", + "schema": "projectPortfolio", + "slug": "portfolio-voorbeeld-1" + }, + "title": "Ruimte", + "description": "Voorbeeld portfolio Ruimte", + "color": "#2E7D32", + "managers": [], + "order": 0, + "riskScale": "" + }, + { + "@self": { + "register": "planninq", + "schema": "projectPortfolio", + "slug": "portfolio-voorbeeld-2" + }, + "title": "Dienstverlening", + "description": "Voorbeeld portfolio Dienstverlening", + "color": "#1565C0", + "managers": [], + "order": 1, + "riskScale": "" + }, + { + "@self": { + "register": "planninq", + "schema": "projectPortfolio", + "slug": "portfolio-voorbeeld-3" + }, + "title": "Bedrijfsvoering", + "description": "Voorbeeld portfolio Bedrijfsvoering", + "color": "#6A1B9A", + "managers": [], + "order": 2, + "riskScale": "" } ] } diff --git a/lib/Settings/planninq_register.json b/lib/Settings/planninq_register.json index 0adab247..50884e7b 100644 --- a/lib/Settings/planninq_register.json +++ b/lib/Settings/planninq_register.json @@ -3,7 +3,7 @@ "info": { "title": "Planninq Register", "description": "Register containing all schemas for the Planninq application.", - "version": "0.9.0" + "version": "0.10.0" }, "x-openregister": { "type": "application", @@ -18,7 +18,7 @@ "title": "Planninq", "description": "Planninq application data register — tasks, projects, columns, time entries, and labels.", "slug": "planninq", - "version": "0.9.0", + "version": "0.10.0", "schemas": [ "task", "project", @@ -30,7 +30,8 @@ "timetableSession", "projectLogEntry", "risk", - "projectStatusReport" + "projectStatusReport", + "projectPortfolio" ], "_note": "`tablePrefix` and `folder` stay on the PRE-RENAME `planix` deliberately, and are not part of this rename. They name PHYSICAL things that already exist: the folder is the Nextcloud Files node OpenRegister created for this register (RegisterService::ensureRegisterFolderExists() replaces a legacy string path with the numeric node id on first run), and moving either would strand what is already stored there rather than rename it. The slug is the only value the import resolves a register by; it is the only one that moves.", "tablePrefix": "planix", @@ -43,7 +44,7 @@ "task": { "slug": "task", "icon": "CheckboxMarkedCircleOutline", - "version": "0.4.0", + "version": "0.5.0", "title": "Task", "description": "A discrete unit of work. Maps to iCalendar VTODO (RFC 5545) and schema:PlanAction.", "x-schema-org": "schema:PlanAction", @@ -62,6 +63,14 @@ } } }, + { + "group": "authenticated", + "match": { + "portfolioReaders": { + "$contains": "$userId" + } + } + }, { "group": "admin" } @@ -187,6 +196,17 @@ "visible": false, "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." }, + "portfolioReaders": { + "title": "Portfolio readers", + "type": "array", + "description": "Nextcloud user UIDs of the managers of the project's portfolio", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the managers of the project's portfolio, who read the project and its objects without being members. ProjectPortfolioListener writes it on the project whenever the project moves or its portfolio's managers change, and ProjectMemberAccessListener copies it onto every project-scoped object (projects-grouping-hierarchy-fields, design decision 2)." + }, "phase": { "title": "Phase", "type": "string", @@ -362,7 +382,7 @@ "project": { "slug": "project", "icon": "FolderOutline", - "version": "0.4.0", + "version": "0.5.0", "title": "Project", "description": "A container grouping related tasks and kanban columns, optionally delivered for a client against a budget. Maps to schema:CreativeWork.", "x-schema-org": "schema:CreativeWork", @@ -381,6 +401,14 @@ } } }, + { + "group": "authenticated", + "match": { + "portfolioReaders": { + "$contains": "$userId" + } + } + }, { "group": "admin" } @@ -450,6 +478,14 @@ "type": "string", "description": "Nextcloud user UID of the project creator. Set automatically on creation; used for delete authorization and creator-based RBAC." }, + "portfolio": { + "title": "Portfolio", + "type": "string", + "description": "The portfolio this project belongs to", + "format": "uuid", + "$ref": "projectPortfolio", + "nullable": true + }, "members": { "title": "Members", "type": "array", @@ -459,6 +495,17 @@ }, "default": [] }, + "portfolioReaders": { + "title": "Portfolio readers", + "type": "array", + "description": "Nextcloud user UIDs of the managers of the project's portfolio", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the managers of the project's portfolio, who read the project and its objects without being members. ProjectPortfolioListener writes it on the project whenever the project moves or its portfolio's managers change, and ProjectMemberAccessListener copies it onto every project-scoped object (projects-grouping-hierarchy-fields, design decision 2)." + }, "contractorRefs": { "title": "Contractors", "type": "array", @@ -681,7 +728,7 @@ "projectPhase": { "slug": "projectPhase", "icon": "TimelineOutline", - "version": "0.2.0", + "version": "0.3.0", "title": "Project Phase", "description": "A delivery stage within a project, such as discovery, build or hypercare. Phases carry their own budget and billable flag, and tasks hang off them. Maps to schema:CreativeWork.", "x-schema-org": "schema:CreativeWork", @@ -700,6 +747,14 @@ } } }, + { + "group": "authenticated", + "match": { + "portfolioReaders": { + "$contains": "$userId" + } + } + }, { "group": "admin" } @@ -763,6 +818,17 @@ "visible": false, "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." }, + "portfolioReaders": { + "title": "Portfolio readers", + "type": "array", + "description": "Nextcloud user UIDs of the managers of the project's portfolio", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the managers of the project's portfolio, who read the project and its objects without being members. ProjectPortfolioListener writes it on the project whenever the project moves or its portfolio's managers change, and ProjectMemberAccessListener copies it onto every project-scoped object (projects-grouping-hierarchy-fields, design decision 2)." + }, "description": { "title": "Description", "type": "string", @@ -817,7 +883,7 @@ "column": { "slug": "column", "icon": "ViewColumnOutline", - "version": "0.3.0", + "version": "0.4.0", "title": "Column", "description": "A kanban column belonging to a project. Maps to schema:DefinedTerm.", "x-schema-org": "schema:DefinedTerm", @@ -837,6 +903,14 @@ } } }, + { + "group": "authenticated", + "match": { + "portfolioReaders": { + "$contains": "$userId" + } + } + }, { "group": "admin" } @@ -900,6 +974,17 @@ "visible": false, "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." }, + "portfolioReaders": { + "title": "Portfolio readers", + "type": "array", + "description": "Nextcloud user UIDs of the managers of the project's portfolio", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the managers of the project's portfolio, who read the project and its objects without being members. ProjectPortfolioListener writes it on the project whenever the project moves or its portfolio's managers change, and ProjectMemberAccessListener copies it onto every project-scoped object (projects-grouping-hierarchy-fields, design decision 2)." + }, "order": { "title": "Display Order", "type": "integer", @@ -946,7 +1031,7 @@ "plannedTimeEntry": { "slug": "plannedTimeEntry", "icon": "TimerOutline", - "version": "0.3.0", + "version": "0.4.0", "title": "Time Entry", "description": "Time logged by a user against a task. Maps to schema:QuantitativeValue.", "x-schema-org": "schema:QuantitativeValue", @@ -973,6 +1058,14 @@ } } }, + { + "group": "authenticated", + "match": { + "portfolioReaders": { + "$contains": "$userId" + } + } + }, { "group": "admin" } @@ -1076,6 +1169,17 @@ "visible": false, "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." }, + "portfolioReaders": { + "title": "Portfolio readers", + "type": "array", + "description": "Nextcloud user UIDs of the managers of the project's portfolio", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the managers of the project's portfolio, who read the project and its objects without being members. ProjectPortfolioListener writes it on the project whenever the project moves or its portfolio's managers change, and ProjectMemberAccessListener copies it onto every project-scoped object (projects-grouping-hierarchy-fields, design decision 2)." + }, "billable": { "title": "Billable", "type": "boolean", @@ -1325,7 +1429,7 @@ "projectLogEntry": { "slug": "projectLogEntry", "icon": "NotebookOutline", - "version": "0.1.0", + "version": "0.2.0", "title": "Log entry", "description": "An issue, lesson learned, meeting or decision recorded in a project's log.", "x-schema-org": "schema:Comment", @@ -1346,6 +1450,14 @@ } } }, + { + "group": "authenticated", + "match": { + "portfolioReaders": { + "$contains": "$userId" + } + } + }, { "group": "admin" } @@ -1409,6 +1521,17 @@ "visible": false, "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." }, + "portfolioReaders": { + "title": "Portfolio readers", + "type": "array", + "description": "Nextcloud user UIDs of the managers of the project's portfolio", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the managers of the project's portfolio, who read the project and its objects without being members. ProjectPortfolioListener writes it on the project whenever the project moves or its portfolio's managers change, and ProjectMemberAccessListener copies it onto every project-scoped object (projects-grouping-hierarchy-fields, design decision 2)." + }, "type": { "title": "Type", "type": "string", @@ -1477,7 +1600,7 @@ "risk": { "slug": "risk", "icon": "AlertOutline", - "version": "0.1.0", + "version": "0.2.0", "title": "Risk", "description": "A risk to a project, scored on likelihood and impact, with an owner, a response and its countermeasures.", "x-schema-org": "schema:Thing", @@ -1498,6 +1621,14 @@ } } }, + { + "group": "authenticated", + "match": { + "portfolioReaders": { + "$contains": "$userId" + } + } + }, { "group": "admin" } @@ -1577,6 +1708,17 @@ "visible": false, "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." }, + "portfolioReaders": { + "title": "Portfolio readers", + "type": "array", + "description": "Nextcloud user UIDs of the managers of the project's portfolio", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the managers of the project's portfolio, who read the project and its objects without being members. ProjectPortfolioListener writes it on the project whenever the project moves or its portfolio's managers change, and ProjectMemberAccessListener copies it onto every project-scoped object (projects-grouping-hierarchy-fields, design decision 2)." + }, "description": { "title": "Description", "type": "string", @@ -1664,7 +1806,7 @@ "projectStatusReport": { "slug": "projectStatusReport", "icon": "ClipboardCheckOutline", - "version": "0.1.0", + "version": "0.2.0", "title": "Project status report", "description": "A dated report on how a project stands on money, organisation, time, information, quality and risk (the GROTIK aspects), each with a note.", "x-schema-org": "schema:Report", @@ -1689,6 +1831,14 @@ } } }, + { + "group": "authenticated", + "match": { + "portfolioReaders": { + "$contains": "$userId" + } + } + }, { "group": "admin" } @@ -1898,6 +2048,17 @@ "visible": false, "x-notes": "Kept by planninq, never edited by a person: the project's members plus its owner. It is this object's access rule, {\"members\": {\"$contains\": \"$userId\"}}, because OpenRegister cannot match across schemas. ProjectMemberAccessListener writes it on every create and update, ProjectMembershipSyncListener when the project's membership changes, and the BackfillProjectMembers repair step on upgrade (planninq#681)." }, + "portfolioReaders": { + "title": "Portfolio readers", + "type": "array", + "description": "Nextcloud user UIDs of the managers of the project's portfolio", + "items": { + "type": "string" + }, + "default": [], + "visible": false, + "x-notes": "Kept by planninq, never edited by a person: the managers of the project's portfolio, who read the project and its objects without being members. ProjectPortfolioListener writes it on the project whenever the project moves or its portfolio's managers change, and ProjectMemberAccessListener copies it onto every project-scoped object (projects-grouping-hierarchy-fields, design decision 2)." + }, "reportDate": { "title": "Report date", "type": "string", @@ -2041,6 +2202,84 @@ "x-notes": "Materialised by OpenRegister from x-openregister-calculations on every save; an overall a client sends is overwritten (portfolio-status-overview, design decision 1)." } } + }, + "projectPortfolio": { + "slug": "projectPortfolio", + "icon": "FolderMultipleOutline", + "version": "0.1.0", + "title": "Portfolio", + "description": "A group of projects run by the same people, such as a department or a programme. Its managers read every project in it.", + "x-schema-org": "schema:Collection", + "type": "object", + "required": [ + "title" + ], + "authorization": { + "read": [ + { + "group": "authenticated" + }, + { + "group": "admin" + } + ], + "create": [ + "admin" + ], + "update": [ + { + "group": "authenticated", + "match": { + "managers": { + "$contains": "$userId" + } + } + }, + { + "group": "admin" + } + ], + "delete": [ + "admin" + ] + }, + "properties": { + "title": { + "title": "Title", + "type": "string", + "description": "The name of the portfolio" + }, + "description": { + "title": "Description", + "type": "string", + "description": "What the portfolio holds and who runs it" + }, + "color": { + "title": "Color", + "type": "string", + "description": "A colour for the portfolio's section in project lists" + }, + "managers": { + "title": "Managers", + "type": "array", + "description": "Nextcloud user UIDs of the people who run the portfolio", + "items": { + "type": "string" + }, + "default": [] + }, + "order": { + "title": "Order", + "type": "integer", + "description": "Where the portfolio comes in project lists, lowest first", + "default": 0 + }, + "riskScale": { + "title": "Risk scale", + "type": "string", + "description": "This portfolio's own risk scale as JSON, in the same shape as the app-wide one. Empty means the app-wide scale." + } + } } }, "objects": [ diff --git a/openspec/changes/projects-grouping-hierarchy-fields/design.md b/openspec/changes/projects-grouping-hierarchy-fields/design.md index d1f1cfe4..6d966104 100644 --- a/openspec/changes/projects-grouping-hierarchy-fields/design.md +++ b/openspec/changes/projects-grouping-hierarchy-fields/design.md @@ -63,3 +63,15 @@ Alternative considered: letting admins add properties to the `project` schema it - [A deleted portfolio strands its projects] -> Deleting a portfolio clears `project.portfolio` and `portfolioReaders` on its projects in the same listener pass, and the dialog says how many projects that affects. - [A deleted field definition leaves values behind] -> Values stay in `customFields` but are not shown. Restoring a definition with the same `key` brings them back. - [Project lists get slower with grouping and trees] -> Grouping and nesting are done in the browser over the list `fetchProjects` already loads. Nothing new is fetched per group. + +## Built at HEAD, section 1 (29 Sep 2026) + +The code at `c4a6694` settled three questions this design left open. + +- **The project-scoped mechanism** (Decision 2 pointed at `projects-members-and-roles` task 1.1, not built) is the denormalised list planninq#681 introduced for `members`. Every project-scoped schema (`task`, `column`, `projectPhase`, `plannedTimeEntry`, `projectLogEntry`, `risk`, `projectStatusReport`) gains a hidden `portfolioReaders` list and a read-only rule `{"portfolioReaders": {"$contains": "$userId"}}`; update and delete stay members-only. `ProjectMemberAccessListener` stamps the list from the project on every write, `ProjectMembershipSyncListener` copies a changed list to the project's objects, and `BackfillProjectMembers` fills it on upgrade. +- **Who may write the derived list.** `ProjectHierarchyGuardListener` sets `project.portfolioReaders` to the portfolio's managers on every project create and update from a person, whatever the client sent. When a portfolio's managers change or it is deleted, it writes the projects inside OpenRegister's `SystemOperationContext` (which it trusts, and which raises no post-event) and copies the list to each project's objects itself. All of it runs on pre-events (ADR-078). +- **Where portfolios are managed.** A declarative index page, Projects > Portfolios (`/projects/portfolios`), over the portfolio schema. OpenRegister enforces the rights: every signed-in user reads, admins create and delete, managers edit. The portfolio's own risk scale is a JSON string on the portfolio in the shape of the app-wide `risk_scale`; the Risks tab uses it when set. A dedicated editor for it is not built. + +The board opens read-only for a portfolio manager who is not on the project: a note says so, cards do not drag and the move menu is hidden. The server refuses their writes the same way. There is no role helper yet (`projects-members-and-roles`), so the rule lives in `isReadOnlyFor()` in `src/utils/portfolioGrouping.js`. + +- **The schema slug is `projectPortfolio`, not `portfolio`.** learniq owns the `portfolio` slug (a learner's portfolio), and slugs are global on a shared OpenRegister (gate-106). The project property stays `project.portfolio`; only the schema slug carries the qualifier. diff --git a/openspec/changes/projects-grouping-hierarchy-fields/tasks.md b/openspec/changes/projects-grouping-hierarchy-fields/tasks.md index 0af87b35..eef99660 100644 --- a/openspec/changes/projects-grouping-hierarchy-fields/tasks.md +++ b/openspec/changes/projects-grouping-hierarchy-fields/tasks.md @@ -2,13 +2,15 @@ ## 1. Portfolios (Enterprise) -- [ ] 1.1 Add the `portfolio` schema and `project.portfolio` and `project.portfolioReaders`, with the read rule on `portfolioReaders`. Verify: `tests/unit/Settings/PlanninqRegisterSchemaTest.php` asserts the schema, the properties and the read rule. -- [ ] 1.2 Group the project list and the board picker by portfolio, with foldable sections and a portfolio filter. Verify: `tests/vitest/projectGrouping.spec.js` for the grouping and ordering; `tests/e2e/project-portfolios.spec.ts` "the project list groups by portfolio" and "a project manager moves a project into a portfolio". -- [ ] 1.3 Add `ProjectHierarchyGuardListener` for `ObjectCreatingEvent` and `ObjectUpdatingEvent` on `project`, writing `portfolioReaders`, and rewrite it on every project when a portfolio's managers change or the portfolio is deleted. Verify: `tests/unit/Listener/ProjectHierarchyGuardListenerTest.php` constructs the real OpenRegister event classes and covers a move, a manager change, a deletion and a client-sent `portfolioReaders`. -- [ ] 1.4 Use the portfolio's `riskScale` in the Risks tab when set. Verify: e2e "a portfolio with a three-level scale". -- [ ] 1.5 Update `testRegisterDeclaresExactlySevenSchemas` and `openspec/specs/project-delivery/spec.md:68-72` for `portfolio` and `fieldDefinition`. Verify: the PHPUnit suite passes. +- [x] 1.1 Add the `portfolio` schema and `project.portfolio` and `project.portfolioReaders`, with the read rule on `portfolioReaders`. Verify: `tests/unit/Settings/PlanninqRegisterSchemaTest.php` asserts the schema, the properties and the read rule. +- [x] 1.2 Group the project list and the board picker by portfolio, with foldable sections and a portfolio filter. Verify: `tests/vitest/projectGrouping.spec.js` for the grouping and ordering; `tests/e2e/project-portfolios.spec.ts` "the project list groups by portfolio" and "a project manager moves a project into a portfolio". +- [x] 1.3 Add `ProjectHierarchyGuardListener` for `ObjectCreatingEvent` and `ObjectUpdatingEvent` on `project`, writing `portfolioReaders`, and rewrite it on every project when a portfolio's managers change or the portfolio is deleted. Verify: `tests/unit/Listener/ProjectHierarchyGuardListenerTest.php` constructs the real OpenRegister event classes and covers a move, a manager change, a deletion and a client-sent `portfolioReaders`. +- [x] 1.4 Use the portfolio's `riskScale` in the Risks tab when set. Verify: e2e "a portfolio with a three-level scale". +- [x] 1.5 Update `testRegisterDeclaresExactlySevenSchemas` and `openspec/specs/project-delivery/spec.md:68-72` for `portfolio` and `fieldDefinition`. Verify: the PHPUnit suite passes. - [ ] 1.6 Check live that a portfolio manager who is not a member reads a project in their portfolio and cannot write it. Verify: e2e "a portfolio manager sees a project they are not on". +Section 1 was built in one PR (see design, "Built at HEAD, section 1"). 1.5 added only `portfolio` (the register test is now `testRegisterDeclaresExactlyTwelveSchemas`); `fieldDefinition` comes with 3.1. 1.6 stays open: the e2e suite has no second, non-admin account, so the check is a live recipe in the PR, with the read rule asserted by `PlanninqRegisterSchemaTest::testPortfolioSchemaAndTheProjectReaderRule` and the stamping by `ProjectHierarchyGuardListenerTest`. + ## 2. Subprojects (Enterprise) - [ ] 2.1 Add `project.parent` and the cycle and depth checks to the guard listener. Verify: `ProjectHierarchyGuardListenerTest.php` cases for self, descendant and a fourth level; e2e `tests/e2e/project-hierarchy.spec.ts` "a cycle is refused". diff --git a/openspec/specs/project-delivery/spec.md b/openspec/specs/project-delivery/spec.md index 34e377d7..bff8548f 100644 --- a/openspec/specs/project-delivery/spec.md +++ b/openspec/specs/project-delivery/spec.md @@ -65,11 +65,11 @@ project. project-scoped schemas, because a phase is exactly as sensitive as the project it belongs to. -#### Scenario: The register declares exactly eleven schemas -@e2e exclude Descriptor contents, asserted by PHPUnit (testRegisterDeclaresExactlyElevenSchemas) +#### Scenario: The register declares exactly twelve schemas +@e2e exclude Descriptor contents, asserted by PHPUnit (testRegisterDeclaresExactlyTwelveSchemas) - GIVEN the planninq register descriptor - WHEN its schema list is read -- THEN it MUST be exactly task, project, projectPhase, column, plannedTimeEntry, label, dependency, timetableSession, projectLogEntry, risk and projectStatusReport +- THEN it MUST be exactly task, project, projectPhase, column, plannedTimeEntry, label, dependency, timetableSession, projectLogEntry, risk, projectStatusReport and projectPortfolio #### Scenario: An unphased project still works @e2e exclude Schema shape, asserted by PHPUnit against the register descriptor diff --git a/src/components/PortfolioSections.vue b/src/components/PortfolioSections.vue new file mode 100644 index 00000000..e4f8d06d --- /dev/null +++ b/src/components/PortfolioSections.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/src/components/ProjectSettingsSidebar.vue b/src/components/ProjectSettingsSidebar.vue index 8f78314d..3955588a 100644 --- a/src/components/ProjectSettingsSidebar.vue +++ b/src/components/ProjectSettingsSidebar.vue @@ -44,6 +44,15 @@ :label="t('planninq', 'Icon (emoji)')" :placeholder="t('planninq', 'e.g. 📁 🚀')" /> + + +