diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 92bba7fb16..d1b6be8885 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -134,6 +134,15 @@ dependencies: '@rush-temp/contact-resources': specifier: file:./projects/contact-resources.tgz version: file:projects/contact-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) + '@rush-temp/controlled-documents': + specifier: file:./projects/controlled-documents.tgz + version: file:projects/controlled-documents.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/controlled-documents-assets': + specifier: file:./projects/controlled-documents-assets.tgz + version: file:projects/controlled-documents-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/controlled-documents-resources': + specifier: file:./projects/controlled-documents-resources.tgz + version: file:projects/controlled-documents-resources.tgz(@tiptap/pm@2.2.4)(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) '@rush-temp/core': specifier: file:./projects/core.tgz version: file:projects/core.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) @@ -263,6 +272,9 @@ dependencies: '@rush-temp/model-contact': specifier: file:./projects/model-contact.tgz version: file:projects/model-contact.tgz + '@rush-temp/model-controlled-documents': + specifier: file:./projects/model-controlled-documents.tgz + version: file:projects/model-controlled-documents.tgz '@rush-temp/model-core': specifier: file:./projects/model-core.tgz version: file:projects/model-core.tgz @@ -296,6 +308,15 @@ dependencies: '@rush-temp/model-presentation': specifier: file:./projects/model-presentation.tgz version: file:projects/model-presentation.tgz + '@rush-temp/model-print': + specifier: file:./projects/model-print.tgz + version: file:projects/model-print.tgz + '@rush-temp/model-products': + specifier: file:./projects/model-products.tgz + version: file:projects/model-products.tgz + '@rush-temp/model-questions': + specifier: file:./projects/model-questions.tgz + version: file:projects/model-questions.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/model-recruit': specifier: file:./projects/model-recruit.tgz version: file:projects/model-recruit.tgz @@ -320,6 +341,9 @@ dependencies: '@rush-temp/model-server-contact': specifier: file:./projects/model-server-contact.tgz version: file:projects/model-server-contact.tgz + '@rush-temp/model-server-controlled-documents': + specifier: file:./projects/model-server-controlled-documents.tgz + version: file:projects/model-server-controlled-documents.tgz '@rush-temp/model-server-core': specifier: file:./projects/model-server-core.tgz version: file:projects/model-server-core.tgz @@ -350,6 +374,9 @@ dependencies: '@rush-temp/model-server-openai': specifier: file:./projects/model-server-openai.tgz version: file:projects/model-server-openai.tgz + '@rush-temp/model-server-products': + specifier: file:./projects/model-server-products.tgz + version: file:projects/model-server-products.tgz '@rush-temp/model-server-recruit': specifier: file:./projects/model-server-recruit.tgz version: file:projects/model-server-recruit.tgz @@ -377,6 +404,9 @@ dependencies: '@rush-temp/model-server-tracker': specifier: file:./projects/model-server-tracker.tgz version: file:projects/model-server-tracker.tgz + '@rush-temp/model-server-training': + specifier: file:./projects/model-server-training.tgz + version: file:projects/model-server-training.tgz '@rush-temp/model-server-translate': specifier: file:./projects/model-server-translate.tgz version: file:projects/model-server-translate.tgz @@ -410,6 +440,9 @@ dependencies: '@rush-temp/model-tracker': specifier: file:./projects/model-tracker.tgz version: file:projects/model-tracker.tgz + '@rush-temp/model-training': + specifier: file:./projects/model-training.tgz + version: file:projects/model-training.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/model-view': specifier: file:./projects/model-view.tgz version: file:projects/model-view.tgz @@ -464,12 +497,39 @@ dependencies: '@rush-temp/presentation': specifier: file:./projects/presentation.tgz version: file:projects/presentation.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) + '@rush-temp/print': + specifier: file:./projects/print.tgz + version: file:projects/print.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2) + '@rush-temp/print-assets': + specifier: file:./projects/print-assets.tgz + version: file:projects/print-assets.tgz(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2) + '@rush-temp/print-resources': + specifier: file:./projects/print-resources.tgz + version: file:projects/print-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) '@rush-temp/prod': specifier: file:./projects/prod.tgz version: file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.3) + '@rush-temp/products': + specifier: file:./projects/products.tgz + version: file:projects/products.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/products-assets': + specifier: file:./projects/products-assets.tgz + version: file:projects/products-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/products-resources': + specifier: file:./projects/products-resources.tgz + version: file:projects/products-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) '@rush-temp/query': specifier: file:./projects/query.tgz version: file:projects/query.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/questions': + specifier: file:./projects/questions.tgz + version: file:projects/questions.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/questions-assets': + specifier: file:./projects/questions-assets.tgz + version: file:projects/questions-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/questions-resources': + specifier: file:./projects/questions-resources.tgz + version: file:projects/questions-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) '@rush-temp/rank': specifier: file:./projects/rank.tgz version: file:projects/rank.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) @@ -542,6 +602,12 @@ dependencies: '@rush-temp/server-contact-resources': specifier: file:./projects/server-contact-resources.tgz version: file:projects/server-contact-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/server-controlled-documents': + specifier: file:./projects/server-controlled-documents.tgz + version: file:projects/server-controlled-documents.tgz(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/server-controlled-documents-resources': + specifier: file:./projects/server-controlled-documents-resources.tgz + version: file:projects/server-controlled-documents-resources.tgz(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/server-core': specifier: file:./projects/server-core.tgz version: file:projects/server-core.tgz(esbuild@0.20.1)(ts-node@10.9.2) @@ -656,6 +722,12 @@ dependencies: '@rush-temp/server-tracker-resources': specifier: file:./projects/server-tracker-resources.tgz version: file:projects/server-tracker-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/server-training': + specifier: file:./projects/server-training.tgz + version: file:projects/server-training.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/server-training-resources': + specifier: file:./projects/server-training-resources.tgz + version: file:projects/server-training-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/server-view': specifier: file:./projects/server-view.tgz version: file:projects/server-view.tgz(esbuild@0.20.1)(ts-node@10.9.2) @@ -674,6 +746,9 @@ dependencies: '@rush-temp/setting-resources': specifier: file:./projects/setting-resources.tgz version: file:projects/setting-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) + '@rush-temp/sign': + specifier: file:./projects/sign.tgz + version: file:projects/sign.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2) '@rush-temp/storage': specifier: file:./projects/storage.tgz version: file:projects/storage.tgz(esbuild@0.20.1)(ts-node@10.9.2) @@ -758,6 +833,15 @@ dependencies: '@rush-temp/tracker-resources': specifier: file:./projects/tracker-resources.tgz version: file:projects/tracker-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) + '@rush-temp/training': + specifier: file:./projects/training.tgz + version: file:projects/training.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/training-assets': + specifier: file:./projects/training-assets.tgz + version: file:projects/training-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/training-resources': + specifier: file:./projects/training-resources.tgz + version: file:projects/training-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) '@rush-temp/translate': specifier: file:./projects/translate.tgz version: file:projects/translate.tgz(esbuild@0.20.1)(ts-node@10.9.2) @@ -1058,6 +1142,9 @@ dependencies: dotenv-webpack: specifier: ^8.0.1 version: 8.0.1(webpack@5.90.3) + effector: + specifier: ~22.8.7 + version: 22.8.8 elastic-apm-node: specifier: ~3.26.0 version: 3.26.0 @@ -10135,6 +10222,11 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false + /effector@22.8.8: + resolution: {integrity: sha512-uqYEPt/jIZ3Y1WhpyED1XuXAJNsVM5TcWZvcyQhl6nYKRynklGiu+Fy/BnLQftmMcxZS478laAuExUPfuOGiOg==} + engines: {node: '>=11.0.0'} + dev: false + /ejs@3.1.9: resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} engines: {node: '>=0.10.0'} @@ -19491,6 +19583,121 @@ packages: - ts-node dev: false + file:projects/controlled-documents-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-Z9r9RWrlieZYmYJTJD173VvEFK9I/Pjr9EGi+CBOkzgRIlxF5hi377t5LbwxHCbuGooUcqyeHppi+8RfJqAlsA==, tarball: file:projects/controlled-documents-assets.tgz} + id: file:projects/controlled-documents-assets.tgz + name: '@rush-temp/controlled-documents-assets' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + + file:projects/controlled-documents-resources.tgz(@tiptap/pm@2.2.4)(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2): + resolution: {integrity: sha512-IQolWd9tfsGpGsV+c9VPRZE/j6mtyaDAcUYYlqG1DscpIa4+ogvCrPotL+jQp48T3EO8GAqGxJwieXWRKbO0OA==, tarball: file:projects/controlled-documents-resources.tgz} + id: file:projects/controlled-documents-resources.tgz + name: '@rush-temp/controlled-documents-resources' + version: 0.0.0 + dependencies: + '@tiptap/core': 2.2.4(@tiptap/pm@2.2.4) + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + effector: 22.8.8 + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) + fast-equals: 5.0.1 + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + sass: 1.71.1 + slugify: 1.6.6 + svelte: 4.2.12 + svelte-check: 3.6.9(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12) + svelte-eslint-parser: 0.33.1(svelte@4.2.12) + svelte-loader: 3.2.0(svelte@4.2.12) + svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12)(typescript@5.3.3) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + yjs: 13.6.12 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@tiptap/pm' + - '@types/node' + - babel-jest + - babel-plugin-macros + - coffeescript + - esbuild + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + - ts-node + dev: false + + file:projects/controlled-documents.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-G2jP+iUJ5ax78nnOETkPwrAnNk6eGtRntEjAY6DXfoXbxzd2k4SqumPteI99dY6/EWiIIqmsq13s5zPVORvJvA==, tarball: file:projects/controlled-documents.tgz} + id: file:projects/controlled-documents.tgz + name: '@rush-temp/controlled-documents' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + lexorank: 1.0.5 + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + file:projects/core.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): resolution: {integrity: sha512-hfCFq02/du3ge1FsqKxAcxp042TYZzNT3e78bLz06dD+bZhfLBhUKUfk6a7GYklvyGpQak7ofWksYXjUXgD/iA==, tarball: file:projects/core.tgz} id: file:projects/core.tgz @@ -20900,6 +21107,25 @@ packages: - supports-color dev: false + file:projects/model-controlled-documents.tgz: + resolution: {integrity: sha512-drhvkfN7Uk6nNFnmVgff+hqu0xlHYA3yExP3tBOgA+xjmH3NaOc4yvzXTelobGwNHIAiKZCh9O3412ZbKNc6Jw==, tarball: file:projects/model-controlled-documents.tgz} + name: '@rush-temp/model-controlled-documents' + version: 0.0.0 + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + prettier: 3.2.5 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: false + file:projects/model-core.tgz: resolution: {integrity: sha512-MqFClhVkMJnTAdP3J/Z3FFONrX/3em+kRE3oarmHsOdeEVl/mgODN06nH23zPVRGxheC2PrAjW0PwoZo1Dk2/w==, tarball: file:projects/model-core.tgz} name: '@rush-temp/model-core' @@ -21108,6 +21334,77 @@ packages: - supports-color dev: false + file:projects/model-print.tgz: + resolution: {integrity: sha512-u/t8Jl8EIVszIffLkznvGqP58+iuskC6H9FM2QKvmgiJy6lshiwAYawjQwpYLp2t6v1f3fKozQoO0KUCl9cphQ==, tarball: file:projects/model-print.tgz} + name: '@rush-temp/model-print' + version: 0.0.0 + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + prettier: 3.2.5 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: false + + file:projects/model-products.tgz: + resolution: {integrity: sha512-36MZPJwrg3v0XHO/pWJtBuq440iWqqJRPF51dWvyyC49eSmQUl+QVyrugc2lTQxsdF+aUJeGzQDCQ0m5Amtyuw==, tarball: file:projects/model-products.tgz} + name: '@rush-temp/model-products' + version: 0.0.0 + dependencies: + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + prettier: 3.2.5 + ts-node: 10.9.2(@types/node@20.11.19)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - supports-color + dev: false + + file:projects/model-questions.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-oPpUgOD35MD+hcbCbvAAjbVrhtqhh6Pe1V9xRsQduWpjsbHGaQMxGVq/eKPcEjj749gyY1Jl2Z6VR0MFukuejg==, tarball: file:projects/model-questions.tgz} + id: file:projects/model-questions.tgz + name: '@rush-temp/model-questions' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + file:projects/model-recruit.tgz: resolution: {integrity: sha512-xylfMWvlyhc9HaC19AKbkMYCL3nCp40c/9npYgIEUPYOVa57it9ZpRG7xTfTkDSPv+XSy8ghXXorjDbS59yzQg==, tarball: file:projects/model-recruit.tgz} name: '@rush-temp/model-recruit' @@ -21261,6 +21558,25 @@ packages: - supports-color dev: false + file:projects/model-server-controlled-documents.tgz: + resolution: {integrity: sha512-O5gLxpVk/VzAfIVn6vA0PeasocwtVWuGjuVuPIpscOXgRFFZ/JM7D48Om129Q6lohczfjjEmZblZ8Nz/nOqsuA==, tarball: file:projects/model-server-controlled-documents.tgz} + name: '@rush-temp/model-server-controlled-documents' + version: 0.0.0 + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + prettier: 3.2.5 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: false + file:projects/model-server-core.tgz: resolution: {integrity: sha512-bG3yLaoMfAhSFcqk65rNtwZOJF6rQcU9IO/xOq4ITa0sn6vahHR3weUSYCIdf1VaEvEx5i8Z7N1ixSOx0w+pxA==, tarball: file:projects/model-server-core.tgz} name: '@rush-temp/model-server-core' @@ -21451,6 +21767,25 @@ packages: - supports-color dev: false + file:projects/model-server-products.tgz: + resolution: {integrity: sha512-MWHqkHpcKEFSTmJuvN9nU6T98V1MLn3YXYcARDGxMW7RCNpA7Pp2PvkxozL/sP99+rKMFwBClyaYhlWwlbvqwA==, tarball: file:projects/model-server-products.tgz} + name: '@rush-temp/model-server-products' + version: 0.0.0 + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + prettier: 3.2.5 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: false + file:projects/model-server-recruit.tgz: resolution: {integrity: sha512-PNuh9lp7FZ8L7nd2aP15jbJURG2OTRS/GXV+b6LddYHULQlNdLi0lEFP5OAMzOykm+VYPEGoyK/cdgauSy2UhA==, tarball: file:projects/model-server-recruit.tgz} name: '@rush-temp/model-server-recruit' @@ -21622,6 +21957,25 @@ packages: - supports-color dev: false + file:projects/model-server-training.tgz: + resolution: {integrity: sha512-HJGq75RMdmrg1APWG6HhCYkbdrufZ6YOESJHHA4X1Ov3N+QigLmudu46nq6oyijfJAhm2Cz45svQinQNeyXzkQ==, tarball: file:projects/model-server-training.tgz} + name: '@rush-temp/model-server-training' + version: 0.0.0 + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + prettier: 3.2.5 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: false + file:projects/model-server-translate.tgz: resolution: {integrity: sha512-8/OSoehpx+/4l2bwpXQe16sz0qQYYJ0IFC4n0DglB+okVxN1A6FIGoLMbqiYGFSa90iiepduMtXq393dS1slSA==, tarball: file:projects/model-server-translate.tgz} name: '@rush-temp/model-server-translate' @@ -21831,6 +22185,36 @@ packages: - supports-color dev: false + file:projects/model-training.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-HHXV4MJC65/LpRxdAUzexVShcVEAlvwBA7R9CdZoSE8hLE4MFsY9QwT2tujFnYAsKD7IXPU9vOsD5nQcXYZ/CQ==, tarball: file:projects/model-training.tgz} + id: file:projects/model-training.tgz + name: '@rush-temp/model-training' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + file:projects/model-view.tgz: resolution: {integrity: sha512-N8MhTksyqcATLZxCtHM6x0uAbdOedZ4tq0rEXsQqZA91iryblx/TODXiUjXbTPXrO7/odSCLKTId8K8gMNW/Vg==, tarball: file:projects/model-view.tgz} name: '@rush-temp/model-view' @@ -22352,7 +22736,7 @@ packages: dev: false file:projects/pod-server.tgz: - resolution: {integrity: sha512-On9iUbvTRMtKD+Eqe4/LgKjk4y8MwXP/VaqQ+5nnmOra1nXnMQHwkaNnT7eYhWLoH4rM88JN9RyifT8UmF7dQQ==, tarball: file:projects/pod-server.tgz} + resolution: {integrity: sha512-yo0cpcy3zneQGR3O47VO6G/jusFUsmVlFLigKzkDD1PRetuC/AtBJdSjK5sKKiTNDFu6LL//wlAcOF3OMXzAhQ==, tarball: file:projects/pod-server.tgz} name: '@rush-temp/pod-server' version: 0.0.0 dependencies: @@ -22496,8 +22880,117 @@ packages: - ts-node dev: false + file:projects/print-assets.tgz(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2): + resolution: {integrity: sha512-0w4ixujsd3G9TOiwcGqJCuzozUnjD1pqptmNhcCjoQo2WbqGPr+0XMbM3yzyAKxTPJZQbzXp6grMR4758rk1LQ==, tarball: file:projects/print-assets.tgz} + id: file:projects/print-assets.tgz + name: '@rush-temp/print-assets' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - svelte + - ts-node + dev: false + + file:projects/print-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2): + resolution: {integrity: sha512-b4IXJJu/Ihtv0tAaRWTB3QZfqHBq3/Cln/KxlX1PelTNbLpRttzggfyvt5CFbTOR2029U23z3KPdddKGQDCJNw==, tarball: file:projects/print-resources.tgz} + id: file:projects/print-resources.tgz + name: '@rush-temp/print-resources' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + sass: 1.71.1 + svelte: 4.2.12 + svelte-check: 3.6.9(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12) + svelte-eslint-parser: 0.33.1(svelte@4.2.12) + svelte-loader: 3.2.0(svelte@4.2.12) + svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12)(typescript@5.3.3) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - coffeescript + - esbuild + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + - ts-node + dev: false + + file:projects/print.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2): + resolution: {integrity: sha512-FAHP30sSwVle3JMkXH2xJl5A8BLUivgmebbDV692ioncGAF6yhairLZ+rO3s5zAxjFsWTFTmetkfvenafOFypw==, tarball: file:projects/print.tgz} + id: file:projects/print.tgz + name: '@rush-temp/print' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - svelte + - ts-node + dev: false + file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.3): - resolution: {integrity: sha512-PgH0PbsoN0q+n48ghZJrY6pRprrEUTaGK7z1o+5XMmxRKgbT3OCFTn9xhEJxIylL4o96vr1r5WLiQupebAU//w==, tarball: file:projects/prod.tgz} + resolution: {integrity: sha512-ajlwjCy+8Cb6To3+gHmC4iRhhFBUMgCmSFkasybI2sbQDwJQgcK8mWSGobfr//sQORFOHY5uhTWBndpTIVdErA==, tarball: file:projects/prod.tgz} id: file:projects/prod.tgz name: '@rush-temp/prod' version: 0.0.0 @@ -22554,6 +23047,115 @@ packages: - vue-template-compiler dev: false + file:projects/products-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-9N54lXnCSdLtnlxCKubAZCPr/VffZRH+TVLwidvLJrj5P1m+mOakZS6ykefNzpxPMYbrujpYyQIpEyNInrVJ1w==, tarball: file:projects/products-assets.tgz} + id: file:projects/products-assets.tgz + name: '@rush-temp/products-assets' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + + file:projects/products-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2): + resolution: {integrity: sha512-XOYNBsSY9uJCV/ol7qxqs+OGDeT2dTfM558UpStOvCNsBVhnBelK49GfOFC+mr/BhDtIJeHHkTbRfy2Y2liyEQ==, tarball: file:projects/products-resources.tgz} + id: file:projects/products-resources.tgz + name: '@rush-temp/products-resources' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) + fast-equals: 5.0.1 + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + sass: 1.71.1 + svelte: 4.2.12 + svelte-check: 3.6.9(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12) + svelte-eslint-parser: 0.33.1(svelte@4.2.12) + svelte-loader: 3.2.0(svelte@4.2.12) + svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12)(typescript@5.3.3) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - coffeescript + - esbuild + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + - ts-node + dev: false + + file:projects/products.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-vJBceYHBTpquDhN82sN5PiVZdKWcVQ+40J7OP7spTnUJ6sKwT+juR+QYDa+Yy6PpPBmEkt2rHqI7oasczL6ULQ==, tarball: file:projects/products.tgz} + id: file:projects/products.tgz + name: '@rush-temp/products' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + file:projects/query.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): resolution: {integrity: sha512-EL/z5iBsZ04802G2QN9HCGerWMIUFHfRJ4WXbjPtBRs+GvsPP5btRmbs49o3OIIPlLCX1mSjik15jyYSIDclyg==, tarball: file:projects/query.tgz} id: file:projects/query.tgz @@ -22587,6 +23189,113 @@ packages: - ts-node dev: false + file:projects/questions-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-ZDwjZ63RvEQLjls3or8d6Jet9ys3zQPmbq2I4Dcz7Ymr4U+WOkdrC3Y8GXih7ORIuLQSD502LevGaackm59ffw==, tarball: file:projects/questions-assets.tgz} + id: file:projects/questions-assets.tgz + name: '@rush-temp/questions-assets' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + + file:projects/questions-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2): + resolution: {integrity: sha512-dHjmgYdEvC3nXfIJfHidVuTG66gsTIJHBhkvtrDURY5+X1YqwrDaR7S9mckeGMS6/HLGxkoRDfM+RH/ZEneSzQ==, tarball: file:projects/questions-resources.tgz} + id: file:projects/questions-resources.tgz + name: '@rush-temp/questions-resources' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) + fast-equals: 5.0.1 + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + lexorank: 1.0.5 + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + sass: 1.71.1 + svelte: 4.2.12 + svelte-check: 3.6.9(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12) + svelte-eslint-parser: 0.33.1(svelte@4.2.12) + svelte-loader: 3.2.0(svelte@4.2.12) + svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12)(typescript@5.3.3) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - coffeescript + - esbuild + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + - ts-node + dev: false + + file:projects/questions.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-RJ+opewa6lUytG241Bj7jDBH9hV/U6KYjOfcisJ28lu+4lyTtcpGpQJBOaiGoUaFaZ0o8vQNVs5tvY2kpa1L3Q==, tarball: file:projects/questions.tgz} + id: file:projects/questions.tgz + name: '@rush-temp/questions' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + file:projects/rank.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): resolution: {integrity: sha512-U9E48aTQN4WJiwY+0n7tVZf3t9GjLKoUAC2An8H7pJf9lJEWFsjGfLZSKIKyIBvdxMdFEgdsB2HospBEDpHLHQ==, tarball: file:projects/rank.tgz} id: file:projects/rank.tgz @@ -23336,6 +24045,68 @@ packages: - ts-node dev: false + file:projects/server-controlled-documents-resources.tgz(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-MBW3z4XGprpVgeKf9jWjOTjtqJiXnP9pBzmYzOlx6drh0M34puEhTa5qo4AmSepADpuvb7skN9XTwwTkhJ1mew==, tarball: file:projects/server-controlled-documents-resources.tgz} + id: file:projects/server-controlled-documents-resources.tgz + name: '@rush-temp/server-controlled-documents-resources' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + + file:projects/server-controlled-documents.tgz(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-Up1FNfPtdoY9upVTgfooAzIkN3fOx9VPeT1JuD6Bg27esViVLdOsSBt+H8sY1w7kmJJXnRCaGvoiXH1XhRTXGA==, tarball: file:projects/server-controlled-documents.tgz} + id: file:projects/server-controlled-documents.tgz + name: '@rush-temp/server-controlled-documents' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + file:projects/server-core.tgz(esbuild@0.20.1)(ts-node@10.9.2): resolution: {integrity: sha512-igJYqfFaPwOzhEA/uED0MqboUq/VGhhiRBUVGWqnfm42Xbl6SF07e7jdJGWkWzt8pEsQKgN732C9cuHm2Otx4A==, tarball: file:projects/server-core.tgz} id: file:projects/server-core.tgz @@ -24540,6 +25311,68 @@ packages: - ts-node dev: false + file:projects/server-training-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-bl3B4EVqKrZXyBO5yAO3RNMUbwGmuEXm2/xYniOmHzf91OqGcVKgciMuC44FvIQ27Ag4RFD8g+5kyk4Thzl1zw==, tarball: file:projects/server-training-resources.tgz} + id: file:projects/server-training-resources.tgz + name: '@rush-temp/server-training-resources' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + + file:projects/server-training.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-quhbh2HouGDFGDbj34zCjCCP+deVwJLaED2rw79RaYLrha8fGkRT8WJw9f3h78frHhSRXJC77gUeDVeVGbVt9A==, tarball: file:projects/server-training.tgz} + id: file:projects/server-training.tgz + name: '@rush-temp/server-training' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + file:projects/server-view-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): resolution: {integrity: sha512-KitBbseratnQwA8nU3bIOk2FiGaY5JQrPf3pHJE+Vs0JQJ6VukpfW1Su+MmWIJtXGB1DAf1UTz7mMqgBrN5dDQ==, tarball: file:projects/server-view-resources.tgz} id: file:projects/server-view-resources.tgz @@ -24790,6 +25623,38 @@ packages: - ts-node dev: false + file:projects/sign.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2): + resolution: {integrity: sha512-swUn68JPVi9z5jcHqL4N210zfaEaLet25v5nYW1ufwhIJn99yV2WHyNWI/BGGwbLurnftQcyglENUsihDAuwRg==, tarball: file:projects/sign.tgz} + id: file:projects/sign.tgz + name: '@rush-temp/sign' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - svelte + - ts-node + dev: false + file:projects/storage.tgz(esbuild@0.20.1)(ts-node@10.9.2): resolution: {integrity: sha512-HBOFnpAZCz2dP/8kdOIHPk7M3KVomZwsOJZpkSDThZC0cto4z1XXnYtBU1qfKfCy43VgPq5MXD8C3zXxVKFmEw==, tarball: file:projects/storage.tgz} id: file:projects/storage.tgz @@ -25904,6 +26769,113 @@ packages: - ts-node dev: false + file:projects/training-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-JbSmN18ObdXYerlT/UxMtLTybWENdkS9sWUeU+s9z5VG81uioFhHm8nhI31l2f8GqBoJxGjIwKBRkuoCIKPDaw==, tarball: file:projects/training-assets.tgz} + id: file:projects/training-assets.tgz + name: '@rush-temp/training-assets' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + + file:projects/training-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2): + resolution: {integrity: sha512-DCNySbsoPmIdcv06BoR6T5dqU9ClAlLAM+InUmPTGX55qMBtns9jYbss6VNwEFh2a3pXCCPBB08q1ctlufUgFQ==, tarball: file:projects/training-resources.tgz} + id: file:projects/training-resources.tgz + name: '@rush-temp/training-resources' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) + fast-equals: 5.0.1 + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + lexorank: 1.0.5 + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + sass: 1.71.1 + svelte: 4.2.12 + svelte-check: 3.6.9(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12) + svelte-eslint-parser: 0.33.1(svelte@4.2.12) + svelte-loader: 3.2.0(svelte@4.2.12) + svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12)(typescript@5.3.3) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - coffeescript + - esbuild + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + - ts-node + dev: false + + file:projects/training.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): + resolution: {integrity: sha512-3HdVf1gYwYqv+vDW/oAIVEfc7hsZqPSdUp6AI4UUpF/K5t/IKTJEGF2bYsTdz/nMRoKA6LUWMo+Q6fFv5aeoeg==, tarball: file:projects/training.tgz} + id: file:projects/training.tgz + name: '@rush-temp/training' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + dev: false + file:projects/translate.tgz(esbuild@0.20.1)(ts-node@10.9.2): resolution: {integrity: sha512-XixP3c+CH8t621FZv0lXI5ubK59Wp1X3GRoxefxkDfOImFa+qFRfwz6rJSvi/83cahJixSX7MDSs1Z+4nuGuWQ==, tarball: file:projects/translate.tgz} id: file:projects/translate.tgz diff --git a/dev/prod/package.json b/dev/prod/package.json index 2efecb62d9..74f38eab43 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -190,6 +190,24 @@ "@hcengineering/guest": "^0.6.0", "@hcengineering/guest-assets": "^0.6.0", "@hcengineering/guest-resources": "^0.6.0", - "@hcengineering/analytics": "^0.6.0" + "@hcengineering/analytics": "^0.6.0", + "@hcengineering/sign": "^0.6.0", + "@hcengineering/print": "^0.6.0", + "@hcengineering/print-assets": "^0.6.0", + "@hcengineering/print-resources": "^0.6.0", + "@hcengineering/controlled-documents": "^0.1.0", + "@hcengineering/controlled-documents-assets": "^0.1.0", + "@hcengineering/controlled-documents-resources": "^0.1.0", + "@hcengineering/questions": "^0.1.0", + "@hcengineering/questions-assets": "^0.1.0", + "@hcengineering/questions-resources": "^0.1.0", + "@hcengineering/training": "^0.1.0", + "@hcengineering/training-assets": "^0.1.0", + "@hcengineering/training-resources": "^0.1.0", + "@hcengineering/server-training": "^0.1.0", + "@hcengineering/server-training-resources": "^0.1.0", + "@hcengineering/products": "~0.1.0", + "@hcengineering/products-assets": "~0.1.0", + "@hcengineering/products-resources": "~0.1.0" } } diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 3e43b85ec2..ef8e05235f 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -48,6 +48,12 @@ import view, { viewId } from '@hcengineering/view' import workbench, { workbenchId } from '@hcengineering/workbench' import { bitrixId } from '@hcengineering/bitrix' +import print, { printId } from '@hcengineering/print' +import sign from '@hcengineering/sign' +import { productsId } from '@hcengineering/products' +import { questionsId } from '@hcengineering/questions' +import { trainingId } from '@hcengineering/training' +import { documentsId } from '@hcengineering/controlled-documents' import '@hcengineering/activity-assets' import '@hcengineering/attachment-assets' @@ -78,6 +84,11 @@ import '@hcengineering/time-assets' import '@hcengineering/tracker-assets' import '@hcengineering/view-assets' import '@hcengineering/workbench-assets' +import '@hcengineering/print-assets' +import '@hcengineering/questions-assets' +import '@hcengineering/training-assets' +import '@hcengineering/products-assets' +import '@hcengineering/controlled-documents-assets' import { coreId } from '@hcengineering/core' import presentation, { presentationId } from '@hcengineering/presentation' @@ -161,6 +172,11 @@ function configureI18n(): void { addStringsLoader(timeId, async (lang: string) => await import(`@hcengineering/time-assets/lang/${lang}.json`)) addStringsLoader(documentId, async (lang: string) => await import(`@hcengineering/document-assets/lang/${lang}.json`)) addStringsLoader(guestId, async (lang: string) => await import(`@hcengineering/guest-assets/lang/${lang}.json`)) + addStringsLoader(documentsId, async (lang: string) => await import(`@hcengineering/controlled-documents-assets/lang/${lang}.json`)) + addStringsLoader(productsId, async (lang: string) => await import(`@hcengineering/products-assets/lang/${lang}.json`)) + addStringsLoader(questionsId, async (lang: string) => await import(`@hcengineering/questions-assets/lang/${lang}.json`)) + addStringsLoader(trainingId, async (lang: string) => await import(`@hcengineering/training-assets/lang/${lang}.json`)) + addStringsLoader(printId, async (lang: string) => await import(`@hcengineering/print-assets/lang/${lang}.json`)) } export async function configurePlatform() { @@ -285,6 +301,11 @@ export async function configurePlatform() { addLocation(guestId, () => import(/* webpackChunkName: "guest" */ '@hcengineering/guest-resources')) addLocation(timeId, () => import(/* webpackChunkName: "time" */ '@hcengineering/time-resources')) addLocation(driveId, () => import(/* webpackChunkName: "drive" */ '@hcengineering/drive-resources')) + addLocation(questionsId, () => import(/* webpackChunkName: "training" */ '@hcengineering/questions-resources')) + addLocation(trainingId, () => import(/* webpackChunkName: "training" */ '@hcengineering/training-resources')) + addLocation(productsId, () => import(/* webpackChunkName: "products" */ '@hcengineering/products-resources')) + addLocation(documentsId, () => import(/* webpackChunkName: "documents" */ '@hcengineering/controlled-documents-resources')) + addLocation(printId, () => import(/* webpackChunkName: "print" */ '@hcengineering/print-resources')) setMetadata(client.metadata.FilterModel, true) setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin]) diff --git a/dev/prod/tsconfig.json b/dev/prod/tsconfig.json index 4ddf0efd12..c4b096db7b 100644 --- a/dev/prod/tsconfig.json +++ b/dev/prod/tsconfig.json @@ -14,7 +14,8 @@ "lib": [ "es2016", "dom", - "ES2021.String" + "ES2021.String", + "ESNext.Array" ] } } \ No newline at end of file diff --git a/models/all/package.json b/models/all/package.json index 0924accedc..2c2c8bfc5d 100644 --- a/models/all/package.json +++ b/models/all/package.json @@ -90,6 +90,14 @@ "@hcengineering/model-time": "^0.6.0", "@hcengineering/model-server-time": "^0.6.0", "@hcengineering/model-drive": "^0.6.0", - "@hcengineering/model-server-drive": "^0.6.0" + "@hcengineering/model-server-drive": "^0.6.0", + "@hcengineering/model-print": "^0.6.0", + "@hcengineering/model-questions": "^0.1.0", + "@hcengineering/model-training": "^0.1.0", + "@hcengineering/model-controlled-documents": "^0.1.0", + "@hcengineering/model-products": "^0.1.0", + "@hcengineering/model-server-products": "^0.1.0", + "@hcengineering/model-server-training": "^0.1.0", + "@hcengineering/model-server-controlled-documents": "^0.1.0" } } diff --git a/models/all/src/index.ts b/models/all/src/index.ts index d2be4dc440..10e1999b7b 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -79,6 +79,19 @@ import workbench, { workbenchId, createModel as workbenchModel } from '@hcengine import { openAIId, createModel as serverOpenAI } from '@hcengineering/model-server-openai' import { createModel as serverTranslate, translateId } from '@hcengineering/model-server-translate' +import { printId, createModel as printModel } from '@hcengineering/model-print' + +import { questionsId, createModel as questionsModel } from '@hcengineering/model-questions' +import trainings, { trainingId, createModel as trainingModel } from '@hcengineering/model-training' +import documents, { documentsId, createModel as documentsModel } from '@hcengineering/model-controlled-documents' +import products, { productsId, createModel as productsModel } from '@hcengineering/model-products' +import { serverProductsId, createModel as serverProductsModel } from '@hcengineering/model-server-products' +import { serverTrainingId, createModel as serverTrainingModel } from '@hcengineering/model-server-training' +import { + serverDocumentsId, + createModel as serverDocumentsModel +} from '@hcengineering/model-server-controlled-documents' + import { type Plugin } from '@hcengineering/platform' interface ConfigurablePlugin extends Omit, 'pluginId' | 'transactions'> {} @@ -298,8 +311,51 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[ } ], [timeModel, timeId], + [printModel, printId], [driveModel, driveId], [supportModel, supportId], + [ + documentsModel, + documentsId, + { + label: documents.string.ConfigLabel, + description: documents.string.ConfigDescription, + enabled: false, + beta: false, + classFilter: defaultFilter + } + ], + [ + questionsModel, + questionsId, + { + enabled: false, + beta: false, + classFilter: defaultFilter + } + ], + [ + trainingModel, + trainingId, + { + label: trainings.string.ConfigLabel, + description: trainings.string.ConfigDescription, + enabled: false, + beta: false, + classFilter: defaultFilter + } + ], + [ + productsModel, + productsId, + { + label: products.string.ConfigLabel, + description: products.string.ConfigDescription, + enabled: false, + beta: false, + classFilter: defaultFilter + } + ], [serverCoreModel, serverCoreId], [serverAttachmentModel, serverAttachmentId], @@ -327,7 +383,10 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[ [serverDocumentModel, serverDocumentId], [serverTimeModel, serverTimeId], [serverGuestModel, serverGuestId], - [serverDriveModel, serverDriveId] + [serverDriveModel, serverDriveId], + [serverProductsModel, serverProductsId], + [serverTrainingModel, serverTrainingId], + [serverDocumentsModel, serverDocumentsId] ] for (const [b, id, config] of builders) { diff --git a/models/all/src/migration.ts b/models/all/src/migration.ts index fe08797792..77af10c5d0 100644 --- a/models/all/src/migration.ts +++ b/models/all/src/migration.ts @@ -42,6 +42,10 @@ import { activityOperation } from '@hcengineering/model-activity' import { activityServerOperation } from '@hcengineering/model-server-activity' import { documentOperation } from '@hcengineering/model-document' import { textEditorOperation } from '@hcengineering/model-text-editor' +import { questionsOperation } from '@hcengineering/model-questions' +import { trainingOperation } from '@hcengineering/model-training' +import { documentsOperation } from '@hcengineering/model-controlled-documents' +import { productsOperation } from '@hcengineering/model-products' export const migrateOperations: [string, MigrateOperation][] = [ ['core', coreOperation], @@ -62,6 +66,10 @@ export const migrateOperations: [string, MigrateOperation][] = [ ['tags', tagsOperation], ['setting', settingOperation], ['tracker', trackerOperation], + ['documents', documentsOperation], + ['questions', questionsOperation], + ['training', trainingOperation], + ['products', productsOperation], ['board', boardOperation], ['hr', hrOperation], ['bitrix', bitrixOperation], diff --git a/models/controlled-documents/.eslintrc.js b/models/controlled-documents/.eslintrc.js new file mode 100644 index 0000000000..c1cf82cba0 --- /dev/null +++ b/models/controlled-documents/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/controlled-documents/.npmignore b/models/controlled-documents/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/controlled-documents/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/controlled-documents/config/rig.json b/models/controlled-documents/config/rig.json new file mode 100644 index 0000000000..2f6be36605 --- /dev/null +++ b/models/controlled-documents/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "model" +} diff --git a/models/controlled-documents/package.json b/models/controlled-documents/package.json new file mode 100644 index 0000000000..831e9e27eb --- /dev/null +++ b/models/controlled-documents/package.json @@ -0,0 +1,60 @@ +{ + "name": "@hcengineering/model-controlled-documents", + "version": "0.1.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Anticrm Platform Contributors", + "template": "@hcengineering/model-package", + "license": "EPL-2.0", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.8.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/attachment": "^0.6.9", + "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model-chunter": "^0.6.0", + "@hcengineering/model-workbench": "^0.6.1", + "@hcengineering/model-attachment": "^0.6.0", + "@hcengineering/model-view": "^0.6.0", + "@hcengineering/model-presentation": "^0.6.0", + "@hcengineering/model-tracker": "^0.6.0", + "@hcengineering/model-contact": "^0.6.1", + "@hcengineering/model-request": "^0.6.0", + "@hcengineering/model-print": "^0.6.0", + "@hcengineering/model": "^0.6.7", + "@hcengineering/setting": "^0.6.11", + "@hcengineering/core": "^0.6.28", + "@hcengineering/ui": "^0.6.11", + "@hcengineering/platform": "^0.6.9", + "@hcengineering/view": "^0.6.9", + "@hcengineering/contact": "^0.6.20", + "@hcengineering/tags": "^0.6.12", + "@hcengineering/request": "^0.6.6", + "@hcengineering/activity": "^0.6.0", + "@hcengineering/controlled-documents": "^0.1.0", + "@hcengineering/controlled-documents-resources": "^0.1.0", + "@hcengineering/training": "^0.1.0", + "@hcengineering/notification": "^0.6.16", + "@hcengineering/model-notification": "^0.6.0", + "@hcengineering/chunter": "^0.6.12" + } +} diff --git a/models/controlled-documents/src/index.ts b/models/controlled-documents/src/index.ts new file mode 100644 index 0000000000..421d3d1b8b --- /dev/null +++ b/models/controlled-documents/src/index.ts @@ -0,0 +1,1027 @@ +// +// Copyright © 2023 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import documentsPlugin, { + documentsId, + type Document, + type DocumentSpace, + DocumentState +} from '@hcengineering/controlled-documents' +import activity from '@hcengineering/activity' +import contact from '@hcengineering/contact' +import { type Builder } from '@hcengineering/model' +import chunter from '@hcengineering/model-chunter' +import core from '@hcengineering/model-core' +import request from '@hcengineering/model-request' +import tracker from '@hcengineering/model-tracker' +import { generateClassNotificationTypes } from '@hcengineering/model-notification' +import view, { classPresenter, createAction } from '@hcengineering/model-view' +import presentation from '@hcengineering/model-presentation' +import workbench from '@hcengineering/model-workbench' +import notification from '@hcengineering/notification' +import setting from '@hcengineering/setting' +import tags from '@hcengineering/tags' +import print from '@hcengineering/model-print' + +import documents from './plugin' +import { definePermissions } from './permissions' +import { + TDocumentSpace, + TDocumentSpaceType, + TDocumentSpaceTypeDescriptor, + TExternalSpace, + TOrgSpace, + TDocumentMeta, + TProjectDocument, + TProjectMeta, + TProject, + TDocument, + TDocumentSnapshot, + TControlledDocumentSnapshot, + THierarchyDocument, + TDocumentTemplate, + TDocumentTraining, + TDocumentCategory, + TControlledDocument, + TChangeControl, + TDocumentSection, + TCollaborativeDocumentSection, + TAttachmentsDocumentSection, + TSequence, + TDocumentRequest, + TDocumentReviewRequest, + TDocumentApprovalRequest, + TDocumentSectionEditor, + TDocumentSectionPresenter, + TDocumentSectionCreator, + TDocumentTemplateSection, + TTypeDocumentState, + TTypeControlledDocumentState, + TDocumentComment +} from './types' +import { defineSpaceType } from './spaceType' + +export { documentsId } from '@hcengineering/controlled-documents/src/index' +export * from './types' + +export function createModel (builder: Builder): void { + builder.createModel( + TDocumentSpace, + TDocumentSpaceType, + TDocumentSpaceTypeDescriptor, + TExternalSpace, + TOrgSpace, + TProject, + TDocumentMeta, + TProjectDocument, + TProjectMeta, + TDocument, + TDocumentSnapshot, + TControlledDocumentSnapshot, + THierarchyDocument, + TDocumentTemplate, + TDocumentTraining, + TDocumentCategory, + TControlledDocument, + TChangeControl, + + TDocumentSection, + TCollaborativeDocumentSection, + TAttachmentsDocumentSection, + + TSequence, + + TDocumentRequest, + TDocumentReviewRequest, + TDocumentApprovalRequest, + TDocumentSectionEditor, + TDocumentSectionPresenter, + TDocumentSectionCreator, + TDocumentTemplateSection, + + TTypeDocumentState, + TTypeControlledDocumentState, + + TDocumentComment + ) + + builder.mixin(documents.class.ControlledDocument, core.class.Class, view.mixin.ObjectTitle, { + titleProvider: documents.function.ControlledDocumentTitleProvider + }) + + builder.mixin(documents.class.DocumentApprovalRequest, core.class.Class, view.mixin.ObjectPresenter, { + presenter: documents.component.DocumentApprovalRequestPresenter + }) + + builder.mixin(documents.class.DocumentReviewRequest, core.class.Class, view.mixin.ObjectPresenter, { + presenter: documents.component.DocumentReviewRequestPresenter + }) + + builder.createDoc(activity.class.DocUpdateMessageViewlet, core.space.Model, { + objectClass: documents.class.DocumentApprovalRequest, + action: 'create', + icon: documents.icon.Document + }) + + builder.createDoc(activity.class.DocUpdateMessageViewlet, core.space.Model, { + objectClass: documents.class.DocumentReviewRequest, + action: 'create', + icon: documents.icon.Document + }) + + builder.createDoc( + workbench.class.Application, + core.space.Model, + { + label: documents.string.DocumentApplication, + icon: documents.icon.DocumentApplication, + locationResolver: documents.resolver.Location, + alias: documentsId, + hidden: false, + navigatorModel: { + specials: [ + { + id: 'my-documents', + position: 'top', + label: documents.string.MyDocuments, + icon: documents.icon.Document, + component: documents.component.MyDocuments, + componentProps: { + query: { + [documents.mixin.DocumentTemplate]: { $exists: false } + }, + config: [ + ['effective', documents.string.Effective, {}], + ['inProgress', documents.string.InProgress, {}], + ['archived', documents.string.Archived, {}], + ['all', documents.string.All, {}] + ] + } + }, + { + id: 'library', + position: 'top', + label: documents.string.Library, + icon: documents.icon.Library, + component: documents.component.DocumentsContainer, + componentProps: { + query: { + [documents.mixin.DocumentTemplate]: { $exists: false } + }, + title: documents.string.Documents, + config: [ + ['effective', documents.string.Effective, {}], + ['inProgress', documents.string.InProgress, {}], + ['archived', documents.string.Archived, {}], + ['all', documents.string.All, {}] + ] + } + }, + { + id: 'templates', + position: 'top', + label: documents.string.DocumentTemplates, + icon: documents.icon.Library, + component: documents.component.DocumentTemplates + }, + { + id: 'categories', + position: 'top', + label: documents.string.Categories, + icon: documents.icon.Library, + component: documents.component.Categories, + componentProps: { + space: documents.space.QualityDocuments + } + } + ], + spaces: [ + { + id: 'orgspaces', + label: documents.string.Organisation, + spaceClass: documents.class.OrgSpace, + addSpaceLabel: documents.string.CreateOrgSpace, + createComponent: documents.component.CreateDocumentsSpace, + specials: [ + { + id: 'projects', + label: documents.string.Projects, + icon: documents.icon.Document, + component: documents.component.Projects + } + ] + }, + { + id: 'projectspaces', + label: documents.string.Projects, + spaceClass: documents.class.ExternalSpace, + specials: [ + { + id: 'projects', + label: documents.string.Projects, + icon: documents.icon.Document, + component: documents.component.Projects + } + ] + } + ] + }, + navHeaderComponent: documents.component.NewDocumentHeader + }, + documents.app.Documents + ) + + // Workflow + builder.createDoc( + activity.class.TxViewlet, + core.space.Model, + { + objectClass: documents.class.DocumentRequest, + icon: request.icon.Requests, + txClass: core.class.TxCreateDoc, + component: request.activity.TxCreateRequest, + label: request.string.CreatedRequest, + labelComponent: request.activity.RequestLabel, + display: 'emphasized' + }, + request.ids.TxRequestCreate + ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: documents.class.Document, + descriptor: view.viewlet.Table, + configOptions: { + sortable: true, + strict: true + }, + config: [ + { + key: '', + label: documents.string.ID, + presenter: documents.component.DocumentPresenter, + props: { isGray: true } + }, + { key: '', label: documents.string.Title, presenter: documents.component.TitlePresenter }, + { + key: '', + label: documents.string.Status, + presenter: documents.component.StatePresenter, + sortingKey: 'state' + }, + { key: '', label: documents.string.Version, presenter: documents.component.DocumentVersionPresenter }, + { key: 'space', sortingKey: 'space' }, + { key: '$lookup.category.title', label: documents.string.Category, sortingKey: '$lookup.category.title' }, + { key: '$lookup.template', label: documents.string.Template, presenter: documents.component.DocumentPresenter }, + { + key: '$lookup.template', + label: documents.string.TemplateVersion, + presenter: documents.component.DocumentVersionPresenter + }, + { + key: '$lookup.owner', + label: documents.string.Owner, + presenter: documents.component.OwnerPresenter, + props: { shouldShowLabel: true, isEditable: false }, + sortingKey: '$lookup.owner.name' + }, + { + key: '', + presenter: tags.component.TagsPresenter, + label: documents.string.Labels, + sortingKey: 'labels', + props: { + _class: documents.class.Document, + key: 'labels' + } + }, + 'modifiedOn' + ], + options: { + lookup: { + owner: contact.mixin.Employee, + category: documents.class.DocumentCategory, + template: documents.mixin.DocumentTemplate + } + } + }, + documents.viewlet.TableDocument + ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: documents.mixin.DocumentTemplate, + descriptor: view.viewlet.Table, + config: [ + { + key: '', + label: documents.string.ID, + presenter: documents.component.DocumentPresenter, + props: { isGray: true } + }, + 'title', + { + key: '', + label: documents.string.Status, + presenter: documents.component.StatePresenter + }, + { key: '', label: documents.string.Version, presenter: documents.component.DocumentVersionPresenter }, + 'docPrefix', + { key: '$lookup.category.title', label: documents.string.Category, sortingKey: '$lookup.category.title' }, + { + key: '', + presenter: tags.component.TagsPresenter, + label: documents.string.Labels, + sortingKey: 'labels', + props: { + _class: documents.class.Document, + key: 'labels', + icon: documents.icon.Document + } + }, + { + key: '$lookup.owner', + label: documents.string.Owner, + presenter: documents.component.OwnerPresenter, + props: { shouldShowLabel: true, isEditable: false }, + sortingKey: '$lookup.owner.name' + } + ], + baseQuery: { + hidden: { $ne: true } + }, + configOptions: { + hiddenKeys: ['attachedTo'] + }, + options: { + lookup: { + owner: contact.mixin.Employee, + category: documents.class.DocumentCategory + } + } + }, + documents.viewlet.TableDocumentTemplate + ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: documents.class.DocumentCategory, + descriptor: view.viewlet.Table, + config: ['', 'title', 'attachments', 'modifiedOn'], + configOptions: { + hiddenKeys: ['description'] + }, + options: {} + }, + documents.viewlet.TableDocumentDomain + ) + + builder.mixin(documents.class.Document, core.class.Class, view.mixin.ObjectFactory, { + component: documents.component.CreateDocument + }) + + builder.mixin(documents.class.Document, core.class.Class, view.mixin.ObjectEditor, { + editor: documents.component.EditDoc + }) + + builder.mixin(documents.class.Document, core.class.Class, view.mixin.ObjectPanel, { + component: documents.component.EditDoc + }) + + builder.mixin(documents.class.ProjectDocument, core.class.Class, view.mixin.ObjectEditor, { + editor: documents.component.EditProjectDoc + }) + + builder.mixin(documents.class.ProjectDocument, core.class.Class, view.mixin.ObjectPanel, { + component: documents.component.EditProjectDoc + }) + + builder.mixin(documents.class.ControlledDocument, core.class.Class, view.mixin.IgnoreActions, { + actions: [tracker.action.NewRelatedIssue] + }) + + builder.mixin(documents.class.DocumentMeta, core.class.Class, view.mixin.ObjectPresenter, { + presenter: documents.component.DocumentMetaPresenter + }) + + builder.mixin(documents.class.DocumentMeta, core.class.Class, view.mixin.LinkProvider, { + encode: documents.function.GetDocumentMetaLinkFragment + }) + + builder.mixin(documents.class.CollaborativeDocumentSection, core.class.Class, view.mixin.ObjectPresenter, { + presenter: documents.component.CollaborativeSectionPresenter + }) + + builder.mixin(documents.class.CollaborativeDocumentSection, core.class.Class, documents.mixin.DocumentSectionEditor, { + editor: documents.component.CollaborativeSectionEditor + }) + + builder.mixin( + documents.class.CollaborativeDocumentSection, + core.class.Class, + documents.mixin.DocumentSectionCreator, + { + creator: documents.function.CollaborativeSectionCreator + } + ) + + builder.mixin(documents.class.AttachmentsDocumentSection, core.class.Class, view.mixin.ObjectPresenter, { + presenter: documents.component.AttachmentsSectionPresenter + }) + + builder.mixin(documents.class.AttachmentsDocumentSection, core.class.Class, documents.mixin.DocumentSectionEditor, { + editor: documents.component.AttachmentsSectionEditor + }) + + builder.mixin(documents.class.AttachmentsDocumentSection, core.class.Class, documents.mixin.DocumentSectionCreator, { + creator: documents.function.AttachmentsSectionCreator + }) + + builder.mixin(documents.class.DocumentSection, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Open, print.action.Print, tracker.action.NewRelatedIssue] + }) + + builder.mixin(documents.class.Document, core.class.Class, view.mixin.ObjectPresenter, { + presenter: documents.component.DocumentPresenter + }) + + builder.mixin(documents.class.Document, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete] + }) + + createAction( + builder, + { + action: view.actionImpl.ShowPopup, + actionProps: { + component: documentsPlugin.component.ChangeOwnerPopup, + fillProps: { + _object: 'object' + } + }, + visibilityTester: documentsPlugin.function.CanChangeDocumentOwner, + label: documentsPlugin.string.ChangeOwner, + icon: contact.icon.Person, + category: view.category.General, + input: 'none', + target: documents.class.Document, + context: { mode: ['context'], group: 'edit' } + }, + documentsPlugin.action.ChangeDocumentOwner + ) + + createAction( + builder, + { + action: documents.actionImpl.CreateChildDocument, + label: documentsPlugin.string.CreateChildDocument, + icon: documents.icon.NewDocument, + category: view.category.General, + input: 'focus', // should only work for one document, not bulk + query: { + space: { $ne: documents.space.UnsortedTemplates } + }, + target: documents.class.ProjectDocument, + visibilityTester: documents.function.CanCreateChildDocument, + context: { mode: ['context'], group: 'create' } + }, + documentsPlugin.action.CreateChildDocument + ) + + createAction( + builder, + { + action: documents.actionImpl.CreateChildTemplate, + label: documentsPlugin.string.CreateChildTemplate, + icon: documents.icon.NewDocument, + category: view.category.General, + input: 'focus', // should only work for one document, not bulk + target: documents.class.ProjectDocument, + visibilityTester: documents.function.CanCreateChildTemplate, + context: { mode: ['context'], group: 'create' } + }, + documentsPlugin.action.CreateChildTemplate + ) + + createAction( + builder, + { + action: documents.actionImpl.CreateDocument, + label: documentsPlugin.string.CreateDocument, + icon: documents.icon.NewDocument, + category: view.category.General, + input: 'none', + target: documents.class.DocumentSpace, + visibilityTester: documents.function.CanCreateDocument, + context: { mode: ['context'], group: 'create' } + }, + documentsPlugin.action.CreateDocument + ) + + createAction( + builder, + { + action: documents.actionImpl.CreateTemplate, + label: documentsPlugin.string.CreateTemplate, + icon: documents.icon.NewDocument, + category: view.category.General, + input: 'none', + target: documents.class.OrgSpace, + visibilityTester: documents.function.CanCreateTemplate, + context: { mode: ['context'], group: 'create' } + }, + documentsPlugin.action.CreateTemplate + ) + + createAction( + builder, + { + action: view.actionImpl.ShowPopup, + actionProps: { + component: documentsPlugin.component.DeleteCategoryPopup, + fillProps: { + _object: 'object' + } + }, + visibilityTester: documentsPlugin.function.CanDeleteDocumentCategory, + label: view.string.Delete, + icon: view.icon.Delete, + category: view.category.General, + input: 'none', + target: documents.class.DocumentCategory, + context: { mode: ['context', 'browser'], group: 'remove' } + }, + documentsPlugin.action.DeleteDocumentCategory + ) + + builder.mixin(documents.class.DocumentCategory, core.class.Class, activity.mixin.ActivityDoc, {}) + + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { + ofClass: documents.class.DocumentCategory, + components: { input: chunter.component.ChatMessageInput } + }) + + builder.mixin(documents.class.DocumentCategory, core.class.Class, view.mixin.ObjectPresenter, { + presenter: documents.component.CategoryPresenter + }) + + builder.mixin(documents.class.DocumentCategory, core.class.Class, view.mixin.AttributePresenter, { + presenter: documents.component.CategoryPresenter + }) + + builder.mixin(documents.class.DocumentCategory, core.class.Class, view.mixin.AttributeEditor, { + inlineEditor: documents.component.CategoryPresenter + }) + + builder.mixin(documents.class.DocumentCategory, core.class.Class, view.mixin.ClassFilters, { + filters: ['code', 'title', 'modifiedOn'] + }) + + builder.mixin(documents.class.DocumentCategory, core.class.Class, view.mixin.ObjectEditor, { + editor: documents.component.EditDocumentCategory + }) + + builder.mixin(documents.class.DocumentCategory, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete] + }) + + builder.mixin(documents.class.TypeDocumentState, core.class.Class, view.mixin.SortFuncs, { + func: documents.function.DocumentStateSort + }) + + builder.mixin(documents.class.TypeDocumentState, core.class.Class, view.mixin.AllValuesFunc, { + func: documents.function.GetAllDocumentStates + }) + + builder.mixin(documents.class.Document, core.class.Class, core.mixin.FullTextSearchContext, { + fullTextSummary: true, + childProcessingAllowed: true + }) + + builder.mixin(documents.class.Document, core.class.Class, view.mixin.ClassFilters, { + filters: [ + 'prefix', + 'title', + 'state', + 'space', + 'template', + 'owner', + 'category', + 'modifiedOn', + 'labels', + 'major', + 'minor', + 'author' + ], + getVisibleFilters: documents.function.GetVisibleFilters + }) + + builder.mixin(documents.class.TypeDocumentState, core.class.Class, view.mixin.AttributeFilterPresenter, { + presenter: documents.component.StateFilterValuePresenter + }) + + builder.mixin(documents.class.TypeDocumentState, core.class.Class, view.mixin.AttributeFilter, { + component: view.component.ValueFilter + }) + + builder.mixin(documents.class.TypeControlledDocumentState, core.class.Class, view.mixin.AttributeFilterPresenter, { + presenter: documents.component.ControlledStateFilterValuePresenter + }) + + builder.mixin(documents.class.TypeControlledDocumentState, core.class.Class, view.mixin.AttributeFilter, { + component: view.component.ValueFilter + }) + + builder.mixin(documents.mixin.DocumentTemplate, core.class.Class, view.mixin.ClassFilters, { + filters: ['prefix', 'title', 'modifiedOn', 'category'] + }) + + builder.mixin(documents.class.Document, core.class.Class, setting.mixin.Editable, { + value: true + }) + + classPresenter(builder, documents.class.TypeDocumentState, documents.component.StatePresenter) + classPresenter(builder, documents.class.TypeControlledDocumentState, documents.component.StatePresenter) + + // DocumentReviewRequest + builder.mixin(documents.class.DocumentReviewRequest, core.class.Class, view.mixin.ObjectEditor, { + editor: documents.component.DocumentReviewRequest + }) + + builder.mixin(documents.class.DocumentReviewRequest, core.class.Class, request.mixin.RequestPresenter, { + presenter: documents.component.DocumentReviewRequestPresenter + }) + + builder.mixin(documents.class.DocumentReviewRequest, core.class.Class, view.mixin.ObjectPanel, { + component: documents.component.DocumentReviewRequest + }) + + builder.mixin( + documents.class.DocumentReviewRequest, + core.class.Class, + notification.mixin.NotificationObjectPresenter, + { + presenter: documents.component.DocumentReviewRequestPresenter + } + ) + + // DocumentApprovalRequest + builder.mixin(documents.class.DocumentApprovalRequest, core.class.Class, view.mixin.ObjectEditor, { + editor: documents.component.DocumentApprovalRequest + }) + + builder.mixin(documents.class.DocumentApprovalRequest, core.class.Class, request.mixin.RequestPresenter, { + presenter: documents.component.DocumentApprovalRequestPresenter + }) + + builder.mixin(documents.class.DocumentApprovalRequest, core.class.Class, view.mixin.ObjectPanel, { + component: documents.component.DocumentApprovalRequest + }) + + builder.mixin( + documents.class.DocumentApprovalRequest, + core.class.Class, + notification.mixin.NotificationObjectPresenter, + { + presenter: documents.component.DocumentApprovalRequestPresenter + } + ) + + createAction(builder, { + action: documents.actionImpl.AddCollaborativeSectionAbove, + label: documents.string.AddSectionAbove, + icon: documents.icon.ArrowUp, + input: 'any', + category: view.category.General, + target: documents.class.CollaborativeDocumentSection, + context: { + mode: ['context', 'browser'], + group: 'create' + } + }) + + createAction(builder, { + action: documents.actionImpl.AddCollaborativeSectionBelow, + label: documents.string.AddSectionBelow, + icon: documents.icon.ArrowDown, + input: 'any', + category: view.category.General, + target: documents.class.CollaborativeDocumentSection, + context: { + mode: ['context', 'browser'], + group: 'create' + } + }) + + createAction(builder, { + action: documents.actionImpl.Duplicate, + label: documents.string.Duplicate, + icon: documents.icon.Duplicate, + input: 'any', + category: view.category.General, + target: documents.class.DocumentSection, + context: { + mode: ['context', 'browser'], + group: 'edit' + } + }) + + createAction(builder, { + action: documents.actionImpl.DeleteCollaborativeSection, + label: view.string.Delete, + icon: view.icon.Delete, + keyBinding: ['Meta + Backspace'], + category: view.category.General, + input: 'any', + target: documents.class.DocumentSection, + context: { mode: ['context', 'browser'], group: 'edit' }, + override: [view.action.Delete] + }) + + createAction(builder, { + action: documents.actionImpl.EditDescription, + label: documents.string.EditDescription, + icon: documents.icon.EditDescription, + input: 'any', + category: view.category.General, + target: documents.mixin.DocumentTemplateSection, + context: { + mode: ['context', 'browser'], + group: 'tools' + } + }) + + createAction(builder, { + action: documents.actionImpl.EditGuidance, + label: documents.string.EditGuidance, + icon: documents.icon.EditGuidance, + input: 'any', + category: view.category.General, + target: documents.mixin.DocumentTemplateSection, + context: { + mode: ['context', 'browser'], + group: 'tools' + } + }) + + builder.mixin(documents.class.CollaborativeDocumentSection, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete] + }) + + builder.mixin(documents.class.DocumentSpace, core.class.Class, view.mixin.IgnoreActions, { + actions: [tracker.action.EditRelatedTargets] + }) + + builder.mixin(documents.class.DocumentSpace, core.class.Class, view.mixin.SpacePresenter, { + presenter: documents.component.DocumentSpacePresenter + }) + + builder.mixin(documents.class.OrgSpace, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Archive] + }) + + builder.mixin(documents.class.Project, core.class.Class, view.mixin.ObjectPresenter, { + presenter: documents.component.ProjectPresenter + }) + + builder.mixin(documents.class.Project, core.class.Class, view.mixin.AttributePresenter, { + presenter: documents.component.ProjectRefPresenter + }) + + builder.mixin(documents.class.Project, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete, view.action.Open, view.action.OpenInNewTab, tracker.action.NewRelatedIssue] + }) + + builder.mixin(documents.class.ProjectDocument, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete, tracker.action.NewRelatedIssue] + }) + + builder.mixin(documents.class.Document, core.class.Class, view.mixin.ObjectIdentifier, { + provider: documents.function.DocumentIdentifierProvider + }) + + createAction( + builder, + { + action: documents.actionImpl.DeleteDocument, + label: view.string.Delete, + icon: view.icon.Delete, + input: 'any', + category: view.category.General, + target: documents.class.Document, + visibilityTester: documents.function.IsLatestDraftDoc, + query: { + state: DocumentState.Draft + }, + context: { + mode: ['context', 'browser'], + group: 'remove' + } + }, + documents.action.DeleteDocument + ) + + createAction( + builder, + { + action: documents.actionImpl.EditDocSpace, + label: documents.string.EditDocumentSpace, + icon: view.icon.Edit, + input: 'focus', + category: documents.category.Document, + target: documents.class.DocumentSpace, + visibilityTester: view.function.CanEditSpace, + query: {}, + context: { + mode: ['context', 'browser'], + group: 'edit' + } + }, + documents.action.EditDocSpace + ) + + createAction( + builder, + { + action: print.actionImpl.Print, + actionProps: { + signed: true + }, + label: print.string.PrintToPDF, + icon: print.icon.Print, + category: view.category.General, + input: 'focus', // NOTE: should only work for one doc for now, not bulk + target: documents.class.Document, + context: { mode: ['context', 'browser'], group: 'tools' }, + visibilityTester: print.function.CanPrint, + override: [print.action.Print] + }, + documents.action.Print + ) + + defineSpaceType(builder) + definePermissions(builder) + defineNotifications(builder) + defineSearch(builder) +} + +export function defineNotifications (builder: Builder): void { + builder.mixin(documents.class.ControlledDocument, core.class.Class, activity.mixin.ActivityDoc, {}) + + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { + ofClass: documents.class.DocumentComment, + components: { input: chunter.component.ChatMessageInput } + }) + + builder.mixin(documents.class.ControlledDocument, core.class.Class, notification.mixin.ClassCollaborators, { + fields: ['author', 'owner', 'reviewers', 'approvers', 'coAuthors'] + }) + + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: documents.string.DocumentApplication, + icon: documents.icon.DocumentApplication + }, + documents.notification.DocumentsNotificationGroup + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + allowedForAuthor: false, + label: documents.string.Document, + group: documents.notification.DocumentsNotificationGroup, + field: 'content', + txClasses: [core.class.TxUpdateDoc], + objectClass: documents.class.ControlledDocument, + providers: { + [notification.providers.PlatformNotification]: true, + [notification.providers.BrowserNotification]: false + } + }, + documents.notification.ContentNotification + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + allowedForAuthor: false, + label: documents.string.Status, + group: documents.notification.DocumentsNotificationGroup, + field: 'state', + txClasses: [core.class.TxUpdateDoc], + objectClass: documents.class.ControlledDocument, + providers: { + [notification.providers.PlatformNotification]: true, + [notification.providers.BrowserNotification]: false, + [notification.providers.EmailNotification]: false + } + }, + documents.notification.StateNotification + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + allowedForAuthor: false, + label: documents.string.CoAuthors, + group: documents.notification.DocumentsNotificationGroup, + field: 'coAuthors', + txClasses: [core.class.TxUpdateDoc], + objectClass: documents.class.ControlledDocument, + providers: { + [notification.providers.PlatformNotification]: true, + [notification.providers.BrowserNotification]: false, + [notification.providers.EmailNotification]: false + } + }, + documents.notification.CoAuthorsNotification + ) + + generateClassNotificationTypes( + builder, + documents.class.ControlledDocument, + documents.notification.DocumentsNotificationGroup, + [ + 'state', + 'code', + 'prefix', + 'seqNumber', + 'major', + 'minor', + 'category', + 'author', + 'content', + 'labels', + 'sections', + 'abstract', + 'snapshots', + 'requests', + 'reviewInterval', + 'controlledState', + 'effectiveDate', + 'plannedEffectiveDate', + 'changeControl', + 'coAuthors' + ], + ['owner'] + ) +} + +export function defineSearch (builder: Builder): void { + builder.mixin(documents.class.Document, core.class.Class, core.mixin.FullTextSearchContext, { + parentPropagate: true + }) + + builder.mixin(documents.class.DocumentMeta, core.class.Class, core.mixin.FullTextSearchContext, { + fullTextSummary: true, + childProcessingAllowed: true, + propagate: [] + }) + + builder.createDoc( + presentation.class.ObjectSearchCategory, + core.space.Model, + { + title: documents.string.Documents, + icon: documents.icon.Document, + label: documents.string.SearchDocument, + query: documents.completion.DocumentMetaQuery, + context: ['search', 'mention', 'spotlight'], + classToSearch: documents.class.DocumentMeta + }, + documents.completion.DocumentMetaCategory + ) +} + +export { documentsOperation } from './migration' +export { documents as default } diff --git a/models/controlled-documents/src/migration.ts b/models/controlled-documents/src/migration.ts new file mode 100644 index 0000000000..6d5433c146 --- /dev/null +++ b/models/controlled-documents/src/migration.ts @@ -0,0 +1,718 @@ +// +// Copyright @ 2022-2023 Hardcore Engineering Inc. +// + +import { + type Data, + type Ref, + TxOperations, + type Class, + type Space, + generateId, + DOMAIN_TX, + AccountRole, + DOMAIN_DOC_INDEX_STATE, + getCollaborativeDoc +} from '@hcengineering/core' +import { + createDefaultSpace, + createOrUpdate, + tryMigrate, + type MigrateOperation, + type MigrationClient, + type MigrationUpgradeClient +} from '@hcengineering/model' +import core, { DOMAIN_SPACE } from '@hcengineering/model-core' +import contact from '@hcengineering/model-contact' +import tags from '@hcengineering/tags' +import { + type ChangeControl, + type DocumentCategory, + type Document, + type DocumentMeta, + type DocumentSpace, + type HierarchyDocument, + type ProjectDocument, + type ProjectMeta, + TEMPLATE_PREFIX, + getDocumentId, + DocumentState, + documentsId, + createDocumentTemplate, + type ControlledDocument, + createChangeControl +} from '@hcengineering/controlled-documents' + +import documents, { DOMAIN_DOCUMENTS } from './index' + +async function migrateDocumentCategories (client: MigrationClient): Promise { + const oldSpace = 'documents:space:Documents' as Ref + + await client.update( + DOMAIN_DOCUMENTS, + { + _class: documents.class.DocumentCategory, + space: oldSpace + }, + { + $set: { + space: documents.space.QualityDocuments + } + } + ) +} + +async function migrateUnusedDocumentSpaces (client: MigrationClient): Promise { + const spaces: Ref[] = [ + 'events:space:EventsDocuments' as Ref, + 'clients:space:ClientsDocuments' as Ref, + 'supplier:space:SuppliersDocuments' as Ref, + 'trainings:space:TrainingsDocuments' as Ref + ] + + await client.update( + DOMAIN_SPACE, + { + _id: { $in: spaces }, + _class: documents.class.DocumentSpace, + archived: false + }, + { + $set: { + archived: true + } + } + ) +} + +async function migrateProjectMeta (client: MigrationClient): Promise { + type ExDocumentMeta = DocumentMeta & { path: Ref[] } + + const metaToMigrate = await client.find(DOMAIN_DOCUMENTS, { + _class: documents.class.DocumentMeta, + path: { $exists: true } + }) + + for (const meta of metaToMigrate) { + const projectMetaId: Ref = generateId() + + const docs = await client.find(DOMAIN_DOCUMENTS, { + attachedTo: meta._id + }) + + for (const doc of docs) { + await client.create(DOMAIN_DOCUMENTS, { + _id: generateId(), + _class: documents.class.ProjectDocument, + space: doc.space, + modifiedBy: core.account.System, + modifiedOn: Date.now(), + attachedTo: projectMetaId, + attachedToClass: documents.class.ProjectMeta, + collection: 'documents', + project: documents.ids.NoProject, + initial: documents.ids.NoProject, + document: doc._id + }) + } + + await client.create(DOMAIN_DOCUMENTS, { + _id: projectMetaId, + _class: documents.class.ProjectMeta, + space: meta.space, + modifiedBy: core.account.System, + modifiedOn: Date.now(), + project: documents.ids.NoProject, + meta: meta._id, + path: meta.path, + parent: meta.path[0] ?? documents.ids.NoParent, + documents: docs.length + }) + + await client.update( + DOMAIN_DOCUMENTS, + { _id: meta._id }, + { + $unset: { + path: undefined + } + } + ) + } +} + +async function migrateDocumentSpaces (client: MigrationClient): Promise { + const affectedClasses: Ref>[] = [ + documents.class.DocumentSpace, + documents.class.OrgSpace, + documents.class.ExternalSpace + ] + + await client.update( + DOMAIN_SPACE, + { + _class: { $in: affectedClasses }, + type: { $exists: false } + }, + { + $set: { + type: documents.spaceType.DocumentSpaceType + } + } + ) + + await client.update( + DOMAIN_SPACE, + { + _class: { $in: affectedClasses }, + type: 'documents:spaceType:DefaultDocLibraryType' + }, + { + $set: { + type: documents.spaceType.DocumentSpaceType + }, + $rename: { + 'documents:spaceType:DefaultDocLibraryType:type:mixin': 'documents:spaceType:DocumentSpaceType:type:mixin' + } + } + ) +} + +async function migrateLibraryDocumentSpace (client: MigrationClient): Promise { + await client.update( + DOMAIN_SPACE, + { + _class: 'documents:class:LibraryDocumentSpace' as Ref> + }, + { + $set: { + _class: documents.class.DocumentSpace + } + } + ) +} + +async function createTemplatesSpace (tx: TxOperations): Promise { + const existingSpace = await tx.findOne(documents.class.DocumentSpace, { + _id: documents.space.UnsortedTemplates + }) + + if (existingSpace === undefined) { + await tx.createDoc( + documents.class.DocumentSpace, + core.space.Space, + { + name: 'Unsorted templates', + description: 'Unsorted templates', + private: false, + archived: false, + autoJoin: true, + members: [], + type: documents.spaceType.DocumentSpaceType + }, + documents.space.UnsortedTemplates + ) + } +} + +async function createQualityDocumentsSpace (tx: TxOperations): Promise { + const existingSpace = await tx.findOne(documents.class.OrgSpace, { + _id: documents.space.QualityDocuments + }) + + if (existingSpace === undefined) { + await tx.createDoc( + documents.class.OrgSpace, + core.space.Space, + { + name: 'Quality documents', + description: "Space for organization's quality documents", + private: true, + archived: false, + members: [], + autoJoin: true, + owners: [], + type: documents.spaceType.DocumentSpaceType + }, + documents.space.QualityDocuments + ) + } +} + +async function migrateSpace (client: MigrationClient): Promise { + await client.update( + DOMAIN_SPACE, + { + _id: documents.space.Documents, + _class: documents.class.DocumentSpace + }, + { + $set: { + _class: core.class.Space + } + } + ) +} + +async function migrateEffectiveDates (client: MigrationClient): Promise { + await client.update( + DOMAIN_DOCUMENTS, + { + effectiveDate: 0 + }, + { + $set: { + plannedEffectiveDate: 0 + }, + $unset: { + effectiveDate: undefined + } + } + ) + + const docsToRelease = await client.find(DOMAIN_DOCUMENTS, { effectiveDate: { $gt: Date.now() } }) + + for (const doc of docsToRelease) { + const plannedEffectiveDate = (doc as any).effectiveDate + await client.update( + DOMAIN_DOCUMENTS, + { _id: doc._id }, + { + $set: { + plannedEffectiveDate + }, + $unset: { + effectiveDate: undefined + } + } + ) + } +} + +async function migrateChangeControls (client: MigrationClient): Promise { + await client.update( + DOMAIN_DOCUMENTS, + { _class: documents.class.ChangeControl, impactedDocuments: { $exists: false } }, + { + $set: { + impactedDocuments: [] + } + } + ) +} + +async function migrateTemplatePrefixes (client: MigrationClient): Promise { + const templatesToMigrate = await client.find(DOMAIN_DOCUMENTS, { + [documents.mixin.DocumentTemplate]: { $exists: true }, + prefix: { $ne: TEMPLATE_PREFIX } + }) + + for (const tmp of templatesToMigrate) { + await client.update( + DOMAIN_DOCUMENTS, + { + _id: tmp._id + }, + { + $set: { + prefix: TEMPLATE_PREFIX, + [`${documents.mixin.DocumentTemplate}.docPrefix`]: tmp.prefix + } + } + ) + } +} + +async function migrateDocumentCodes (client: MigrationClient): Promise { + const classes = client.hierarchy.getDescendants(documents.class.Document) + const docsToMigrate = await client.find(DOMAIN_DOCUMENTS, { + _class: { $in: classes }, + code: { $exists: false } + }) + + for (const doc of docsToMigrate) { + await client.update( + DOMAIN_DOCUMENTS, + { + _id: doc._id + }, + { + $set: { + code: getDocumentId(doc) + } + } + ) + } +} + +async function migrateCollaborativeDocument (client: MigrationClient): Promise { + const mixin = 'documents:mixin:CollaborativeDocument' + + const docsToMigrate = await client.find(DOMAIN_DOCUMENTS, { + [`${mixin}.document`]: { $exists: true } + }) + + for (const doc of docsToMigrate) { + const content = (doc as any)[mixin].document + const snapshots = (doc as any)[mixin].snapshots ?? [] + + // move mixin document -> content field + await client.update( + DOMAIN_DOCUMENTS, + { _id: doc._id }, + { + $set: { + content, + snapshots: snapshots.length + }, + $unset: { + [mixin]: undefined + } + } + ) + + // make snapshots attached docs + await client.update( + DOMAIN_DOCUMENTS, + { _id: { $in: snapshots } }, + { + $set: { + attachedTo: doc._id, + attachedToClass: doc._class, + collection: 'snapshots' + } + } + ) + } +} + +async function fixChangeControlsForDocs (tx: TxOperations): Promise { + const defaultCCSpec: Data = { + description: '', + reason: '', + impact: '', + impactedDocuments: [] + } + const controlledDocuments = await tx.findAll( + documents.class.ControlledDocument, + {}, + { lookup: { changeControl: documents.class.ChangeControl } } + ) + + for (const cdoc of controlledDocuments) { + const existingCC = await tx.findOne(documents.class.ChangeControl, { _id: cdoc.changeControl }) + + if (existingCC !== undefined) { + continue + } + + const newCc = await tx.createDoc( + documents.class.ChangeControl, + cdoc.space, + defaultCCSpec, + cdoc.changeControl?.length > 0 ? cdoc.changeControl : undefined + ) + + if (cdoc.changeControl === undefined) { + await tx.update(cdoc, { changeControl: newCc }) + } + } +} + +async function createProductChangeControlTemplate (tx: TxOperations): Promise { + const ccCategory = 'documents:category:DOC - CC' as Ref + const productChangeControlTemplate = await tx.findOne(documents.mixin.DocumentTemplate, { + category: ccCategory + }) + + if (productChangeControlTemplate === undefined) { + const ccRecordId = generateId() + const ccRecord: Data = { + description: '', + reason: 'New template creation', + impact: '', + impactedDocuments: [] + } + + const seq = await tx.findOne(documents.class.Sequence, { + _id: documents.sequence.Templates + }) + + if (seq === undefined) { + return + } + + const { success } = await createDocumentTemplate( + tx, + documents.class.ControlledDocument, + documents.space.QualityDocuments, + documents.mixin.DocumentTemplate, + documents.ids.NoProject, + undefined, + documents.template.ProductChangeControl as unknown as Ref, + 'CC', + { + title: 'Change Control Template for new Product Version', + abstract: + 'This Template is to be used to create a Change Control document each time you want to create a new product version.', + changeControl: ccRecordId, + requests: 0, + reviewers: [], + approvers: [], + coAuthors: [], + code: `TMPL-${seq.sequence + 1}`, + prefix: '', + seqNumber: 0, + major: 0, + minor: 1, + state: DocumentState.Effective, + sections: 0, + commentSequence: 0, + content: getCollaborativeDoc(generateId()) + }, + ccCategory, + undefined, + { + title: 'Section 1' + } + ) + + if (!success) { + return + } + + await createChangeControl(tx, ccRecordId, ccRecord, documents.space.QualityDocuments) + } +} + +async function createTemplateSequence (tx: TxOperations): Promise { + const templateSeq = await tx.findOne(documents.class.Sequence, { + _id: documents.sequence.Templates + }) + + if (templateSeq === undefined) { + await tx.createDoc( + documents.class.Sequence, + documents.space.Documents, + { + attachedTo: documents.mixin.DocumentTemplate, + sequence: 0 + }, + documents.sequence.Templates + ) + } +} + +async function createDocumentCategories (tx: TxOperations): Promise { + const categories: Pick, 'code' | 'title'>[] = [ + { code: 'CA', title: 'CAPA (Corrective and Preventive Action)' }, + { code: 'CC', title: 'Change Control' }, + { code: 'CE', title: 'Clinical Evaluation, Post-Market Clinical Follow-Up' }, + { code: 'CH', title: 'Complaint Handling & Support' }, + { code: 'CS', title: 'Clincial Studies' }, + { code: 'DC', title: 'Document-Control' }, + { code: 'DI', title: 'Design-Input' }, + { code: 'DT', title: 'Design Transfer' }, + { code: 'HF', title: 'Human Factors' }, + { code: 'HR', title: 'Human Resources' }, + { code: 'HW', title: 'Hardware Development' }, + { code: 'IA', title: 'Internal Audit' }, + { code: 'IM', title: 'Installation & Maintenance' }, + { code: 'IS', title: 'Infrastructure' }, + { code: 'LA', title: 'Labeling' }, + { code: 'MA', title: 'Marketing' }, + { code: 'MR', title: 'Management Review' }, + { code: 'PD', title: 'Product Development' }, + { code: 'PM', title: 'Post-Market Surveillance' }, + { code: 'PR', title: 'Production' }, + { code: 'PS', title: 'Purchase and Supplier Management' }, + { code: 'PSA', title: 'Product Safety' }, + { code: 'QM', title: 'Quality Manual' }, + { code: 'RM', title: 'Risk Management' }, + { code: 'RU', title: 'Regulatory Update' }, + { code: 'SA', title: 'Sales and Marketing' }, + { code: 'SU', title: 'Support' }, + { code: 'SW', title: 'Software Development' }, + { code: 'TF', title: 'Technical File, Product Release' }, + { code: 'VI', title: 'Vigilance' }, + { code: 'VV', title: 'Verification & Validation' }, + { code: 'CM', title: 'Client Management' } + ] + + await Promise.all( + categories.map((c) => + createOrUpdate( + tx, + documents.class.DocumentCategory, + documents.space.QualityDocuments, + { ...c, attachments: 0 }, + ((documents.category.DOC as string) + ' - ' + c.code) as Ref + ) + ) + ) +} + +async function createTagCategories (tx: TxOperations): Promise { + await createOrUpdate( + tx, + tags.class.TagCategory, + tags.space.Tags, + { + icon: tags.icon.Tags, + label: 'Labels', + targetClass: documents.class.Document, + tags: [], + default: true + }, + documents.category.Other + ) + + await createOrUpdate( + tx, + tags.class.TagCategory, + tags.space.Tags, + { + icon: tags.icon.Tags, + label: 'Labels', + targetClass: documents.mixin.DocumentTemplate, + tags: [], + default: true + }, + documents.category.OtherTemplate + ) +} + +async function migrateDocumentSpacesMixins (client: MigrationClient): Promise { + const oldSpaceTypeMixin = `${documents.spaceType.DocumentSpaceType}:type:mixin` + const newSpaceTypeMixin = documents.mixin.DocumentSpaceTypeData + const affectedClasses: Ref>[] = [ + documents.class.DocumentSpace, + documents.class.OrgSpace, + documents.class.ExternalSpace + ] + + await client.update( + DOMAIN_TX, + { + objectClass: core.class.Attribute, + 'attributes.attributeOf': oldSpaceTypeMixin + }, + { + $set: { + 'attributes.attributeOf': newSpaceTypeMixin + } + } + ) + + await client.update( + DOMAIN_SPACE, + { + _class: { $in: affectedClasses }, + [oldSpaceTypeMixin]: { $exists: true } + }, + { + $rename: { + [oldSpaceTypeMixin]: newSpaceTypeMixin + } + } + ) +} + +async function migrateDefaultProjectOwners (client: MigrationClient): Promise { + const workspaceOwners = await client.model.findAll(contact.class.PersonAccount, { + role: AccountRole.Owner + }) + + await client.update( + DOMAIN_SPACE, + { + _id: documents.space.QualityDocuments, + $or: [{ owners: { $exists: false } }, { owners: [core.account.System] }] + }, + { + $set: { + owners: workspaceOwners.map((it) => it._id) + } + } + ) +} + +async function migrateMetaTitles (client: MigrationClient): Promise { + const targetMetas = await client.find(DOMAIN_DOCUMENTS, { title: { $exists: false } }) + + for (const meta of targetMetas) { + let targetDoc = ( + await client.find(DOMAIN_DOCUMENTS, { attachedTo: meta._id, state: DocumentState.Effective }) + )[0] + + if (targetDoc === undefined) { + targetDoc = (await client.find(DOMAIN_DOCUMENTS, { attachedTo: meta._id }))[0] + } + + if (targetDoc === undefined) { + continue + } + + await client.update( + DOMAIN_DOCUMENTS, + { _id: meta._id }, + { + $set: { + title: `${getDocumentId(targetDoc)} ${targetDoc.title}` + } + } + ) + } +} + +async function migrateMetaIndexState (client: MigrationClient): Promise { + await client.update( + DOMAIN_DOC_INDEX_STATE, + { + _class: core.class.DocIndexState, + objectClass: documents.class.DocumentMeta + }, + { + $set: { + stages: {} + } + } + ) +} + +export const documentsOperation: MigrateOperation = { + async migrate (client: MigrationClient): Promise { + await migrateSpace(client) + await migrateEffectiveDates(client) + await migrateChangeControls(client) + await migrateTemplatePrefixes(client) + // Note: Order matters! It is important to migrate template prefixes first. + await migrateDocumentCodes(client) + await migrateCollaborativeDocument(client) + await migrateLibraryDocumentSpace(client) + await migrateDocumentSpaces(client) + await migrateDocumentCategories(client) + await migrateUnusedDocumentSpaces(client) + await migrateProjectMeta(client) + await migrateDocumentSpacesMixins(client) + await migrateDefaultProjectOwners(client) + await migrateMetaTitles(client) + await tryMigrate(client, documentsId, [ + { + state: 'migrateMetaIndexState', + func: migrateMetaIndexState + } + ]) + }, + async upgrade (client: MigrationUpgradeClient): Promise { + const tx = new TxOperations(client, core.account.System) + await createDefaultSpace(client, documents.space.Documents, { name: 'Documents', description: 'Documents' }) + await createQualityDocumentsSpace(tx) + await createTemplatesSpace(tx) + await createTemplateSequence(tx) + await createTagCategories(tx) + await createDocumentCategories(tx) + await fixChangeControlsForDocs(tx) + await createProductChangeControlTemplate(tx) + } +} diff --git a/models/controlled-documents/src/permissions.ts b/models/controlled-documents/src/permissions.ts new file mode 100644 index 0000000000..13bbfa7191 --- /dev/null +++ b/models/controlled-documents/src/permissions.ts @@ -0,0 +1,89 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// + +import { type Builder } from '@hcengineering/model' +import core from '@hcengineering/core' +import documents from '@hcengineering/controlled-documents' + +export function definePermissions (builder: Builder): void { + builder.createDoc( + core.class.Permission, + core.space.Model, + { + label: documents.string.ReviewDocumentPermission, + description: documents.string.ReviewDocumentDescription + }, + documents.permission.ReviewDocument + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + label: documents.string.ApproveDocumentPermission, + description: documents.string.ApproveDocumentDescription + }, + documents.permission.ApproveDocument + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + label: documents.string.CoAuthorDocumentPermission, + description: documents.string.CoAuthorDocumentDescription + }, + documents.permission.CoAuthorDocument + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + label: documents.string.CreateDocumentPermission, + description: documents.string.CreateDocumentDescription + }, + documents.permission.CreateDocument + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + label: documents.string.UpdateDocumentOwnerPermission, + description: documents.string.UpdateDocumentOwnerDescription + }, + documents.permission.UpdateDocumentOwner + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + label: documents.string.CreateDocumentCategoryPermission, + description: documents.string.CreateDocumentCategoryDescription + }, + documents.permission.CreateDocumentCategory + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + label: documents.string.UpdateDocumentCategoryPermission, + description: documents.string.UpdateDocumentCategoryDescription + }, + documents.permission.UpdateDocumentCategory + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + label: documents.string.DeleteDocumentCategoryPermission, + description: documents.string.DeleteDocumentCategoryDescription + }, + documents.permission.DeleteDocumentCategory + ) +} diff --git a/models/controlled-documents/src/plugin.ts b/models/controlled-documents/src/plugin.ts new file mode 100644 index 0000000000..18b4ef1814 --- /dev/null +++ b/models/controlled-documents/src/plugin.ts @@ -0,0 +1,91 @@ +// +// Copyright © 2023 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { documentsId } from '@hcengineering/controlled-documents' +import documents from '@hcengineering/controlled-documents-resources/src/plugin' +import type { Client, Doc, Ref, Role } from '@hcengineering/core' +import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/model-presentation' +import { mergeIds, type Resource } from '@hcengineering/platform' +import { type TagCategory } from '@hcengineering/tags' +import { type AnyComponent } from '@hcengineering/ui' +import { type ActionCategory, type ViewAction } from '@hcengineering/view' +import { type NotificationType, type NotificationGroup } from '@hcengineering/notification' + +export default mergeIds(documentsId, documents, { + component: { + DocumentTemplateSectionPresenter: '' as AnyComponent, + ContentSectionPresenter: '' as AnyComponent, + AttachmentSectionPresenter: '' as AnyComponent, + DocumentVersions: '' as AnyComponent, + EditDocumentContent: '' as AnyComponent, + EditDocumentAttachment: '' as AnyComponent, + TemplateSectionPresenter: '' as AnyComponent, + + // new model components + CategoryPresenter: '' as AnyComponent, + Categories: '' as AnyComponent, + DocumentTemplates: '' as AnyComponent, + CollaborativeSectionPresenter: '' as AnyComponent, + AttachmentsSectionPresenter: '' as AnyComponent, + StateFilterValuePresenter: '' as AnyComponent, + ControlledStateFilterValuePresenter: '' as AnyComponent, + + // Projects + Projects: '' as AnyComponent + }, + completion: { + DocumentMetaQuery: '' as Resource, + DocumentMetaCategory: '' as Ref + }, + category: { + Document: '' as Ref, + Other: '' as Ref, + OtherTemplate: '' as Ref + }, + function: { + DocumentIdentifierProvider: '' as Resource<(client: Client, ref: Ref, doc?: T) => Promise> + }, + actionImpl: { + AddCollaborativeSectionAbove: '' as ViewAction, + AddCollaborativeSectionBelow: '' as ViewAction, + DeleteCollaborativeSection: '' as ViewAction, + Duplicate: '' as ViewAction, + EditDescription: '' as ViewAction, + EditGuidance: '' as ViewAction, + CreateChildDocument: '' as ViewAction, + CreateChildTemplate: '' as ViewAction, + CreateDocument: '' as ViewAction, + CreateTemplate: '' as ViewAction, + DeleteDocument: '' as ViewAction, + EditDocSpace: '' as ViewAction + }, + viewlet: { + TableDocument: '' as Ref, + ListDocument: '' as Ref, + TableDocumentTemplate: '' as Ref, + TableDocumentDomain: '' as Ref + }, + role: { + QARA: '' as Ref, + Manager: '' as Ref, + QualifiedUser: '' as Ref + }, + notification: { + DocumentsNotificationGroup: '' as Ref, + ContentNotification: '' as Ref, + StateNotification: '' as Ref, + CoAuthorsNotification: '' as Ref + } +}) diff --git a/models/controlled-documents/src/roles.ts b/models/controlled-documents/src/roles.ts new file mode 100644 index 0000000000..de3ba4a92b --- /dev/null +++ b/models/controlled-documents/src/roles.ts @@ -0,0 +1,48 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// + +import core from '@hcengineering/core' + +import documents from './plugin' + +export const roles = [ + { + _id: documents.role.QualifiedUser, + name: 'Qualified User', + permissions: [ + documents.permission.ReviewDocument, + documents.permission.ApproveDocument, + documents.permission.CoAuthorDocument + ] + }, + { + _id: documents.role.Manager, + name: 'Manager', + permissions: [ + documents.permission.CreateDocument, + documents.permission.ReviewDocument, + documents.permission.ApproveDocument, + documents.permission.CoAuthorDocument, + documents.permission.CreateDocumentCategory, + documents.permission.UpdateDocumentCategory, + documents.permission.DeleteDocumentCategory, + core.permission.UpdateSpace + ] + }, + { + _id: documents.role.QARA, + name: 'QARA', + permissions: [ + documents.permission.CreateDocument, + documents.permission.ReviewDocument, + documents.permission.ApproveDocument, + documents.permission.CoAuthorDocument, + documents.permission.UpdateDocumentOwner, + documents.permission.CreateDocumentCategory, + documents.permission.UpdateDocumentCategory, + documents.permission.DeleteDocumentCategory, + core.permission.UpdateSpace + ] + } +] diff --git a/models/controlled-documents/src/spaceType.ts b/models/controlled-documents/src/spaceType.ts new file mode 100644 index 0000000000..5ba8276a56 --- /dev/null +++ b/models/controlled-documents/src/spaceType.ts @@ -0,0 +1,85 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import { Prop, type Builder } from '@hcengineering/model' +import core from '@hcengineering/model-core' +import { getRoleAttributeProps } from '@hcengineering/setting' +import { type Permission, type Ref } from '@hcengineering/core' + +import { TDocumentSpaceTypeData } from './types' +import documents from './plugin' +import { roles } from './roles' + +export const documentPermissions: Ref[] = [ + documents.permission.CreateDocument, + documents.permission.ReviewDocument, + documents.permission.ApproveDocument, + documents.permission.CoAuthorDocument, + documents.permission.UpdateDocumentOwner, + documents.permission.CreateDocumentCategory, + documents.permission.UpdateDocumentCategory, + documents.permission.DeleteDocumentCategory, + core.permission.UpdateSpace +] + +export function defineSpaceType (builder: Builder): void { + for (const role of roles) { + const { label, roleType } = getRoleAttributeProps(role.name) + + Prop(roleType, label)(TDocumentSpaceTypeData.prototype, role._id) + } + + builder.createModel(TDocumentSpaceTypeData) + + builder.createDoc( + core.class.SpaceTypeDescriptor, + core.space.Model, + { + name: documents.string.DocumentApplication, + description: documents.string.Description, + icon: documents.icon.Document, + baseClass: documents.class.DocumentSpace, + availablePermissions: [...documentPermissions] + }, + documents.descriptor.DocumentSpaceType + ) + + builder.createDoc( + documents.class.DocumentSpaceType, + core.space.Model, + { + name: 'Default Documents', + descriptor: documents.descriptor.DocumentSpaceType, + roles: roles.length, + projects: false, + targetClass: documents.mixin.DocumentSpaceTypeData + }, + documents.spaceType.DocumentSpaceType + ) + + for (const role of roles) { + builder.createDoc( + core.class.Role, + core.space.Model, + { + attachedTo: documents.spaceType.DocumentSpaceType, + attachedToClass: documents.class.DocumentSpaceType, + collection: 'roles', + name: role.name, + permissions: role.permissions + }, + role._id + ) + } +} diff --git a/models/controlled-documents/src/types.ts b/models/controlled-documents/src/types.ts new file mode 100644 index 0000000000..b902477bb8 --- /dev/null +++ b/models/controlled-documents/src/types.ts @@ -0,0 +1,590 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import request from '@hcengineering/request' +import { + type AttachmentsDocumentSection, + type ChangeControl, + type CollaborativeDocumentSection, + type ControlledDocument, + type ControlledDocumentState, + type Document, + type HierarchyDocument, + type DocumentApprovalRequest, + type DocumentCategory, + type DocumentRequest, + type DocumentReviewRequest, + type DocumentSection, + type DocumentComment, + type DocumentSectionCreator, + type DocumentSectionEditor, + type DocumentSectionPresenter, + type DocumentSpace, + type DocumentSpaceType, + type DocumentSpaceTypeDescriptor, + type DocumentState, + type DocumentTemplate, + type DocumentTemplateSection, + type Sequence, + type DocumentMeta, + type ExternalSpace, + type OrgSpace, + type Project, + type ProjectDocument, + type ProjectMeta, + type DocumentTraining, + type DocumentSnapshot, + type ControlledDocumentSnapshot +} from '@hcengineering/controlled-documents' +import { TRequest } from '@hcengineering/model-request' +import { type Attachment } from '@hcengineering/attachment' +import contact, { type Employee } from '@hcengineering/contact' +import { + DateRangeMode, + IndexKind, + type AttachedData, + type Class, + type Doc, + type Markup, + type Ref, + type Timestamp, + type Type, + type CollectionSize, + type CollaborativeDoc, + type Role, + type TypedSpace, + type Account, + type RolesAssignment, + type Domain +} from '@hcengineering/core' +import { + ArrOf, + Collection, + Hidden, + Index, + Mixin, + Model, + Prop, + TypeBoolean, + TypeDate, + TypeNumber, + TypeRef, + TypeString, + UX, + TypeCollaborativeDoc, + TypeMarkup, + ReadOnly +} from '@hcengineering/model' +import attachment from '@hcengineering/model-attachment' +import chunter, { TChatMessage } from '@hcengineering/model-chunter' +import core, { + TAttachedDoc, + TClass, + TDoc, + TTypedSpace, + TType, + TSpaceTypeDescriptor, + TSpaceType +} from '@hcengineering/model-core' +import { getEmbeddedLabel, type Resource } from '@hcengineering/platform' +import tags, { type TagReference } from '@hcengineering/tags' +import { type AnyComponent } from '@hcengineering/ui' +import training, { type Training, type TrainingRequest } from '@hcengineering/training' + +import documents from './plugin' + +export const DOMAIN_DOCUMENTS = 'documents' as Domain + +@Model(documents.class.DocumentSpace, core.class.TypedSpace) +@UX(core.string.Space) +export class TDocumentSpace extends TTypedSpace implements DocumentSpace { + @Prop(TypeRef(documents.class.DocumentSpaceType), documents.string.DocumentSpaceType) + declare type: Ref +} + +@Model(documents.class.DocumentSpaceType, core.class.SpaceType) +export class TDocumentSpaceType extends TSpaceType implements DocumentSpaceType { + @Prop(TypeRef(documents.class.DocumentSpaceTypeDescriptor), getEmbeddedLabel('Descriptor')) + declare descriptor: Ref + + @Prop(TypeRef(core.class.Class), getEmbeddedLabel('Target Class')) + declare targetClass: Ref> + + @Prop(TypeBoolean(), getEmbeddedLabel('Projects')) + projects!: boolean +} + +@Model(documents.class.DocumentSpaceTypeDescriptor, core.class.SpaceTypeDescriptor) +export class TDocumentSpaceTypeDescriptor extends TSpaceTypeDescriptor implements DocumentSpaceTypeDescriptor { + projectClass?: Ref> +} + +@Model(documents.class.OrgSpace, documents.class.DocumentSpace) +export class TOrgSpace extends TDocumentSpace implements OrgSpace {} + +@Model(documents.class.ExternalSpace, documents.class.DocumentSpace) +export class TExternalSpace extends TDocumentSpace implements ExternalSpace {} + +@Model(documents.class.Project, core.class.Doc, DOMAIN_DOCUMENTS) +export class TProject extends TDoc implements Project { + @Prop(TypeRef(documents.class.DocumentSpace), documents.string.Space) + @Index(IndexKind.Indexed) + declare space: Ref + + @Prop(TypeString(), documents.string.Name) + @Index(IndexKind.FullText) + @ReadOnly() + name!: string + + @Prop(TypeBoolean(), documents.string.Readonly) + @ReadOnly() + @Hidden() + readonly!: boolean +} + +@Model(documents.class.DocumentMeta, core.class.Doc, DOMAIN_DOCUMENTS) +@UX(documents.string.Document) +export class TDocumentMeta extends TDoc implements DocumentMeta { + @Prop(Collection(documents.class.Document), documents.string.Documents) + documents!: CollectionSize + + @Prop(TypeString(), documents.string.Title) + @Index(IndexKind.FullText) + title!: string +} + +@Model(documents.class.ProjectMeta, core.class.Doc, DOMAIN_DOCUMENTS) +@UX(documents.string.Project) +export class TProjectMeta extends TDoc implements ProjectMeta { + @Prop(TypeRef(documents.class.Project), documents.string.Project) + @Index(IndexKind.Indexed) + project!: Ref + + @Prop(TypeRef(documents.class.DocumentMeta), documents.string.Document) + @Index(IndexKind.Indexed) + @Hidden() + meta!: Ref + + @Prop(ArrOf(TypeRef(documents.class.DocumentMeta)), documents.string.Path) + path!: Ref[] + + @Prop(TypeRef(documents.class.DocumentMeta), documents.string.Parent) + @Index(IndexKind.Indexed) + parent!: Ref + + // @Prop(TypeRef(documents.class.Document), documents.string.Document) + // @Index(IndexKind.Indexed) + // head!: Ref + + @Prop(Collection(documents.class.ProjectDocument), documents.string.Documents) + documents!: CollectionSize +} + +@Model(documents.class.ProjectDocument, core.class.AttachedDoc, DOMAIN_DOCUMENTS) +@UX(documents.string.Document) +export class TProjectDocument extends TAttachedDoc implements ProjectDocument { + @Prop(TypeRef(core.class.Space), core.string.Space) + @Index(IndexKind.Indexed) + @Hidden() + declare space: Ref + + @Prop(TypeRef(documents.class.ProjectMeta), documents.string.Project) + @Index(IndexKind.Indexed) + @Hidden() + declare attachedTo: Ref + + @Prop(TypeString(), core.string.Collection) + @Hidden() + override collection: 'documents' = 'documents' + + @Prop(TypeRef(documents.class.Project), documents.string.Project) + @Index(IndexKind.Indexed) + project!: Ref + + @Prop(TypeRef(documents.class.Project), documents.string.Project) + @Hidden() + initial!: Ref + + @Prop(TypeRef(documents.class.Document), documents.string.Document) + @Index(IndexKind.Indexed) + document!: Ref +} + +@Model(documents.class.Document, core.class.Doc, DOMAIN_DOCUMENTS) +@UX(documents.string.Document, documents.icon.Document) +export class TDocument extends TDoc implements Document { + @Prop(TypeRef(core.class.Space), core.string.Space) + @Index(IndexKind.Indexed) + @Hidden() + declare space: Ref + + @Prop(TypeRef(documents.mixin.DocumentTemplate), documents.string.DocumentTemplate) + @Hidden() + template?: Ref + + @Prop(TypeString(), documents.string.Title) + @Index(IndexKind.FullText) + title!: string + + @Prop(TypeString(), documents.string.Title) + @Index(IndexKind.FullText) + code!: string + + @Prop(TypeString(), documents.string.Code) + @Index(IndexKind.FullText) + prefix!: string + + @Prop(TypeNumber(), documents.string.Number) + @Hidden() + seqNumber!: number + + @Prop(TypeNumber(), documents.string.Major) + major!: number + + @Prop(TypeNumber(), documents.string.Minor) + minor!: number + + @Prop(TypeRef(documents.class.DocumentCategory), documents.string.Category) + category?: Ref + + @Prop(TypeRef(contact.mixin.Employee), documents.string.Author) + author?: Ref + + @Prop(TypeRef(contact.mixin.Employee), documents.string.Owner) + owner?: Ref + + @Prop(TypeDocumentState(), documents.string.Status) + state!: DocumentState + + @Prop(TypeCollaborativeDoc(), documents.string.CollaborativeDocument) + @Hidden() + content!: CollaborativeDoc + + @Prop(Collection(tags.class.TagReference), documents.string.Labels) + labels?: CollectionSize + + @Prop(Collection(documents.class.DocumentSection), documents.string.Sections) + sections!: CollectionSize + + @Prop(TypeString(), documents.string.MetaAbstract) + @Index(IndexKind.FullText) + abstract?: string + + @Prop(TypeNumber(), documents.string.Number) + @Hidden() + commentSequence!: number + + @Prop(Collection(documents.class.DocumentComment), chunter.string.Comments) + comments?: CollectionSize + + @Prop(Collection(documents.class.DocumentSnapshot), documents.string.Snapshots) + snapshots?: CollectionSize +} + +@Model(documents.class.HierarchyDocument, documents.class.Document) +@UX(documents.string.Document, documents.icon.Document) +export class THierarchyDocument extends TDocument implements HierarchyDocument { + @Prop(TypeRef(documents.class.DocumentMeta), core.string.AttachedTo) + @Index(IndexKind.Indexed) + @Hidden() + attachedTo!: Ref + + @Prop(TypeRef(core.class.Class), core.string.AttachedToClass) + @Index(IndexKind.Indexed) + @Hidden() + attachedToClass!: Ref> + + @Prop(TypeString(), core.string.Collection) + @Hidden() + collection!: 'documents' +} + +@Mixin(documents.mixin.DocumentTemplate, documents.class.Document) +@UX(documents.string.DocumentTemplate, documents.icon.Document) +export class TDocumentTemplate extends TDocument implements DocumentTemplate { + @Prop(TypeNumber(), core.string.Collection) + @Hidden() + sequence!: number + + @Prop(TypeString(), documents.string.DocumentPrefix) + @Index(IndexKind.FullText) + docPrefix!: string +} + +@Mixin(documents.mixin.DocumentTraining, documents.class.Document) +@UX(training.string.Training, training.icon.Training) +export class TDocumentTraining extends TDocument implements DocumentTraining { + @Prop(TypeBoolean(), documents.string.DocumentTrainingEnabled, { defaultValue: false }) + enabled: boolean = false + + @Prop(TypeRef(training.class.Training), training.string.Training, { defaultValue: null }) + training: Ref | null = null + + @Prop(ArrOf(TypeRef(core.class.Role)), training.string.TrainingRequestRoles, { defaultValue: [] }) + roles: Array> = [] + + @Prop(ArrOf(TypeRef(contact.mixin.Employee)), training.string.TrainingRequestTrainees, { defaultValue: [] }) + trainees: TrainingRequest['trainees'] = [] + + @Prop(TypeNumber(), training.string.TrainingRequestMaxAttempts, { defaultValue: null }) + maxAttempts: TrainingRequest['maxAttempts'] = null + + @Prop(TypeNumber(), documents.string.DocumentTrainingDueDays, { defaultValue: null }) + dueDays: number | null = null +} + +@Model(documents.class.DocumentCategory, core.class.Doc, DOMAIN_DOCUMENTS) +@UX(documents.string.Category) +export class TDocumentCategory extends TDoc implements DocumentCategory { + @Prop(TypeRef(core.class.TypedSpace), core.string.Space) + @Index(IndexKind.Indexed) + @Hidden() + declare space: Ref + + @Prop(TypeString(), documents.string.Code) + @Index(IndexKind.FullText) + code!: string + + @Prop(TypeString(), documents.string.Title) + @Index(IndexKind.FullText) + title!: string + + @Prop(TypeMarkup(), documents.string.Description) + @Index(IndexKind.FullText) + description?: string + + @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) + attachments?: CollectionSize +} + +@Model(documents.class.ControlledDocument, documents.class.HierarchyDocument) +@UX( + documents.string.ControlledDocument, + documents.icon.Document, + undefined, + undefined, + undefined, + documents.string.Documents +) +export class TControlledDocument extends THierarchyDocument implements ControlledDocument { + @Prop(Collection(documents.class.DocumentRequest), documents.string.Requests) + @Hidden() + requests!: CollectionSize + + @Prop(ArrOf(TypeRef(contact.mixin.Employee)), documents.string.Reviewers) + reviewers!: Ref[] + + @Prop(ArrOf(TypeRef(contact.mixin.Employee)), documents.string.Approvers) + approvers!: Ref[] + + @Prop(ArrOf(TypeRef(contact.mixin.Employee)), documents.string.CoAuthors) + coAuthors!: Ref[] + + @Prop(TypeNumber(), documents.string.ReviewInterval) + reviewInterval?: number + + @Prop(TypeControlledDocumentState(), documents.string.ControlledStatus) + controlledState?: ControlledDocumentState + + @Prop(TypeDate(DateRangeMode.DATE), documents.string.EffectiveDate) + effectiveDate?: Timestamp + + @Prop(TypeDate(DateRangeMode.DATE), documents.string.PlannedEffectiveDate) + @Hidden() + plannedEffectiveDate!: Timestamp + + @Prop(TypeRef(documents.class.Document), documents.string.ChangeControl) + @Hidden() + changeControl!: Ref +} + +@Model(documents.class.ChangeControl, core.class.Doc, DOMAIN_DOCUMENTS) +@UX(documents.string.ChangeControl) +export class TChangeControl extends TDoc implements ChangeControl { + @Prop(TypeString(), documents.string.Description) + @Index(IndexKind.FullText) + description!: string + + @Prop(TypeString(), documents.string.Reason) + @Index(IndexKind.FullText) + reason!: string + + @Prop(TypeString(), documents.string.ImpactAnalysis) + impact!: string + + @Prop(ArrOf(TypeRef(documents.class.Document)), documents.string.ImpactedDocuments) + impactedDocuments!: Ref[] +} + +@Model(documents.class.DocumentSection, core.class.AttachedDoc, DOMAIN_DOCUMENTS) +@UX(documents.string.Section) +export class TDocumentSection extends TAttachedDoc implements DocumentSection { + @Prop(TypeString(), documents.string.Title) + @Index(IndexKind.FullText) + title!: string + + @Prop(TypeString(), documents.string.Rank) + @Hidden() + rank!: string + + @Prop(TypeString(), documents.string.Key) + @Hidden() + key!: string + + @Prop(TypeRef(documents.mixin.DocumentTemplateSection), documents.string.SectionTemplate) + templateSectionId?: Ref +} + +@Model(documents.class.DocumentSnapshot, core.class.AttachedDoc, DOMAIN_DOCUMENTS) +@UX(documents.string.Snapshot) +export class TDocumentSnapshot extends TAttachedDoc implements DocumentSnapshot { + @Prop(TypeString(), documents.string.Name) + name?: string + + @Prop(TypeCollaborativeDoc(), documents.string.CollaborativeDocument) + @Hidden() + content!: CollaborativeDoc + + @Prop(TypeDocumentState(), documents.string.Status) + state?: DocumentState + + @Prop(Collection(documents.class.DocumentSection), documents.string.Sections) + sections!: CollectionSize +} + +@Model(documents.class.ControlledDocumentSnapshot, documents.class.DocumentSnapshot) +@UX(documents.string.ControlledSnapshot) +export class TControlledDocumentSnapshot extends TDocumentSnapshot implements ControlledDocumentSnapshot { + @Prop(TypeControlledDocumentState(), documents.string.Status) + controlledState!: ControlledDocumentState +} + +@Model(documents.class.DocumentComment, chunter.class.ChatMessage) +export class TDocumentComment extends TChatMessage implements DocumentComment { + @Prop(TypeString(), documents.string.SectionKey) + sectionKey?: string + + @Prop(TypeString(), documents.string.ID) + nodeId?: string + + @Prop(TypeBoolean(), documents.string.Resolve) + resolved?: boolean + + @Prop(TypeNumber(), documents.string.Index) + index?: number +} + +@Mixin(documents.mixin.DocumentTemplateSection, documents.class.DocumentSection) +@UX(documents.string.SectionTemplate) +export class TDocumentTemplateSection extends TDocumentSection implements DocumentTemplateSection { + @Prop(TypeBoolean(), documents.string.Required) + mandatory?: boolean + + @Prop(TypeString(), documents.string.Description) + @Index(IndexKind.FullText) + description?: string + + @Prop(TypeMarkup(), documents.string.Guidance) + @Index(IndexKind.FullText) + guidance?: Markup +} + +@Model(documents.class.CollaborativeDocumentSection, documents.class.DocumentSection) +@UX(documents.string.CollaborativeSection) +export class TCollaborativeDocumentSection extends TDocumentSection implements CollaborativeDocumentSection { + @Prop(TypeString(), documents.string.CollaboratorSectionId) + @Hidden() + collaboratorSectionId!: string + + @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) + attachments?: CollectionSize +} + +@Model(documents.class.AttachmentsDocumentSection, documents.class.DocumentSection) +@UX(documents.string.AttachmentsSection) +export class TAttachmentsDocumentSection extends TDocumentSection implements AttachmentsDocumentSection { + @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) + attachments?: CollectionSize + + @Prop(TypeNumber(), documents.string.AttachmentsMax) + maximum?: number +} + +@Model(documents.class.Sequence, core.class.Doc, DOMAIN_DOCUMENTS) +export class TSequence extends TDoc implements Sequence { + attachedTo!: Ref> + sequence!: number +} + +@Model(documents.class.DocumentRequest, request.class.Request) +@UX(documents.string.DocumentRequest) +export class TDocumentRequest extends TRequest implements DocumentRequest {} + +@Model(documents.class.DocumentReviewRequest, documents.class.DocumentRequest) +@UX(documents.string.DocumentReviewRequest) +export class TDocumentReviewRequest extends TDocumentRequest implements DocumentReviewRequest {} + +@Model(documents.class.DocumentApprovalRequest, documents.class.DocumentRequest) +@UX(documents.string.DocumentApprovalRequest) +export class TDocumentApprovalRequest extends TDocumentRequest implements DocumentApprovalRequest {} + +@Mixin(documents.mixin.DocumentSectionEditor, core.class.Class) +export class TDocumentSectionEditor extends TClass implements DocumentSectionEditor { + editor!: AnyComponent +} + +@Mixin(documents.mixin.DocumentSectionPresenter, core.class.Class) +export class TDocumentSectionPresenter extends TClass implements DocumentSectionPresenter { + presenter!: AnyComponent +} + +@Mixin(documents.mixin.DocumentSectionCreator, core.class.Class) +export class TDocumentSectionCreator extends TClass implements DocumentSectionCreator { + creator!: Resource< + ( + document: Document, + section: AttachedData, + copyFrom?: DocumentSection + ) => AttachedData + > +} + +@Mixin(documents.mixin.DocumentSpaceTypeData, documents.class.DocumentSpace) +@UX(getEmbeddedLabel('Default Documents'), documents.icon.Document) +export class TDocumentSpaceTypeData extends TDocumentSpace implements RolesAssignment { + [key: Ref]: Ref[] +} + +/** + * @public + */ +export function TypeDocumentState (): Type { + return { _class: documents.class.TypeDocumentState, label: documents.string.Status } +} + +@UX(documents.string.Status) +@Model(documents.class.TypeDocumentState, core.class.Type) +export class TTypeDocumentState extends TType {} + +/** + * @public + */ +export function TypeControlledDocumentState (): Type { + return { _class: documents.class.TypeControlledDocumentState, label: documents.string.ControlledStatus } +} + +@UX(documents.string.ControlledStatus) +@Model(documents.class.TypeControlledDocumentState, core.class.Type) +export class TTypeControlledDocumentState extends TType {} diff --git a/models/controlled-documents/tsconfig.json b/models/controlled-documents/tsconfig.json new file mode 100644 index 0000000000..9765b6086d --- /dev/null +++ b/models/controlled-documents/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/models/print/.eslintrc.js b/models/print/.eslintrc.js new file mode 100644 index 0000000000..c1cf82cba0 --- /dev/null +++ b/models/print/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/print/.npmignore b/models/print/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/print/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/print/config/rig.json b/models/print/config/rig.json new file mode 100644 index 0000000000..2f6be36605 --- /dev/null +++ b/models/print/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "model" +} diff --git a/models/print/package.json b/models/print/package.json new file mode 100644 index 0000000000..e3da767eb1 --- /dev/null +++ b/models/print/package.json @@ -0,0 +1,43 @@ +{ + "name": "@hcengineering/model-print", + "version": "0.6.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Uberflow Contributors", + "template": "@hcengineering/model-package", + "license": "EPL-2.0", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/core": "^0.6.28", + "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model": "^0.6.7", + "@hcengineering/model-view": "^0.6.0", + "@hcengineering/platform": "^0.6.9", + "@hcengineering/view": "^0.6.9", + "@hcengineering/ui": "^0.6.11", + "@hcengineering/presentation": "^0.6.2", + "@hcengineering/model-presentation": "^0.6.0", + "@hcengineering/print-resources": "^0.6.0", + "@hcengineering/print": "^0.6.0" + } +} diff --git a/models/print/src/index.ts b/models/print/src/index.ts new file mode 100644 index 0000000000..1b6e523d98 --- /dev/null +++ b/models/print/src/index.ts @@ -0,0 +1,44 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// + +import { type Builder } from '@hcengineering/model' +import core from '@hcengineering/model-core' +import view, { createAction } from '@hcengineering/model-view' +import presentation from '@hcengineering/model-presentation' + +import print from './plugin' + +export { printId } from '@hcengineering/print' +export * from './migration' +export default print + +export function createModel (builder: Builder): void { + createAction( + builder, + { + action: print.actionImpl.Print, + label: print.string.PrintToPDF, + icon: print.icon.Print, + category: view.category.General, + input: 'focus', // NOTE: should only work for one doc for now, not bulk + target: core.class.Doc, + context: { mode: ['context', 'browser'], group: 'tools' }, + visibilityTester: print.function.CanPrint + }, + print.action.Print + ) + + builder.createDoc( + presentation.class.FilePreviewExtension, + core.space.Model, + { + contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + alignment: 'float', + component: print.component.DOCXViewer, + extension: presentation.extension.FilePreviewExtension, + availabilityChecker: print.function.CanConvert + }, + print.previewExtension.DOCX + ) +} diff --git a/models/print/src/migration.ts b/models/print/src/migration.ts new file mode 100644 index 0000000000..a388f11f29 --- /dev/null +++ b/models/print/src/migration.ts @@ -0,0 +1,21 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model' + +export const loveOperation: MigrateOperation = { + async migrate (client: MigrationClient): Promise {}, + async upgrade (client: MigrationUpgradeClient): Promise {} +} diff --git a/models/print/src/plugin.ts b/models/print/src/plugin.ts new file mode 100644 index 0000000000..154799ed6d --- /dev/null +++ b/models/print/src/plugin.ts @@ -0,0 +1,37 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { type Doc, type Ref } from '@hcengineering/core' +import { mergeIds } from '@hcengineering/platform' +import { type ViewAction, type Action } from '@hcengineering/view' +import { type FilePreviewExtension } from '@hcengineering/presentation' +import { printId } from '@hcengineering/print' +// This import is needed because of similar import in print plugin +// Otherwise, TS complains about missing types for resulting merge +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { type AnyComponent } from '@hcengineering/ui/src/types' +import print from '@hcengineering/print-resources/src/plugin' + +export default mergeIds(printId, print, { + action: { + Print: '' as Ref>> + }, + actionImpl: { + Print: '' as ViewAction + }, + previewExtension: { + DOCX: '' as Ref + } +}) diff --git a/models/print/tsconfig.json b/models/print/tsconfig.json new file mode 100644 index 0000000000..9765b6086d --- /dev/null +++ b/models/print/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/models/products/.eslintrc.js b/models/products/.eslintrc.js new file mode 100644 index 0000000000..c1cf82cba0 --- /dev/null +++ b/models/products/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/products/.npmignore b/models/products/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/products/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/products/config/rig.json b/models/products/config/rig.json new file mode 100644 index 0000000000..2f6be36605 --- /dev/null +++ b/models/products/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "model" +} diff --git a/models/products/package.json b/models/products/package.json new file mode 100644 index 0000000000..daa57be191 --- /dev/null +++ b/models/products/package.json @@ -0,0 +1,57 @@ +{ + "name": "@hcengineering/model-products", + "version": "0.1.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Hardcore Engineering Inc.", + "template": "@hcengineering/model-package", + "scripts": { + "build": "compile", + "build:watch": "compile", + "genmodel": "ts-node src/__genmodel.ts", + "version": "ts-node src/__showversion.ts", + "bumpversion": "ts-node src/__bumpversion.ts", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.8.0", + "@types/node": "~20.11.16", + "ts-node": "^10.8.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/attachment": "^0.6.9", + "@hcengineering/model": "^0.6.7", + "@hcengineering/model-attachment": "^0.6.0", + "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model-presentation": "^0.6.0", + "@hcengineering/model-view": "^0.6.0", + "@hcengineering/model-workbench": "^0.6.1", + "@hcengineering/activity": "^0.6.0", + "@hcengineering/platform": "^0.6.9", + "@hcengineering/core": "^0.6.28", + "@hcengineering/contact": "^0.6.20", + "@hcengineering/ui": "^0.6.11", + "@hcengineering/view": "^0.6.9", + "@hcengineering/model-tracker": "^0.6.0", + "@hcengineering/chunter": "^0.6.12", + "@hcengineering/setting": "^0.6.11", + "@hcengineering/products": "~0.1.0", + "@hcengineering/products-resources": "~0.1.0", + "@hcengineering/controlled-documents": "^0.1.0", + "@hcengineering/model-controlled-documents": "^0.1.0" + } +} diff --git a/models/products/src/index.ts b/models/products/src/index.ts new file mode 100644 index 0000000000..ea7dc6a40b --- /dev/null +++ b/models/products/src/index.ts @@ -0,0 +1,489 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import documents, { TExternalSpace, TProject } from '@hcengineering/model-controlled-documents' +import type { Document } from '@hcengineering/controlled-documents' +import type { Product, ProductVersionState, ProductVersion } from '@hcengineering/products' +import { productsId } from '@hcengineering/products' +import activity from '@hcengineering/activity' +import { type Attachment } from '@hcengineering/attachment' +import contact from '@hcengineering/contact' +import chunter from '@hcengineering/chunter' +import { getRoleAttributeProps } from '@hcengineering/setting' +import type { Type, CollectionSize, Markup, Arr, RolesAssignment, Permission, Role } from '@hcengineering/core' +import { IndexKind, Ref, Account } from '@hcengineering/core' +import { + type Builder, + Model, + Prop, + TypeRef, + UX, + TypeMarkup, + Index, + TypeString, + Hidden, + TypeNumber, + Collection, + ArrOf, + TypeAny, + ReadOnly, + Mixin +} from '@hcengineering/model' +import attachment from '@hcengineering/model-attachment' +import core, { TType } from '@hcengineering/model-core' +import presentation from '@hcengineering/model-presentation' +import tracker from '@hcengineering/model-tracker' +import { type Action, type ViewAction } from '@hcengineering/view' +import view, { createAction } from '@hcengineering/model-view' +import workbench from '@hcengineering/model-workbench' +import { getEmbeddedLabel, type Asset } from '@hcengineering/platform' + +import products from './plugin' +import { roles } from './roles' + +export { productsId } from '@hcengineering/products/src/index' + +const productPermissions: Ref[] = [ + documents.permission.CreateDocument, + documents.permission.ReviewDocument, + documents.permission.ApproveDocument, + documents.permission.CoAuthorDocument, + documents.permission.UpdateDocumentOwner, + core.permission.UpdateSpace, + core.permission.ArchiveSpace +] + +/** @public */ +export function TypeProductVersionState (): Type { + return { _class: products.class.TypeProductVersionState, label: products.string.ProductVersion } +} + +@Model(products.class.TypeProductVersionState, core.class.Type) +@UX(products.string.ProductVersionState) +export class TTypeProductVersionState extends TType {} + +@Model(products.class.Product, documents.class.ExternalSpace) +@UX(products.string.Product, products.icon.Product, 'Product', 'name', undefined, products.string.Products) +export class TProduct extends TExternalSpace implements Product { + @Prop(ArrOf(TypeRef(core.class.Account)), core.string.Members) + declare members: Arr> + + @Prop(TypeMarkup(), products.string.Description) + @Index(IndexKind.FullText) + fullDescription?: string + + @Prop(TypeString(), products.string.Icon) + @Index(IndexKind.FullText) + @Hidden() + icon?: Asset + + @Prop(TypeNumber(), products.string.Color) + @Index(IndexKind.FullText) + @Hidden() + color?: number + + @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) + attachments?: CollectionSize +} + +@Model(products.class.ProductVersion, documents.class.Project) +@UX(products.string.ProductVersion, products.icon.ProductVersion, undefined, undefined, products.string.ProductVersions) +export class TProductVersion extends TProject implements ProductVersion { + @Prop(TypeRef(products.class.Product), products.string.Product) + @Index(IndexKind.Indexed) + declare space: Ref + + @Prop(TypeNumber(), products.string.Major) + @ReadOnly() + major!: number + + @Prop(TypeNumber(), products.string.Minor) + @ReadOnly() + minor!: number + + @Prop(TypeString(), products.string.Codename) + codename?: string + + @Prop(TypeMarkup(), products.string.Description) + @Index(IndexKind.FullText) + description!: Markup + + @Prop(TypeRef(products.class.ProductVersion), products.string.ProductVersionParent) + @Index(IndexKind.Indexed) + @ReadOnly() + parent!: Ref + + @Prop(TypeProductVersionState(), products.string.ProductVersionState) + @ReadOnly() + state!: ProductVersionState + + @Prop( + TypeAny( + products.component.ChangeControlInlineEditor, + products.string.ChangeControl, + products.component.ChangeControlInlineEditor + ), + products.string.ChangeControl + ) + @ReadOnly() + changeControl?: Ref +} + +@Mixin(products.mixin.ProductTypeData, products.class.Product) +@UX(getEmbeddedLabel('Default Products'), products.icon.ProductVersion) +export class TProductTypeData extends TProduct implements RolesAssignment { + [key: Ref]: Ref[] +} + +function defineProduct (builder: Builder): void { + builder.createModel(TProduct, TProductTypeData) + + builder.mixin(products.class.Product, core.class.Class, activity.mixin.ActivityDoc, {}) + + builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectIdentifier, { + provider: products.function.ProductIdentifierProvider + }) + + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { + ofClass: products.class.Product, + components: { input: chunter.component.ChatMessageInput } + }) + + builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectEditor, { + editor: products.component.EditProduct + }) + + builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectPanel, { + component: products.component.EditProduct + }) + + builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectPresenter, { + presenter: products.component.ProductPresenter + }) + + builder.mixin(products.class.Product, core.class.Class, view.mixin.SpacePresenter, { + presenter: documents.component.DocumentSpacePresenter + }) + + builder.createDoc( + presentation.class.ObjectSearchCategory, + core.space.Model, + { + title: products.string.Products, + icon: products.icon.Product, + label: products.string.SearchProduct, + query: products.completion.ProductQuery, + context: ['search', 'mention', 'spotlight'], + classToSearch: products.class.Product + }, + products.completion.ProductQueryCategory + ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: products.class.Product, + descriptor: view.viewlet.Table, + config: [ + '', + 'archived', + { + key: 'owners', + presenter: contact.component.AccountArrayEditor, + label: core.string.Owners, + props: { + readonly: true, + size: 'card' + } + }, + { + key: 'members', + presenter: contact.component.AccountArrayEditor, + label: core.string.Members, + props: { + readonly: true, + size: 'card' + } + }, + 'modifiedOn' + ], + configOptions: { + hiddenKeys: ['name'], + sortable: true, + strict: true + } + }, + products.viewlet.TableProduct + ) + + builder.mixin(products.class.Product, core.class.Class, view.mixin.ClassFilters, { + filters: ['archived', 'private', 'createdBy'], + ignoreKeys: ['name', 'type', 'description', 'fullDescription', 'modifiedBy', 'createdOn', 'modifiedOn'], + getVisibleFilters: products.function.GetVisibleFilters + }) + + builder.mixin(products.class.Product, core.class.Class, view.mixin.IgnoreActions, { + actions: [tracker.action.NewRelatedIssue] + }) + + createAction(builder, { + label: products.string.Unarchive, + icon: view.icon.Archive, + action: view.actionImpl.UpdateDocument as ViewAction, + actionProps: { + key: 'archived', + ask: true, + value: false, + label: products.string.Unarchive, + message: products.string.UnarchiveConfirm + }, + input: 'any', + category: view.category.General, + target: products.class.Product, + visibilityTester: view.function.CanArchiveSpace, + query: { + archived: true + }, + context: { + mode: ['context', 'browser'], + group: 'tools' + } + }) +} + +function defineSpaceType (builder: Builder): void { + for (const role of roles) { + const { label, roleType } = getRoleAttributeProps(role.name) + + Prop(roleType, label)(TProductTypeData.prototype, role._id) + } + + builder.createDoc( + documents.class.DocumentSpaceTypeDescriptor, + core.space.Model, + { + name: products.string.ProductsApplication, + description: products.string.ProductsApplication, + icon: products.icon.ProductsApplication, + baseClass: products.class.Product, + availablePermissions: [...productPermissions], + projectClass: products.class.ProductVersion + }, + products.spaceTypeDescriptor.ProductType + ) + + builder.createDoc( + documents.class.DocumentSpaceType, + core.space.Model, + { + name: 'Default Products', + descriptor: products.spaceTypeDescriptor.ProductType, + roles: roles.length, + projects: true, + targetClass: products.mixin.ProductTypeData + }, + products.spaceType.ProductType + ) + + for (const role of roles) { + builder.createDoc( + core.class.Role, + core.space.Model, + { + attachedTo: products.spaceType.ProductType, + attachedToClass: documents.class.DocumentSpaceType, + collection: 'roles', + name: role.name, + permissions: role.permissions + }, + role._id + ) + } +} + +function defineProductVersion (builder: Builder): void { + builder.createModel(TProductVersion) + + builder.mixin(products.class.ProductVersion, core.class.Class, activity.mixin.ActivityDoc, {}) + + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { + ofClass: products.class.ProductVersion, + components: { input: chunter.component.ChatMessageInput } + }) + + builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ObjectEditor, { + editor: products.component.EditProductVersion + }) + + builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ObjectPanel, { + component: products.component.EditProductVersion + }) + + builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ObjectPresenter, { + presenter: products.component.ProductVersionPresenter + }) + + builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.CollectionPresenter, { + presenter: products.component.ProductVersionsPresenter + }) + + builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.CollectionEditor, { + editor: products.component.ProductVersionsEditor + }) + + builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.AttributeEditor, { + inlineEditor: products.component.ProductVersionInlineEditor + }) + + // builder.createDoc( + // presentation.class.ObjectSearchCategory, + // core.space.Model, + // { + // title: products.string.ProductVersions, + // icon: products.icon.ProductVersion, + // label: products.string.SearchProductVersion, + // query: products.completion.ProductVersionQuery, + // context: ['search', 'mention', 'spotlight'], + // classToSearch: products.class.ProductVersion + // }, + // products.completion.ProductVersionQueryCategory + // ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: products.class.ProductVersion, + descriptor: view.viewlet.Table, + config: [ + '', + 'space', + 'state', + '$lookup.parent', + { + key: '$lookup.space.owners', + presenter: contact.component.AccountArrayEditor, + label: core.string.Owners, + props: { + readonly: true, + size: 'card' + } + }, + 'createdBy', + 'createdOn' + ], + configOptions: { + hiddenKeys: ['attachedTo', 'description', 'readonly'], + sortable: true + } + }, + products.viewlet.TableProductVersion + ) + + builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ClassFilters, { + filters: ['space', 'name', 'parent', 'state'], + ignoreKeys: ['createdBy', 'modifiedBy', 'createdOn', 'modifiedOn'] + }) + + builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.IgnoreActions, { + actions: [tracker.action.NewRelatedIssue, documents.action.CreateDocument as Ref] + }) + + createAction( + builder, + { + action: view.actionImpl.Delete, + label: view.string.Delete, + icon: view.icon.Delete, + visibilityTester: products.function.CanDeleteProductVersion, + category: view.category.General, + input: 'any', + target: products.class.ProductVersion, + context: { mode: ['context', 'browser'], group: 'remove' }, + override: [view.action.Delete] + }, + products.action.DeleteProductVersion + ) +} + +function defineProductVersionState (builder: Builder): void { + builder.createModel(TTypeProductVersionState) + + builder.mixin(products.class.TypeProductVersionState, core.class.Class, view.mixin.AttributeEditor, { + inlineEditor: products.component.ProductVersionStateEditor + }) + + builder.mixin(products.class.TypeProductVersionState, core.class.Class, view.mixin.AttributePresenter, { + presenter: products.component.ProductVersionStatePresenter + }) + + builder.mixin(products.class.TypeProductVersionState, core.class.Class, view.mixin.AttributeFilter, { + component: view.component.ValueFilter + }) +} + +function defineApplication (builder: Builder): void { + builder.createDoc( + workbench.class.Application, + core.space.Model, + { + label: products.string.ProductsApplication, + icon: products.icon.ProductsApplication, + alias: productsId, + hidden: false, + navigatorModel: { + spaces: [], + specials: [ + { + id: 'products', + label: products.string.Products, + icon: products.icon.Product, + component: workbench.component.SpecialView, + componentProps: { + _class: products.class.Product, + label: products.string.Products + }, + position: 'top' + }, + { + id: 'product-versions', + label: products.string.ProductVersions, + icon: products.icon.ProductVersion, + component: workbench.component.SpecialView, + componentProps: { + _class: products.class.ProductVersion, + label: products.string.ProductVersions + }, + position: 'top' + } + ] + }, + navHeaderComponent: products.component.NewProductHeader + }, + products.app.Products + ) +} + +export function createModel (builder: Builder): void { + defineSpaceType(builder) + defineProduct(builder) + defineProductVersion(builder) + defineProductVersionState(builder) + defineApplication(builder) +} + +export { productsOperation } from './migration' +export { default } from './plugin' diff --git a/models/products/src/migration.ts b/models/products/src/migration.ts new file mode 100644 index 0000000000..d65f7d096a --- /dev/null +++ b/models/products/src/migration.ts @@ -0,0 +1,84 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { type Employee } from '@hcengineering/contact' +import { type Ref, DOMAIN_TX } from '@hcengineering/core' +import { type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model' +import core, { DOMAIN_SPACE } from '@hcengineering/model-core' +import { type Product } from '@hcengineering/products' + +import products from './plugin' + +async function migrateProductSpacesMixins (client: MigrationClient): Promise { + const oldSpaceTypeMixin = `${products.spaceType.ProductType}:type:mixin` + const newSpaceTypeMixin = products.mixin.ProductTypeData + + await client.update( + DOMAIN_TX, + { + objectClass: core.class.Attribute, + 'attributes.attributeOf': oldSpaceTypeMixin + }, + { + $set: { + 'attributes.attributeOf': newSpaceTypeMixin + } + } + ) + + await client.update( + DOMAIN_SPACE, + { + _class: products.class.Product, + [oldSpaceTypeMixin]: { $exists: true } + }, + { + $rename: { + [oldSpaceTypeMixin]: newSpaceTypeMixin + } + } + ) +} + +async function migrateProductOwner (client: MigrationClient): Promise { + type ExProduct = Product & { owner: Ref } + + const docs = await client.find(DOMAIN_SPACE, { _class: products.class.Product, owner: { $exists: true } }) + for (const doc of docs) { + if (doc.owner == null) continue + const currentOwners = doc.owners ?? [] + const currenMembers = doc.members ?? [] + + const owners = Array.from(new Set(...currentOwners, doc.owner)) + const members = Array.from(new Set(...currenMembers, ...owners)) + + await client.update( + DOMAIN_SPACE, + { _id: doc._id }, + { + $set: { owners, members }, + $unset: { owner: undefined } + } + ) + } +} + +export const productsOperation: MigrateOperation = { + async migrate (client: MigrationClient): Promise { + await migrateProductSpacesMixins(client) + await migrateProductOwner(client) + }, + async upgrade (client: MigrationUpgradeClient): Promise {} +} diff --git a/models/products/src/plugin.ts b/models/products/src/plugin.ts new file mode 100644 index 0000000000..e63b3347b4 --- /dev/null +++ b/models/products/src/plugin.ts @@ -0,0 +1,51 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import '@hcengineering/controlled-documents' +import { productsId } from '@hcengineering/products' +import products from '@hcengineering/products-resources/src/plugin' +import type { Client, Doc, Ref, Role } from '@hcengineering/core' +import { type Resource, mergeIds } from '@hcengineering/platform' +import { type AnyComponent } from '@hcengineering/ui/src/types' +import type { Action } from '@hcengineering/view' + +export default mergeIds(productsId, products, { + action: { + DeleteProductVersion: '' as Ref> + }, + component: { + CreateProduct: '' as AnyComponent, + ProductPresenter: '' as AnyComponent, + NewProductHeader: '' as AnyComponent, + CreateProductVersion: '' as AnyComponent, + ChangeControlInlineEditor: '' as AnyComponent, + ProductVersionPresenter: '' as AnyComponent, + ProductVersionInlineEditor: '' as AnyComponent, + ProductVersionsEditor: '' as AnyComponent, + ProductVersionsPresenter: '' as AnyComponent, + ProductVersionStateEditor: '' as AnyComponent, + ProductVersionStatePresenter: '' as AnyComponent, + ProductVersionVersionPresenter: '' as AnyComponent + }, + function: { + CanDeleteProductVersion: '' as Resource<(doc?: Doc | Doc[]) => Promise>, + ProductIdentifierProvider: '' as Resource<(client: Client, ref: Ref, doc?: T) => Promise> + }, + role: { + QARA: '' as Ref, + Manager: '' as Ref, + QualifiedUser: '' as Ref + } +}) diff --git a/models/products/src/roles.ts b/models/products/src/roles.ts new file mode 100644 index 0000000000..ae0543af26 --- /dev/null +++ b/models/products/src/roles.ts @@ -0,0 +1,56 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import core from '@hcengineering/core' +import documents from '@hcengineering/controlled-documents' +import products from './plugin' + +export const roles = [ + { + _id: products.role.QualifiedUser, + name: 'Qualified User', + permissions: [ + documents.permission.CreateDocument, + documents.permission.ReviewDocument, + documents.permission.ApproveDocument, + documents.permission.CoAuthorDocument + ] + }, + { + _id: products.role.Manager, + name: 'Manager', + permissions: [ + core.permission.ArchiveSpace, + core.permission.UpdateSpace, + documents.permission.CreateDocument, + documents.permission.ReviewDocument, + documents.permission.ApproveDocument, + documents.permission.CoAuthorDocument + ] + }, + { + _id: products.role.QARA, + name: 'QARA', + permissions: [ + core.permission.ArchiveSpace, + core.permission.UpdateSpace, + documents.permission.CreateDocument, + documents.permission.ReviewDocument, + documents.permission.ApproveDocument, + documents.permission.CoAuthorDocument, + documents.permission.UpdateDocumentOwner + ] + } +] diff --git a/models/products/tsconfig.json b/models/products/tsconfig.json new file mode 100644 index 0000000000..9765b6086d --- /dev/null +++ b/models/products/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/models/questions/.eslintrc.js b/models/questions/.eslintrc.js new file mode 100644 index 0000000000..72235dc283 --- /dev/null +++ b/models/questions/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/questions/config/rig.json b/models/questions/config/rig.json new file mode 100644 index 0000000000..0110930f55 --- /dev/null +++ b/models/questions/config/rig.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig" +} diff --git a/models/questions/jest.config.js b/models/questions/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/models/questions/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/models/questions/package.json b/models/questions/package.json new file mode 100644 index 0000000000..4480ea78c1 --- /dev/null +++ b/models/questions/package.json @@ -0,0 +1,45 @@ +{ + "name": "@hcengineering/model-questions", + "version": "0.1.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Hardcore Engineering Inc", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:test": "jest --passWithNoTests --silent", + "_phase:format": "format src", + "test": "jest --passWithNoTests --silent", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.4.0", + "eslint-plugin-promise": "^6.1.1", + "eslint": "^8.54.0", + "prettier": "^3.1.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@hcengineering/questions": "^0.1.0", + "@hcengineering/questions-resources": "^0.1.0", + "@hcengineering/contact": "^0.6.20", + "@hcengineering/core": "^0.6.28", + "@hcengineering/model": "^0.6.7", + "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model-tracker": "^0.6.0", + "@hcengineering/model-view": "^0.6.0", + "@hcengineering/platform": "^0.6.9", + "@hcengineering/view": "^0.6.9" + } +} diff --git a/models/questions/src/doc-types/base.ts b/models/questions/src/doc-types/base.ts new file mode 100644 index 0000000000..071f5f0740 --- /dev/null +++ b/models/questions/src/doc-types/base.ts @@ -0,0 +1,90 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +import type { Answer, Assessment, Percentage, Question, QuestionOption, Rank } from '@hcengineering/questions' +import contact, { type Employee } from '@hcengineering/contact' +import { type Domain, IndexKind, type Ref, type Timestamp, type Type } from '@hcengineering/core' +import { Hidden, Index, Model, Prop, TypeRef, TypeString, TypeTimestamp, UX } from '@hcengineering/model' +import core, { TAttachedDoc, TType } from '@hcengineering/model-core' +import questions from '../plugin' + +export const DOMAIN_QUESTIONS = 'questions' as Domain + +/** @public */ +@Model(questions.type.Rank, core.class.Type) +@UX(questions.string.TypeRank) +export class TTypeRank extends TType {} + +/** @public */ +export function TypeRank (): Type { + return { _class: questions.type.Rank, label: questions.string.TypeRank } +} + +/** @public */ +@Model(questions.type.Percentage, core.class.Type) +@UX(questions.string.TypePercentage) +export class TTypePercentage extends TType {} + +/** @public */ +export function TypePercentage (): Type { + return { _class: questions.type.Percentage, label: questions.string.TypePercentage } +} + +/** @public */ +@Model(questions.type.QuestionOption, core.class.Type) +@UX(questions.string.Option) +export class TTypeQuestionOption extends TType {} + +/** @public */ +export function TypeQuestionOption (): Type { + return { _class: questions.type.QuestionOption, label: questions.string.TypeQuestionOption } +} + +/** @public */ +@Model(questions.class.Question, core.class.AttachedDoc, DOMAIN_QUESTIONS) +export class TQuestion> extends TAttachedDoc implements Question { + @Prop(TypeRank(), questions.string.Rank) + @Index(IndexKind.Indexed) + @Hidden() + rank!: Rank + + @Prop(TypeString(), questions.string.Question) + @Index(IndexKind.FullText) + title: string = '' + + @Prop(TypeRef(contact.mixin.Employee), questions.string.Owner) + @Index(IndexKind.Indexed) + owner!: Ref + + @Prop(TypeTimestamp(), questions.string.ReleasedDate) + @Index(IndexKind.Indexed) + releasedOn: Timestamp | null = null + + @Prop(TypeRef(contact.mixin.Employee), questions.string.ReleasedBy) + releasedBy: Ref | null = null + + // Should be overridden and decorated in child classes + questionData!: QuestionData +} + +/** @public */ +@Model(questions.class.Assessment, questions.class.Question) +export class TAssessment, AssessmentData extends NonNullable> + extends TQuestion + implements Assessment { + // Should be overridden and decorated in child classes + assessmentData!: AssessmentData +} + +/** @public */ +@Model(questions.class.Answer, core.class.AttachedDoc, DOMAIN_QUESTIONS) +export class TAnswer, AnswerData extends NonNullable> + extends TAttachedDoc + implements Answer { + // Should be overridden and decorated in child classes + question!: Ref + + // Should be overridden and decorated in child classes + answerData!: AnswerData +} diff --git a/models/questions/src/doc-types/index.ts b/models/questions/src/doc-types/index.ts new file mode 100644 index 0000000000..75dd1160df --- /dev/null +++ b/models/questions/src/doc-types/index.ts @@ -0,0 +1,9 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +export * from './base' +export * from './mixin' +export * from './questions/MultipleChoice' +export * from './questions/Ordering' +export * from './questions/SingleChoice' diff --git a/models/questions/src/doc-types/mixin.ts b/models/questions/src/doc-types/mixin.ts new file mode 100644 index 0000000000..2be7aa219e --- /dev/null +++ b/models/questions/src/doc-types/mixin.ts @@ -0,0 +1,35 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +import type { + Answer, + AnswerDataAssessFunction, + Assessment, + Question, + QuestionInitFunction, + QuestionMixin, + QuestionDataPresenter, + QuestionDataEditor, + AnswerDataPresenter, + AnswerDataEditor +} from '@hcengineering/questions' +import type { Class, Ref } from '@hcengineering/core' +import { Mixin } from '@hcengineering/model' +import core, { TClass } from '@hcengineering/model-core' +import type { Resource } from '@hcengineering/platform' +import questions from '../plugin' + +/** @public */ +@Mixin(questions.mixin.QuestionMixin, core.class.Class) +export class TQuestionMixin, A extends Answer> + extends TClass + implements QuestionMixin { + questionInit!: Resource> + questionDataPresenter!: Resource> + questionDataEditor!: Resource> + answerClassRef!: Ref> + answerDataPresenter!: Resource> + answerDataEditor!: Resource> + answerDataAssess!: Q extends Assessment ? Resource> : null +} diff --git a/models/questions/src/doc-types/questions/MultipleChoice.ts b/models/questions/src/doc-types/questions/MultipleChoice.ts new file mode 100644 index 0000000000..7073326d44 --- /dev/null +++ b/models/questions/src/doc-types/questions/MultipleChoice.ts @@ -0,0 +1,105 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +import type { + MultipleChoiceAnswerData, + MultipleChoiceAssessment, + MultipleChoiceAssessmentAnswer, + MultipleChoiceAssessmentData, + MultipleChoiceQuestion, + MultipleChoiceQuestionAnswer, + MultipleChoiceQuestionData +} from '@hcengineering/questions' +import { IndexKind, type Ref, type Type } from '@hcengineering/core' +import { Hidden, Index, Model, Prop, TypeRef, UX } from '@hcengineering/model' +import core, { TType } from '@hcengineering/model-core' +import questions from '../../plugin' +import { TAnswer, TAssessment, TQuestion } from '../base' + +/** @public */ +@Model(questions.class.MultipleChoiceQuestionData, core.class.Type) +@UX(questions.string.Question) +export class TMultipleChoiceQuestionData extends TType {} + +/** @public */ +export function TypeMultipleChoiceQuestionData (): Type { + return { + _class: questions.class.MultipleChoiceQuestionData, + label: questions.string.MultipleChoiceQuestion + } +} + +/** @public */ +@Model(questions.class.MultipleChoiceAssessmentData, core.class.Type) +@UX(questions.string.Assessment) +export class TMultipleChoiceAssessmentData extends TType {} + +/** @public */ +export function TypeMultipleChoiceAssessmentData (): Type { + return { + _class: questions.class.MultipleChoiceAssessmentData, + label: questions.string.MultipleChoiceAssessment + } +} + +/** @public */ +@Model(questions.class.MultipleChoiceAnswerData, core.class.Type) +@UX(questions.string.Answer) +export class TMultipleChoiceAnswerData extends TType {} + +/** @public */ +export function TypeMultipleChoiceAnswerData (): Type { + return { + _class: questions.class.MultipleChoiceAnswerData, + label: questions.string.MultipleChoice + } +} + +/** @public */ +@Model(questions.class.MultipleChoiceQuestion, questions.class.Question) +@UX(questions.string.MultipleChoiceQuestion) +export class TMultipleChoiceQuestion extends TQuestion implements MultipleChoiceQuestion { + @Prop(TypeMultipleChoiceQuestionData(), questions.string.Question) + declare questionData: MultipleChoiceQuestionData +} + +/** @public */ +@Model(questions.class.MultipleChoiceQuestionAnswer, questions.class.Answer) +@UX(questions.string.MultipleChoice) +export class TMultipleChoiceQuestionAnswer + extends TAnswer + implements MultipleChoiceQuestionAnswer { + @Prop(TypeRef(questions.class.MultipleChoiceQuestion), questions.string.Question) + @Index(IndexKind.Indexed) + @Hidden() + declare question: Ref + + @Prop(TypeMultipleChoiceAnswerData(), questions.string.Answer) + declare answerData: MultipleChoiceAnswerData +} + +/** @public */ +@Model(questions.class.MultipleChoiceAssessment, questions.class.Assessment) +@UX(questions.string.MultipleChoiceAssessment) +export class TMultipleChoiceAssessment + extends TAssessment + implements MultipleChoiceAssessment { + @Prop(TypeMultipleChoiceAssessmentData(), questions.string.Assessment) + declare assessmentData: MultipleChoiceAssessmentData +} + +/** @public */ +@Model(questions.class.MultipleChoiceAssessmentAnswer, questions.class.Answer) +@UX(questions.string.MultipleChoice) +export class TMultipleChoiceAssessmentAnswer + extends TAnswer + implements MultipleChoiceAssessmentAnswer { + @Prop(TypeRef(questions.class.MultipleChoiceAssessment), questions.string.Question) + @Index(IndexKind.Indexed) + @Hidden() + declare question: Ref + + @Prop(TypeMultipleChoiceAnswerData(), questions.string.Answer) + declare answerData: MultipleChoiceAnswerData +} diff --git a/models/questions/src/doc-types/questions/Ordering.ts b/models/questions/src/doc-types/questions/Ordering.ts new file mode 100644 index 0000000000..633a7c93dc --- /dev/null +++ b/models/questions/src/doc-types/questions/Ordering.ts @@ -0,0 +1,105 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +import type { + OrderingAnswerData, + OrderingAssessment, + OrderingAssessmentAnswer, + OrderingAssessmentData, + OrderingQuestion, + OrderingQuestionAnswer, + OrderingQuestionData +} from '@hcengineering/questions' +import { IndexKind, type Ref, type Type } from '@hcengineering/core' +import { Hidden, Index, Model, Prop, TypeRef, UX } from '@hcengineering/model' +import core, { TType } from '@hcengineering/model-core' +import questions from '../../plugin' +import { TAnswer, TAssessment, TQuestion } from '../base' + +/** @public */ +@Model(questions.class.OrderingQuestionData, core.class.Type) +@UX(questions.string.Question) +export class TOrderingQuestionData extends TType {} + +/** @public */ +export function TypeOrderingQuestionData (): Type { + return { + _class: questions.class.OrderingQuestionData, + label: questions.string.OrderingQuestion + } +} + +/** @public */ +@Model(questions.class.OrderingAssessmentData, core.class.Type) +@UX(questions.string.Assessment) +export class TOrderingAssessmentData extends TType {} + +/** @public */ +export function TypeOrderingAssessmentData (): Type { + return { + _class: questions.class.OrderingAssessmentData, + label: questions.string.OrderingAssessment + } +} + +/** @public */ +@Model(questions.class.OrderingAnswerData, core.class.Type) +@UX(questions.string.Answer) +export class TOrderingAnswerData extends TType {} + +/** @public */ +export function TypeOrderingAnswerData (): Type { + return { + _class: questions.class.OrderingAnswerData, + label: questions.string.Ordering + } +} + +/** @public */ +@Model(questions.class.OrderingQuestion, questions.class.Question) +@UX(questions.string.OrderingQuestion) +export class TOrderingQuestion extends TQuestion implements OrderingQuestion { + @Prop(TypeOrderingQuestionData(), questions.string.Question) + declare questionData: OrderingQuestionData +} + +/** @public */ +@Model(questions.class.OrderingQuestionAnswer, questions.class.Answer) +@UX(questions.string.Ordering) +export class TOrderingQuestionAnswer + extends TAnswer + implements OrderingQuestionAnswer { + @Prop(TypeRef(questions.class.OrderingQuestion), questions.string.Question) + @Index(IndexKind.Indexed) + @Hidden() + declare question: Ref + + @Prop(TypeOrderingAnswerData(), questions.string.Answer) + declare answerData: OrderingAnswerData +} + +/** @public */ +@Model(questions.class.OrderingAssessment, questions.class.Assessment) +@UX(questions.string.OrderingAssessment) +export class TOrderingAssessment + extends TAssessment + implements OrderingAssessment { + @Prop(TypeOrderingAssessmentData(), questions.string.Assessment) + declare assessmentData: OrderingAssessmentData +} + +/** @public */ +@Model(questions.class.OrderingAssessmentAnswer, questions.class.Answer) +@UX(questions.string.Ordering) +export class TOrderingAssessmentAnswer + extends TAnswer + implements OrderingAssessmentAnswer { + @Prop(TypeRef(questions.class.OrderingAssessment), questions.string.Question) + @Index(IndexKind.Indexed) + @Hidden() + declare question: Ref + + @Prop(TypeOrderingAnswerData(), questions.string.Answer) + declare answerData: OrderingAnswerData +} diff --git a/models/questions/src/doc-types/questions/SingleChoice.ts b/models/questions/src/doc-types/questions/SingleChoice.ts new file mode 100644 index 0000000000..01bff3f697 --- /dev/null +++ b/models/questions/src/doc-types/questions/SingleChoice.ts @@ -0,0 +1,105 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +import type { + SingleChoiceAnswerData, + SingleChoiceAssessment, + SingleChoiceAssessmentAnswer, + SingleChoiceAssessmentData, + SingleChoiceQuestion, + SingleChoiceQuestionAnswer, + SingleChoiceQuestionData +} from '@hcengineering/questions' +import { IndexKind, type Ref, type Type } from '@hcengineering/core' +import { Hidden, Index, Model, Prop, TypeRef, UX } from '@hcengineering/model' +import core, { TType } from '@hcengineering/model-core' +import questions from '../../plugin' +import { TAnswer, TAssessment, TQuestion } from '../base' + +/** @public */ +@Model(questions.class.SingleChoiceQuestionData, core.class.Type) +@UX(questions.string.Question) +export class TSingleChoiceQuestionData extends TType {} + +/** @public */ +export function TypeSingleChoiceQuestionData (): Type { + return { + _class: questions.class.SingleChoiceQuestionData, + label: questions.string.SingleChoiceQuestion + } +} + +/** @public */ +@Model(questions.class.SingleChoiceAssessmentData, core.class.Type) +@UX(questions.string.Assessment) +export class TSingleChoiceAssessmentData extends TType {} + +/** @public */ +export function TypeSingleChoiceAssessmentData (): Type { + return { + _class: questions.class.SingleChoiceAssessmentData, + label: questions.string.SingleChoiceAssessment + } +} + +/** @public */ +@Model(questions.class.SingleChoiceAnswerData, core.class.Type) +@UX(questions.string.Answer) +export class TSingleChoiceAnswerData extends TType {} + +/** @public */ +export function TypeSingleChoiceAnswerData (): Type { + return { + _class: questions.class.SingleChoiceAnswerData, + label: questions.string.SingleChoice + } +} + +/** @public */ +@Model(questions.class.SingleChoiceQuestion, questions.class.Question) +@UX(questions.string.SingleChoiceQuestion) +export class TSingleChoiceQuestion extends TQuestion implements SingleChoiceQuestion { + @Prop(TypeSingleChoiceQuestionData(), questions.string.Question) + declare questionData: SingleChoiceQuestionData +} + +/** @public */ +@Model(questions.class.SingleChoiceQuestionAnswer, questions.class.Answer) +@UX(questions.string.SingleChoice) +export class TSingleChoiceQuestionAnswer + extends TAnswer + implements SingleChoiceQuestionAnswer { + @Prop(TypeRef(questions.class.SingleChoiceQuestion), questions.string.Question) + @Index(IndexKind.Indexed) + @Hidden() + declare question: Ref + + @Prop(TypeSingleChoiceAnswerData(), questions.string.Answer) + declare answerData: SingleChoiceAnswerData +} + +/** @public */ +@Model(questions.class.SingleChoiceAssessment, questions.class.Assessment) +@UX(questions.string.SingleChoiceAssessment) +export class TSingleChoiceAssessment + extends TAssessment + implements SingleChoiceAssessment { + @Prop(TypeSingleChoiceAssessmentData(), questions.string.Assessment) + declare assessmentData: SingleChoiceAssessmentData +} + +/** @public */ +@Model(questions.class.SingleChoiceAssessmentAnswer, questions.class.Answer) +@UX(questions.string.SingleChoice) +export class TSingleChoiceAssessmentAnswer + extends TAnswer + implements SingleChoiceAssessmentAnswer { + @Prop(TypeRef(questions.class.SingleChoiceAssessment), questions.string.Question) + @Index(IndexKind.Indexed) + @Hidden() + declare question: Ref + + @Prop(TypeSingleChoiceAnswerData(), questions.string.Answer) + declare answerData: SingleChoiceAnswerData +} diff --git a/models/questions/src/index.ts b/models/questions/src/index.ts new file mode 100644 index 0000000000..68e5432237 --- /dev/null +++ b/models/questions/src/index.ts @@ -0,0 +1,296 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +import type { + Answer, + MultipleChoiceAssessment, + MultipleChoiceAssessmentAnswer, + MultipleChoiceQuestion, + MultipleChoiceQuestionAnswer, + OrderingAssessment, + OrderingAssessmentAnswer, + OrderingQuestion, + OrderingQuestionAnswer, + Question, + QuestionMixin, + SingleChoiceAssessment, + SingleChoiceAssessmentAnswer, + SingleChoiceQuestion, + SingleChoiceQuestionAnswer +} from '@hcengineering/questions' +import { type Class, type MixinData, type Ref } from '@hcengineering/core' +import { type Builder } from '@hcengineering/model' +import core from '@hcengineering/model-core' +import tracker from '@hcengineering/model-tracker' +import view, { createAction } from '@hcengineering/model-view' +import { + TAnswer, + TAssessment, + TMultipleChoiceAnswerData, + TMultipleChoiceAssessment, + TMultipleChoiceAssessmentAnswer, + TMultipleChoiceAssessmentData, + TMultipleChoiceQuestion, + TMultipleChoiceQuestionAnswer, + TMultipleChoiceQuestionData, + TOrderingAnswerData, + TOrderingAssessment, + TOrderingAssessmentAnswer, + TOrderingAssessmentData, + TOrderingQuestion, + TOrderingQuestionAnswer, + TOrderingQuestionData, + TQuestion, + TQuestionMixin, + TSingleChoiceAnswerData, + TSingleChoiceAssessment, + TSingleChoiceAssessmentAnswer, + TSingleChoiceAssessmentData, + TSingleChoiceQuestion, + TSingleChoiceQuestionAnswer, + TSingleChoiceQuestionData, + TTypePercentage, + TTypeQuestionOption, + TTypeRank +} from './doc-types' +import questions from './plugin' + +export { questionsOperation } from './migration' +export { default } from './plugin' +export { questionsId } from '@hcengineering/questions/src/index' +export * from './doc-types' + +export function createModel (builder: Builder): void { + builder.createModel(TTypeRank) + + builder.createModel(TTypePercentage) + builder.mixin(questions.type.Percentage, core.class.Class, view.mixin.AttributeEditor, { + inlineEditor: view.component.NumberEditor + }) + + builder.createModel(TTypeQuestionOption) + builder.createModel(TQuestion, TAssessment, TAnswer) + builder.createModel(TQuestionMixin) + + defineActions(builder) + defineSingleChoice(builder) + defineMultipleChoice(builder) + defineOrdering(builder) +} + +function defineSingleChoice (builder: Builder): void { + builder.createModel(TSingleChoiceQuestionData, TSingleChoiceAnswerData) + + defineQuestion( + builder, + questions.class.SingleChoiceQuestion, + TSingleChoiceQuestion, + questions.class.SingleChoiceQuestionAnswer, + TSingleChoiceQuestionAnswer, + { + answerClassRef: questions.class.SingleChoiceQuestionAnswer, + answerDataAssess: null, + answerDataEditor: questions.component.SingleChoiceQuestionAnswerDataEditor, + answerDataPresenter: questions.component.SingleChoiceQuestionAnswerDataPresenter, + questionInit: questions.function.SingleChoiceQuestionInit, + questionDataEditor: questions.component.SingleChoiceQuestionDataEditor, + questionDataPresenter: questions.component.SingleChoiceQuestionDataPresenter + } + ) + + builder.createModel(TSingleChoiceAssessmentData) + + defineQuestion( + builder, + questions.class.SingleChoiceAssessment, + TSingleChoiceAssessment, + questions.class.SingleChoiceAssessmentAnswer, + TSingleChoiceAssessmentAnswer, + { + answerClassRef: questions.class.SingleChoiceAssessmentAnswer, + answerDataAssess: questions.function.SingleChoiceAssessmentAssess, + answerDataEditor: questions.component.SingleChoiceAssessmentAnswerDataEditor, + answerDataPresenter: questions.component.SingleChoiceAssessmentAnswerDataPresenter, + questionInit: questions.function.SingleChoiceAssessmentInit, + questionDataEditor: questions.component.SingleChoiceAssessmentDataEditor, + questionDataPresenter: questions.component.SingleChoiceAssessmentDataPresenter + } + ) +} + +function defineMultipleChoice (builder: Builder): void { + builder.createModel(TMultipleChoiceQuestionData, TMultipleChoiceAnswerData) + + defineQuestion( + builder, + questions.class.MultipleChoiceQuestion, + TMultipleChoiceQuestion, + questions.class.MultipleChoiceQuestionAnswer, + TMultipleChoiceQuestionAnswer, + { + answerClassRef: questions.class.MultipleChoiceQuestionAnswer, + answerDataAssess: null, + answerDataEditor: questions.component.MultipleChoiceQuestionAnswerDataEditor, + answerDataPresenter: questions.component.MultipleChoiceQuestionAnswerDataPresenter, + questionInit: questions.function.MultipleChoiceQuestionInit, + questionDataEditor: questions.component.MultipleChoiceQuestionDataEditor, + questionDataPresenter: questions.component.MultipleChoiceQuestionDataPresenter + } + ) + + builder.createModel(TMultipleChoiceAssessmentData) + + defineQuestion( + builder, + questions.class.MultipleChoiceAssessment, + TMultipleChoiceAssessment, + questions.class.MultipleChoiceAssessmentAnswer, + TMultipleChoiceAssessmentAnswer, + { + answerClassRef: questions.class.MultipleChoiceAssessmentAnswer, + answerDataAssess: questions.function.MultipleChoiceAssessmentAssess, + answerDataEditor: questions.component.MultipleChoiceAssessmentAnswerDataEditor, + answerDataPresenter: questions.component.MultipleChoiceAssessmentAnswerDataPresenter, + questionInit: questions.function.MultipleChoiceAssessmentInit, + questionDataEditor: questions.component.MultipleChoiceAssessmentDataEditor, + questionDataPresenter: questions.component.MultipleChoiceAssessmentDataPresenter + } + ) +} + +function defineOrdering (builder: Builder): void { + builder.createModel(TOrderingQuestionData, TOrderingAnswerData) + + defineQuestion( + builder, + questions.class.OrderingQuestion, + TOrderingQuestion, + questions.class.OrderingQuestionAnswer, + TOrderingQuestionAnswer, + { + answerClassRef: questions.class.OrderingQuestionAnswer, + answerDataAssess: null, + answerDataEditor: questions.component.OrderingQuestionAnswerDataEditor, + answerDataPresenter: questions.component.OrderingQuestionAnswerDataPresenter, + questionInit: questions.function.OrderingQuestionInit, + questionDataEditor: questions.component.OrderingQuestionDataEditor, + questionDataPresenter: questions.component.OrderingQuestionDataPresenter + } + ) + + builder.createModel(TOrderingAssessmentData) + + defineQuestion( + builder, + questions.class.OrderingAssessment, + TOrderingAssessment, + questions.class.OrderingAssessmentAnswer, + TOrderingAssessmentAnswer, + { + answerClassRef: questions.class.OrderingAssessmentAnswer, + answerDataAssess: questions.function.OrderingAssessmentAssess, + answerDataEditor: questions.component.OrderingAssessmentAnswerDataEditor, + answerDataPresenter: questions.component.OrderingAssessmentAnswerDataPresenter, + questionInit: questions.function.OrderingAssessmentInit, + questionDataEditor: questions.component.OrderingAssessmentDataEditor, + questionDataPresenter: questions.component.OrderingAssessmentDataPresenter + } + ) +} + +function defineActions (builder: Builder): void { + builder.createDoc( + view.class.ActionCategory, + core.space.Model, + { + label: questions.string.Questions, + visible: true + }, + questions.actionCategory.Questions + ) + + builder.mixin(questions.class.Question, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Open, view.action.OpenInNewTab, view.action.Delete, tracker.action.NewRelatedIssue] + }) + + createAction( + builder, + { + action: questions.action.QuestionMoveUpAction, + visibilityTester: questions.action.QuestionMoveUpIsAvailable, + input: 'focus', + label: view.string.MoveUp, + icon: questions.icon.ArrowUp, + target: questions.class.Question, + context: { mode: ['context', 'workbench'], group: 'edit' }, + category: questions.actionCategory.Questions + }, + questions.action.QuestionMoveUp + ) + + createAction( + builder, + { + action: questions.action.QuestionMoveDownAction, + visibilityTester: questions.action.QuestionMoveDownIsAvailable, + input: 'focus', + label: view.string.MoveDown, + icon: questions.icon.ArrowDown, + target: questions.class.Question, + context: { mode: ['context', 'workbench'], group: 'edit' }, + category: questions.actionCategory.Questions + }, + questions.action.QuestionMoveDown + ) + + createAction( + builder, + { + action: questions.action.QuestionDuplicateAction, + visibilityTester: questions.action.QuestionDuplicateIsAvailable, + input: 'focus', + label: questions.string.Duplicate, + icon: questions.icon.Duplicate, + target: questions.class.Question, + context: { mode: ['context', 'workbench'], group: 'copy' }, + category: questions.actionCategory.Questions + }, + questions.action.QuestionDuplicate + ) + + createAction( + builder, + { + override: [view.action.Delete], + action: questions.action.QuestionDeleteAction, + visibilityTester: questions.action.QuestionDeleteIsAvailable, + input: 'any', + label: view.string.Delete, + icon: view.icon.Delete, + target: questions.class.Question, + context: { mode: ['context', 'workbench'], group: 'remove' }, + category: questions.actionCategory.Questions + }, + questions.action.QuestionDelete + ) + + builder.mixin(questions.class.Answer, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Open, view.action.OpenInNewTab, view.action.Delete, tracker.action.NewRelatedIssue] + }) +} + +function defineQuestion, A extends Answer> ( + builder: Builder, + questionClassRef: Ref>, + questionModelClass: new () => Q, + answerClassRef: Ref>, + answerModelClass: new () => A, + attributes: MixinData, QuestionMixin> +): void { + builder.createModel(questionModelClass, answerModelClass) + builder.mixin, QuestionMixin>(questionClassRef, core.class.Class, questions.mixin.QuestionMixin, { + ...attributes, + answerClassRef + }) +} diff --git a/models/questions/src/migration.ts b/models/questions/src/migration.ts new file mode 100644 index 0000000000..97db1ae447 --- /dev/null +++ b/models/questions/src/migration.ts @@ -0,0 +1,11 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +import { type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model' + +export const questionsOperation: MigrateOperation = { + async migrate (client: MigrationClient): Promise {}, + + async upgrade (client: MigrationUpgradeClient): Promise {} +} diff --git a/models/questions/src/plugin.ts b/models/questions/src/plugin.ts new file mode 100644 index 0000000000..4c5a2103ee --- /dev/null +++ b/models/questions/src/plugin.ts @@ -0,0 +1,15 @@ +// +// Copyright @ 2024 Hardcore Engineering Inc. +// + +import { questionsId } from '@hcengineering/questions' +import questions from '@hcengineering/questions-resources/src/plugin' +import type { Ref } from '@hcengineering/core' +import { mergeIds } from '@hcengineering/platform' +import type { ActionCategory } from '@hcengineering/view' + +export default mergeIds(questionsId, questions, { + actionCategory: { + Questions: '' as Ref + } +}) diff --git a/models/questions/tsconfig.json b/models/questions/tsconfig.json new file mode 100644 index 0000000000..59e4fd4297 --- /dev/null +++ b/models/questions/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/models/server-controlled-documents/.eslintrc.js b/models/server-controlled-documents/.eslintrc.js new file mode 100644 index 0000000000..c1cf82cba0 --- /dev/null +++ b/models/server-controlled-documents/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/server-controlled-documents/.npmignore b/models/server-controlled-documents/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/server-controlled-documents/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/server-controlled-documents/config/rig.json b/models/server-controlled-documents/config/rig.json new file mode 100644 index 0000000000..2f6be36605 --- /dev/null +++ b/models/server-controlled-documents/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "model" +} diff --git a/models/server-controlled-documents/package.json b/models/server-controlled-documents/package.json new file mode 100644 index 0000000000..e945bed54e --- /dev/null +++ b/models/server-controlled-documents/package.json @@ -0,0 +1,42 @@ +{ + "name": "@hcengineering/model-server-controlled-documents", + "version": "0.1.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Hardcore Engineering Inc.", + "template": "@hcengineering/model-package", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.8.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/core": "^0.6.28", + "@hcengineering/model": "^0.6.7", + "@hcengineering/platform": "^0.6.9", + "@hcengineering/server-core": "^0.6.1", + "@hcengineering/model-core": "^0.6.0", + "@hcengineering/request": "^0.6.6", + "@hcengineering/contact": "^0.6.20", + "@hcengineering/controlled-documents": "^0.1.0", + "@hcengineering/server-controlled-documents": "^0.1.0", + "@hcengineering/server-notification": "^0.6.1" + } +} diff --git a/models/server-controlled-documents/src/index.ts b/models/server-controlled-documents/src/index.ts new file mode 100644 index 0000000000..63ff65431b --- /dev/null +++ b/models/server-controlled-documents/src/index.ts @@ -0,0 +1,73 @@ +// +// Copyright © 2023 Hardcore Engineering Inc. +// +import { type Builder } from '@hcengineering/model' +import core from '@hcengineering/core' +import serverCore from '@hcengineering/server-core' +import { RequestStatus } from '@hcengineering/request' +import documents, { DocumentState } from '@hcengineering/controlled-documents' +import serverDocuments from '@hcengineering/server-controlled-documents' +import contact from '@hcengineering/contact' +import serverNotification from '@hcengineering/server-notification' + +export { serverDocumentsId } from '@hcengineering/server-controlled-documents/src/index' + +export function createModel (builder: Builder): void { + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverDocuments.trigger.OnCollaborativeSectionDeleted, + txMatch: { + _class: core.class.TxCollectionCUD, + 'tx.objectClass': documents.class.CollaborativeDocumentSection, + 'tx._class': core.class.TxRemoveDoc + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverDocuments.trigger.OnDocPlannedEffectiveDateChanged, + txMatch: { + _class: core.class.TxUpdateDoc, + objectClass: documents.class.ControlledDocument + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverDocuments.trigger.OnDocApprovalRequestApproved, + txMatch: { + _class: core.class.TxCollectionCUD, + objectClass: documents.class.ControlledDocument, + 'tx._class': core.class.TxUpdateDoc, + 'tx.objectClass': documents.class.DocumentApprovalRequest, + 'tx.operations.status': RequestStatus.Completed + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverDocuments.trigger.OnDocHasBecomeEffective, + txMatch: { + _class: core.class.TxUpdateDoc, + objectClass: documents.class.ControlledDocument, + 'operations.state': DocumentState.Effective + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverDocuments.trigger.OnWorkspaceOwnerAdded, + txMatch: { + objectClass: contact.class.PersonAccount + } + }) + + builder.mixin(documents.class.DocumentMeta, core.class.Class, serverCore.mixin.SearchPresenter, { + searchConfig: { + iconConfig: { + component: documents.component.DocumentIcon, + props: [] + }, + title: 'title' + } + }) + + builder.mixin(documents.class.ControlledDocument, core.class.Class, serverNotification.mixin.TextPresenter, { + presenter: serverDocuments.function.ControlledDocumentTextPresenter + }) +} diff --git a/models/server-controlled-documents/tsconfig.json b/models/server-controlled-documents/tsconfig.json new file mode 100644 index 0000000000..9765b6086d --- /dev/null +++ b/models/server-controlled-documents/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/models/server-products/.eslintrc.js b/models/server-products/.eslintrc.js new file mode 100644 index 0000000000..c1cf82cba0 --- /dev/null +++ b/models/server-products/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/server-products/.npmignore b/models/server-products/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/server-products/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/server-products/config/rig.json b/models/server-products/config/rig.json new file mode 100644 index 0000000000..2f6be36605 --- /dev/null +++ b/models/server-products/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "model" +} diff --git a/models/server-products/package.json b/models/server-products/package.json new file mode 100644 index 0000000000..06ffe8f59d --- /dev/null +++ b/models/server-products/package.json @@ -0,0 +1,38 @@ +{ + "name": "@hcengineering/model-server-products", + "version": "0.1.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Hardcore Engineering Inc.", + "template": "@hcengineering/model-package", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.8.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/core": "^0.6.28", + "@hcengineering/model": "^0.6.7", + "@hcengineering/platform": "^0.6.9", + "@hcengineering/server-core": "^0.6.1", + "@hcengineering/model-core": "^0.6.0", + "@hcengineering/products": "~0.1.0" + } +} diff --git a/models/server-products/src/index.ts b/models/server-products/src/index.ts new file mode 100644 index 0000000000..52d8a30d99 --- /dev/null +++ b/models/server-products/src/index.ts @@ -0,0 +1,25 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// + +import type { Plugin } from '@hcengineering/platform' +import products from '@hcengineering/products' + +import core from '@hcengineering/core' +import { type Builder } from '@hcengineering/model' +import serverCore from '@hcengineering/server-core' + +export const serverProductsId = 'server-products' as Plugin + +export function createModel (builder: Builder): void { + builder.mixin(products.class.Product, core.class.Class, serverCore.mixin.SearchPresenter, { + searchConfig: { + iconConfig: { + component: products.component.ProductSearchIcon, + props: ['icon', 'color'] + }, + title: 'name' + } + }) +} diff --git a/models/server-products/tsconfig.json b/models/server-products/tsconfig.json new file mode 100644 index 0000000000..9765b6086d --- /dev/null +++ b/models/server-products/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/models/server-training/.eslintrc.js b/models/server-training/.eslintrc.js new file mode 100644 index 0000000000..c1cf82cba0 --- /dev/null +++ b/models/server-training/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/server-training/.npmignore b/models/server-training/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/server-training/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/server-training/config/rig.json b/models/server-training/config/rig.json new file mode 100644 index 0000000000..2f6be36605 --- /dev/null +++ b/models/server-training/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "model" +} diff --git a/models/server-training/package.json b/models/server-training/package.json new file mode 100644 index 0000000000..2916615e0a --- /dev/null +++ b/models/server-training/package.json @@ -0,0 +1,40 @@ +{ + "name": "@hcengineering/model-server-training", + "version": "0.1.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Hardcore Engineering Inc", + "template": "@hcengineering/model-package", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.8.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/core": "^0.6.28", + "@hcengineering/model": "^0.6.7", + "@hcengineering/notification": "^0.6.16", + "@hcengineering/platform": "^0.6.9", + "@hcengineering/server-core": "^0.6.1", + "@hcengineering/server-notification": "^0.6.1", + "@hcengineering/model-training": "^0.1.0", + "@hcengineering/server-training": "^0.1.0" + } +} diff --git a/models/server-training/src/index.ts b/models/server-training/src/index.ts new file mode 100644 index 0000000000..353cff6404 --- /dev/null +++ b/models/server-training/src/index.ts @@ -0,0 +1,31 @@ +// +// Copyright @ 2022 Hardcore Engineering Inc. +// +import { type Builder } from '@hcengineering/model' + +import training from '@hcengineering/model-training' +import serverTraining from '@hcengineering/server-training' +import core from '@hcengineering/core' +import notification from '@hcengineering/notification' +import serverNotification from '@hcengineering/server-notification' + +export { serverTrainingId } from '@hcengineering/server-training/src/index' + +export function createModel (builder: Builder): void { + builder.mixin( + training.notification.TrainingRequest, + notification.class.NotificationType, + serverNotification.mixin.TypeMatch, + { + func: serverTraining.function.TrainingRequestNotificationTypeMatch + } + ) + + builder.mixin(training.class.TrainingRequest, core.class.Class, serverNotification.mixin.TextPresenter, { + presenter: serverTraining.function.TrainingRequestTextPresenter + }) + + builder.mixin(training.class.TrainingRequest, core.class.Class, serverNotification.mixin.HTMLPresenter, { + presenter: serverTraining.function.TrainingRequestHTMLPresenter + }) +} diff --git a/models/server-training/tsconfig.json b/models/server-training/tsconfig.json new file mode 100644 index 0000000000..9765b6086d --- /dev/null +++ b/models/server-training/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/models/training/.eslintrc.js b/models/training/.eslintrc.js new file mode 100644 index 0000000000..72235dc283 --- /dev/null +++ b/models/training/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/training/config/rig.json b/models/training/config/rig.json new file mode 100644 index 0000000000..0110930f55 --- /dev/null +++ b/models/training/config/rig.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig" +} diff --git a/models/training/jest.config.js b/models/training/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/models/training/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/models/training/package.json b/models/training/package.json new file mode 100644 index 0000000000..5ffbe01d94 --- /dev/null +++ b/models/training/package.json @@ -0,0 +1,58 @@ +{ + "name": "@hcengineering/model-training", + "version": "0.1.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Hardcore Engineering Inc", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:test": "jest --passWithNoTests --silent", + "_phase:format": "format src", + "test": "jest --passWithNoTests --silent", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.4.0", + "eslint-plugin-promise": "^6.1.1", + "eslint": "^8.54.0", + "prettier": "^3.1.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@hcengineering/model-questions": "^0.1.0", + "@hcengineering/questions": "^0.1.0", + "@hcengineering/training": "^0.1.0", + "@hcengineering/training-resources": "^0.1.0", + "@hcengineering/activity": "^0.6.0", + "@hcengineering/attachment": "^0.6.9", + "@hcengineering/contact": "^0.6.20", + "@hcengineering/core": "^0.6.28", + "@hcengineering/model": "^0.6.7", + "@hcengineering/model-attachment": "^0.6.0", + "@hcengineering/model-contact": "^0.6.1", + "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model-notification": "^0.6.0", + "@hcengineering/model-tracker": "^0.6.0", + "@hcengineering/model-view": "^0.6.0", + "@hcengineering/model-workbench": "^0.6.1", + "@hcengineering/notification": "^0.6.16", + "@hcengineering/platform": "^0.6.9", + "@hcengineering/setting": "^0.6.11", + "@hcengineering/ui": "^0.6.11", + "@hcengineering/view": "^0.6.9", + "@hcengineering/workbench": "^0.6.9", + "@hcengineering/model-print": "^0.6.0" + } +} diff --git a/models/training/src/index.ts b/models/training/src/index.ts new file mode 100644 index 0000000000..e714a7efe6 --- /dev/null +++ b/models/training/src/index.ts @@ -0,0 +1,897 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import activity from '@hcengineering/activity' +import notification, { type NotificationType } from '@hcengineering/notification' +import { type Asset, type IntlString } from '@hcengineering/platform' +import type { BuildModelKey, Viewlet, ViewletDescriptor } from '@hcengineering/view' +import questions from '@hcengineering/model-questions' +import contact from '@hcengineering/contact' +import tracker from '@hcengineering/model-tracker' +import attachment from '@hcengineering/model-attachment' +import print from '@hcengineering/model-print' +import setting, { getRoleAttributeProps } from '@hcengineering/setting' +import { + type Training, + type TrainingAttempt, + trainingId, + type TrainingRequest, + TrainingSpecialIds +} from '@hcengineering/training' + +import { AccountRole, type Data, type FindOptions, type Permission, type Ref } from '@hcengineering/core' +import { Prop, type Builder } from '@hcengineering/model' + +import contacts from '@hcengineering/model-contact' +import core from '@hcengineering/model-core' +import view, { classPresenter, createAction } from '@hcengineering/model-view' +import workbench from '@hcengineering/model-workbench' +import training from './plugin' +import { + TSequence, + TTraining, + TTrainingAttempt, + TTrainingAttemptState, + TTrainingRequest, + TTrainingState, + TTrainingsTypeData +} from './types' +import { roles } from './roles' +export { trainingOperation } from './migration' +export { trainingId } from '@hcengineering/training/src/index' +export { default } from './plugin' + +export function createModel (builder: Builder): void { + defineBase(builder) + defineSpaceType(builder) + defineTraining(builder) + defineTrainingRequest(builder) + defineTrainingAttempt(builder) + defineApplication(builder) + defineSettings(builder) +} + +function defineBase (builder: Builder): void { + builder.createDoc( + view.class.ActionCategory, + core.space.Model, + { + label: training.string.TrainingApplication, + visible: true + }, + training.actionCategory.Training + ) + + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: training.string.TrainingApplication, + icon: training.icon.TrainingApplication + }, + training.notification.TrainingGroup + ) + + builder.createDoc(notification.class.ActivityNotificationViewlet, core.space.Model, { + messageMatch: { + _class: activity.class.DocUpdateMessage, + objectClass: training.class.TrainingRequest + }, + presenter: training.component.TrainingRequestNotificationPresenter + }) + + builder.createModel(TSequence) +} + +function defineSpaceType (builder: Builder): void { + for (const role of roles) { + const { label, roleType } = getRoleAttributeProps(role.name) + + Prop(roleType, label)(TTrainingsTypeData.prototype, role._id) + } + + builder.createModel(TTrainingsTypeData) + + builder.createDoc( + core.class.SpaceTypeDescriptor, + core.space.Model, + { + name: training.string.TrainingApplication, + description: training.string.TrainingApplication, + icon: training.icon.TrainingApplication, + baseClass: core.class.TypedSpace, + availablePermissions: [ + training.permission.CreateTraining, + training.permission.ChangeSomeoneElsesTrainingOwner, + training.permission.ViewSomeoneElsesTrainingOverview, + training.permission.ViewSomeoneElsesTrainingQuestions, + training.permission.CreateRequestOnSomeoneElsesTraining, + training.permission.ChangeSomeoneElsesSentRequestOwner, + training.permission.ViewSomeoneElsesSentRequest, + training.permission.ViewSomeoneElsesTraineesResults + ] + }, + training.spaceTypeDescriptor.Trainings + ) + + builder.createDoc( + core.class.SpaceType, + core.space.Model, + { + name: 'Default Trainings', + descriptor: training.spaceTypeDescriptor.Trainings, + roles: roles.length, + targetClass: training.mixin.TrainingsTypeData + }, + training.spaceType.Trainings + ) + + for (const role of roles) { + builder.createDoc( + core.class.Role, + core.space.Model, + { + attachedTo: training.spaceType.Trainings, + attachedToClass: core.class.SpaceType, + collection: 'roles', + name: role.name, + permissions: role.permissions + }, + role._id + ) + } +} + +function defineTraining (builder: Builder): void { + builder.createModel(TTrainingState) + + builder.mixin(training.class.TypeTrainingState, core.class.Class, view.mixin.SortFuncs, { + func: training.function.TrainingStateSort + }) + + builder.mixin(training.class.TypeTrainingState, core.class.Class, view.mixin.AllValuesFunc, { + func: training.function.TrainingStateAllValues + }) + + builder.mixin(training.class.TypeTrainingState, core.class.Class, view.mixin.AttributeFilterPresenter, { + presenter: training.component.TrainingStateFilterPresenter + }) + + builder.mixin(training.class.TypeTrainingState, core.class.Class, view.mixin.AttributeFilter, { + component: view.component.ValueFilter + }) + + classPresenter(builder, training.class.TypeTrainingState, training.component.TrainingStatePresenter) + + builder.createModel(TTraining) + + builder.mixin(training.class.Training, core.class.Class, view.mixin.LinkProvider, { + encode: training.function.TrainingLinkProviderEncode + }) + + builder.mixin(training.class.Training, core.class.Class, view.mixin.ObjectPanel, { + component: training.component.TrainingPanel + }) + + builder.createDoc(view.class.Viewlet, core.space.Model, { + attachTo: training.class.Training, + descriptor: view.viewlet.Table, + config: [ + { + key: '', + sortingKey: 'seqNumber', + label: training.string.Training, + presenter: training.component.TrainingCodePresenter + }, + { + ...columns.trainingTitle, + key: 'title' + }, + { + ...columns.trainingRevision, + key: 'revision', + sortingKey: 'revision' + }, + { + key: 'state', + sortingKey: 'state', + displayProps: { align: 'center' } + }, + { + key: '', + sortingKey: 'passingScore', + label: training.string.TrainingPassingScore, + presenter: training.component.TrainingPassingScorePresenter, + displayProps: { align: 'center' } + }, + { + key: 'attachments', + label: attachment.string.Attachments, + props: { + canAdd: false, + canRemove: false + }, + displayProps: { align: 'center' } + }, + { + key: 'requests', + label: training.string.TrainingRequests, + presenter: view.component.NumberPresenter, + displayProps: { align: 'center' } + }, + 'modifiedOn', + { + ...columns.owner, + key: 'owner' + } + ], + configOptions: { + strict: true, + sortable: true + } + }) + + builder.mixin(training.class.Training, core.class.Class, view.mixin.ClassFilters, { + filters: ['revision', 'state', 'modifiedOn'] as Array, + strict: true + }) + + builder.mixin(training.class.Training, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete, tracker.action.NewRelatedIssue, print.action.Print] + }) + + createAction( + builder, + { + action: training.action.TrainingChangeOwnerAction, + visibilityTester: training.action.TrainingChangeOwnerIsAvailable, + input: 'focus', + label: training.string.ChangeOwner, + icon: contact.icon.Person, + target: training.class.Training, + context: { mode: ['context'], group: 'edit' }, + category: training.actionCategory.Training + }, + training.action.TrainingChangeOwner + ) + + createAction( + builder, + { + action: training.action.TrainingReleaseAction, + visibilityTester: training.action.TrainingReleaseIsAvailable, + input: 'focus', + label: training.string.TrainingRelease, + icon: training.icon.Release, + target: training.class.Training, + context: { mode: ['context'], group: 'edit' }, + category: training.actionCategory.Training + }, + training.action.TrainingRelease + ) + + createAction( + builder, + { + action: training.action.TrainingDuplicateAction, + visibilityTester: training.action.TrainingDuplicateIsAvailable, + input: 'focus', + label: training.string.Duplicate, + icon: training.icon.Duplicate, + target: training.class.Training, + context: { mode: ['context'], group: 'copy' }, + category: training.actionCategory.Training + }, + training.action.TrainingDuplicate + ) + + createAction( + builder, + { + action: training.action.TrainingDraftAction, + visibilityTester: training.action.TrainingDraftIsAvailable, + input: 'focus', + label: training.string.CreateNewVersion, + icon: training.icon.Duplicate, + target: training.class.Training, + context: { mode: ['context'], group: 'copy' }, + category: training.actionCategory.Training + }, + training.action.TrainingDraft + ) + + createAction( + builder, + { + action: training.action.TrainingRequestCreateAction, + visibilityTester: training.action.TrainingRequestCreateIsAvailable, + input: 'focus', + label: training.string.TrainingRequestAssign, + icon: training.icon.ViewSentRequests, + target: training.class.Training, + context: { mode: ['context'], group: 'tools' }, + category: training.actionCategory.Training + }, + training.action.TrainingRequestCreate + ) + + createAction( + builder, + { + override: [view.action.Delete], + action: training.action.TrainingDeleteAction, + visibilityTester: training.action.TrainingDeleteIsAvailable, + input: 'focus', + label: view.string.Delete, + icon: view.icon.Delete, + target: training.class.Training, + context: { mode: ['context'], group: 'remove' }, + category: training.actionCategory.Training + }, + training.action.TrainingDelete + ) + + definePermission(builder, training.permission.ChangeSomeoneElsesTrainingOwner, { + label: training.string.Permission_ChangeSomeoneElsesTrainingOwner, + description: training.string.Permission_ChangeSomeoneElsesTrainingOwner_Description + }) + definePermission(builder, training.permission.CreateTraining, { + label: training.string.Permission_CreateTraining, + description: training.string.Permission_CreateTraining_Description + }) + definePermission(builder, training.permission.ViewSomeoneElsesTrainingOverview, { + label: training.string.Permission_ViewSomeoneElsesTrainingOverview, + description: training.string.Permission_ViewSomeoneElsesTrainingOverview_Description + }) + definePermission(builder, training.permission.ViewSomeoneElsesTrainingQuestions, { + label: training.string.Permission_ViewSomeoneElsesTrainingQuestions, + description: training.string.Permission_ViewSomeoneElsesTrainingQuestions_Description + }) +} + +function defineTrainingRequest (builder: Builder): void { + builder.createModel(TTrainingRequest) + + builder.mixin(training.class.TrainingRequest, core.class.Class, view.mixin.LinkProvider, { + encode: training.function.TrainingRequestLinkProviderEncode + }) + + builder.mixin(training.class.TrainingRequest, core.class.Class, view.mixin.ObjectPanel, { + component: training.component.TrainingRequestPanel + }) + + const columnSeqNumber: BuildModelKey = { + key: '', + sortingKey: '$lookup.attachedTo.seqNumber', + label: training.string.Training, + presenter: training.component.SentRequestPresenter + } + const columnTrainingTitle: BuildModelKey = { + ...columns.trainingTitle, + key: '$lookup.attachedTo.title' + } + const columnTrainingRevision: BuildModelKey = { + ...columns.trainingRevision, + key: '$lookup.attachedTo.revision', + sortingKey: '$lookup.attachedTo.revision' + } + const columnSentState: BuildModelKey = { + key: '', + sortingKey: 'canceledOn', + presenter: training.component.SentRequestStatePresenter, + label: training.string.State, + displayProps: { align: 'center' } + } + const columnIncomingState: BuildModelKey = { + key: '', + label: training.string.State, + presenter: training.component.IncomingRequestStatePresenter, + displayProps: { align: 'center' } + } + const columnCompletion: BuildModelKey = { + key: '', + label: training.string.TrainingRequestCompletion, + presenter: training.component.SentRequestCompletionPresenter + } + const columnIncomingAttempts: BuildModelKey = { + key: '', + label: training.string.TrainingAttempts, + presenter: training.component.IncomingRequestAttemptsPresenter, + displayProps: { align: 'center' } + } + const columnMaxAttempts: BuildModelKey = { + key: 'maxAttempts', + label: training.string.TrainingRequestMaxAttempts, + presenter: training.component.TrainingRequestMaxAttemptsPresenter, + displayProps: { align: 'center' } + } + const columnDueDate: BuildModelKey = { + key: 'dueDate', + label: training.string.TrainingRequestDueDate, + presenter: training.component.TrainingRequestDueDateEditor + } + const columnOwner: BuildModelKey = { + ...columns.owner, + key: 'owner' + } + const requestsViewletData: Omit, 'descriptor' | 'config'> = { + attachTo: training.class.TrainingRequest, + /* eslint-disable @typescript-eslint/consistent-type-assertions */ + options: { + lookup: { + attachedTo: training.class.Training + } + } as FindOptions, + /* eslint-enable @typescript-eslint/consistent-type-assertions */ + configOptions: { + strict: true, + sortable: true + } + } + + ;(function defineSentRequest () { + defineTableBrowserViewletDescriptor( + builder, + training.icon.ViewSentRequests, + training.string.ViewSentRequests, + training.viewlet.SentRequests + ) + const sentRequestsViewletData: Data = { + ...requestsViewletData, + config: [ + columnSeqNumber, + columnTrainingTitle, + columnTrainingRevision, + columnSentState, + columnCompletion, + 'createdOn', + columnMaxAttempts, + columnDueDate, + columnOwner + ], + descriptor: training.viewlet.SentRequests + } + builder.createDoc(view.class.Viewlet, core.space.Model, sentRequestsViewletData) + + defineTableBrowserViewletDescriptor( + builder, + training.icon.ViewSentRequests, + training.string.ViewSentRequests, + training.viewlet.TrainingSentRequests + ) + const trainingSentRequestsViewletData: Data = { + ...sentRequestsViewletData, + descriptor: training.viewlet.TrainingSentRequests, + config: sentRequestsViewletData.config.filter( + (column) => column !== columnTrainingTitle && column !== columnTrainingRevision + ) + } + builder.createDoc(view.class.Viewlet, core.space.Model, trainingSentRequestsViewletData) + + definePermission(builder, training.permission.ChangeSomeoneElsesSentRequestOwner, { + label: training.string.Permission_ChangeSomeoneElsesSentRequestOwner, + description: training.string.Permission_ChangeSomeoneElsesSentRequestOwner_Description + }) + definePermission(builder, training.permission.CreateRequestOnSomeoneElsesTraining, { + label: training.string.Permission_CreateRequestOnSomeoneElsesTraining, + description: training.string.Permission_CreateRequestOnSomeoneElsesTraining_Description + }) + definePermission(builder, training.permission.ViewSomeoneElsesSentRequest, { + label: training.string.Permission_ViewSomeoneElsesSentRequest, + description: training.string.Permission_ViewSomeoneElsesSentRequest_Description + }) + })() + ;(function defineIncomingRequest () { + defineTableBrowserViewletDescriptor( + builder, + training.icon.ViewIncomingRequests, + training.string.ViewIncomingRequests, + training.viewlet.IncomingRequests + ) + const incomingRequestsViewletData: Data = { + ...requestsViewletData, + descriptor: training.viewlet.IncomingRequests, + config: [ + columnSeqNumber, + columnTrainingTitle, + columnTrainingRevision, + columnIncomingAttempts, + columnIncomingState, + 'createdOn', + columnDueDate, + columnOwner + ] + } + builder.createDoc(view.class.Viewlet, core.space.Model, incomingRequestsViewletData) + + defineTableBrowserViewletDescriptor( + builder, + training.icon.ViewIncomingRequests, + training.string.ViewIncomingRequests, + training.viewlet.TrainingIncomingRequests + ) + const trainingIncomingRequestsViewletData: Data = { + ...incomingRequestsViewletData, + descriptor: training.viewlet.TrainingIncomingRequests, + config: incomingRequestsViewletData.config.filter( + (column) => column !== columnTrainingTitle && column !== columnTrainingRevision + ) + } + builder.createDoc(view.class.Viewlet, core.space.Model, trainingIncomingRequestsViewletData) + })() + + builder.mixin(training.class.TrainingRequest, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete, tracker.action.NewRelatedIssue, print.action.Print] + }) + + createAction( + builder, + { + action: training.action.TrainingRequestChangeOwnerAction, + visibilityTester: training.action.TrainingRequestChangeOwnerIsAvailable, + input: 'focus', + label: training.string.ChangeOwner, + icon: contact.icon.Person, + target: training.class.TrainingRequest, + context: { mode: ['context'], group: 'edit' }, + category: training.actionCategory.Training + }, + training.action.TrainingRequestChangeOwner + ) + + createAction( + builder, + { + action: training.action.TrainingRequestCancelAction, + visibilityTester: training.action.TrainingRequestCancelIsAvailable, + input: 'focus', + label: training.string.TrainingRequestCancel, + icon: training.icon.Cancel, + target: training.class.TrainingRequest, + context: { mode: ['context'], group: 'remove' }, + category: training.actionCategory.Training + }, + training.action.TrainingRequestCancel + ) + + builder.mixin(training.class.TrainingRequest, core.class.Class, activity.mixin.ActivityDoc, {}) + + builder.mixin(training.class.TrainingRequest, core.class.Class, view.mixin.ObjectTitle, { + titleProvider: training.function.TrainingRequestObjectTitleProvider + }) + + builder.mixin(training.class.TrainingRequest, core.class.Class, notification.mixin.ClassCollaborators, { + fields: ['trainees'] as Array + }) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + allowedForAuthor: true, + label: training.string.TrainingRequest, + group: training.notification.TrainingGroup, + txClasses: [core.class.TxCreateDoc, core.class.TxUpdateDoc], + objectClass: training.class.TrainingRequest, + providers: { + [notification.providers.EmailNotification]: true, + [notification.providers.PlatformNotification]: true + }, + templates: { + textTemplate: '{sender} sent you a training request {doc}', + htmlTemplate: '

{sender} sent you a training request {doc}

', + subjectTemplate: 'Training request {doc}' + } + }, + training.notification.TrainingRequest + ) +} + +function defineTrainingAttempt (builder: Builder): void { + builder.createModel(TTrainingAttemptState) + + builder.mixin(training.class.TypeTrainingAttemptState, core.class.Class, view.mixin.SortFuncs, { + func: training.function.TrainingAttemptStateSort + }) + + builder.mixin(training.class.TypeTrainingAttemptState, core.class.Class, view.mixin.AllValuesFunc, { + func: training.function.TrainingAttemptStateAllValues + }) + + builder.mixin(training.class.TypeTrainingAttemptState, core.class.Class, view.mixin.AttributeFilterPresenter, { + presenter: training.component.TrainingAttemptStateFilterPresenter + }) + + builder.mixin(training.class.TypeTrainingAttemptState, core.class.Class, view.mixin.AttributeFilter, { + component: view.component.ValueFilter + }) + + classPresenter(builder, training.class.TypeTrainingAttemptState, training.component.TrainingAttemptStatePresenter) + + builder.createModel(TTrainingAttempt) + + builder.mixin(training.class.TrainingAttempt, core.class.Class, view.mixin.LinkProvider, { + encode: training.function.TrainingAttemptLinkProviderEncode + }) + + builder.mixin(training.class.TrainingAttempt, core.class.Class, view.mixin.ObjectPanel, { + component: training.component.TrainingAttemptPanel + }) + + const columnTrainingSeqNumber: BuildModelKey = { + key: '', + sortingKey: '$lookup.attachedTo.$lookup.attachedTo.seqNumber', + label: training.string.Training, + presenter: training.component.TrainingAttemptPresenter + } + const columnTrainingTitle: BuildModelKey = { + ...columns.trainingTitle, + key: '$lookup.attachedTo.$lookup.attachedTo.title' + } + const columnTrainingRevision: BuildModelKey = { + ...columns.trainingRevision, + key: '$lookup.attachedTo.$lookup.attachedTo.revision', + sortingKey: '$lookup.attachedTo.$lookup.attachedTo.revision' + } + const columnAttemptSeqNumber: BuildModelKey = { + key: '', + sortingKey: 'seqNumber', + label: training.string.TrainingAttempt, + presenter: training.component.TrainingAttemptNumberPresenter, + displayProps: { align: 'center' } + } + const columnScore: BuildModelKey = { + key: '', + sortingKey: 'score', + presenter: training.component.TrainingAttemptScorePresenter, + label: questions.string.Score + } + const columnState: BuildModelKey = { + key: 'state', + sortingKey: 'state', + displayProps: { align: 'center' } + } + const columnOwner: BuildModelKey = { + ...columns.owner, + key: 'owner' + } + + defineTableBrowserViewletDescriptor( + builder, + training.icon.TrainingAttempt, + training.string.TrainingAttempts, + training.viewlet.TrainingAttempts + ) + const trainingAttemptsViewletData: Omit, 'descriptor'> = { + attachTo: training.class.TrainingAttempt, + /* eslint-disable @typescript-eslint/consistent-type-assertions */ + options: { + lookup: { + attachedTo: [ + training.class.TrainingRequest, + { + attachedTo: training.class.Training + } + ] + } + } as FindOptions, + /* eslint-enable @typescript-eslint/consistent-type-assertions */ + config: [ + columnTrainingSeqNumber, + columnTrainingTitle, + columnTrainingRevision, + columnAttemptSeqNumber, + columnState, + columnScore, + 'createdOn', + 'submittedOn', + columnOwner + ], + configOptions: { + strict: true, + sortable: true + } + } + builder.createDoc(view.class.Viewlet, core.space.Model, { + ...trainingAttemptsViewletData, + descriptor: training.viewlet.TrainingAttempts + }) + + defineTableBrowserViewletDescriptor( + builder, + training.icon.TrainingAttempt, + training.string.TrainingAttempts, + training.viewlet.TrainingAttemptsNested + ) + builder.createDoc(view.class.Viewlet, core.space.Model, { + ...trainingAttemptsViewletData, + descriptor: training.viewlet.TrainingAttemptsNested, + config: trainingAttemptsViewletData.config.filter( + (column) => column !== columnTrainingTitle && column !== columnTrainingRevision + ) + }) + + builder.mixin(training.class.TrainingAttempt, core.class.Class, view.mixin.ClassFilters, { + filters: ['state', 'owner', 'submittedOn'] as Array, + strict: true + }) + + builder.mixin(training.class.TrainingAttempt, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete, tracker.action.NewRelatedIssue, print.action.Print] + }) + + definePermission(builder, training.permission.ViewSomeoneElsesTraineesResults, { + label: training.string.Permission_ViewSomeoneElsesTraineesResults, + description: training.string.Permission_ViewSomeoneElsesTraineesResults_Description + }) +} + +function defineApplication (builder: Builder): void { + builder.createDoc( + workbench.class.Application, + core.space.Model, + { + label: training.string.TrainingApplication, + icon: training.icon.TrainingApplication, + alias: trainingId, + hidden: false, + locationResolver: training.resolver.Location, + navigatorModel: { + spaces: [], + specials: [ + { + id: TrainingSpecialIds.AllTrainings, + icon: training.icon.ViewAllTrainings, + label: training.string.ViewAllTrainings, + position: '00:common', + component: training.component.ViewAllTrainings, + componentProps: { + _class: training.class.Training, + icon: training.icon.ViewAllTrainings, + label: training.string.ViewAllTrainings + } + }, + { + id: TrainingSpecialIds.IncomingRequests, + icon: training.icon.ViewIncomingRequests, + label: training.string.ViewIncomingRequests, + position: '10:trainee', + component: training.component.ViewIncomingRequests, + componentProps: { + _class: training.class.TrainingRequest, + icon: training.icon.ViewIncomingRequests, + label: training.string.ViewIncomingRequests, + descriptors: [training.viewlet.IncomingRequests] + } + }, + { + id: TrainingSpecialIds.MyResults, + icon: training.icon.ViewMyResults, + label: training.string.ViewMyResults, + position: '10:trainee', + component: training.component.ViewMyResults, + componentProps: { + _class: training.class.TrainingAttempt, + icon: training.icon.ViewMyResults, + label: training.string.ViewMyResults, + descriptors: [training.viewlet.TrainingAttempts] + } + }, + { + id: TrainingSpecialIds.MyTrainings, + icon: training.icon.ViewMyTrainings, + label: training.string.ViewMyTrainings, + position: '20:trainer', + component: training.component.ViewMyTrainings, + componentProps: { + _class: training.class.Training, + icon: training.icon.ViewMyTrainings, + label: training.string.ViewMyTrainings + } + }, + { + id: TrainingSpecialIds.SentRequests, + icon: training.icon.ViewSentRequests, + label: training.string.ViewSentRequests, + position: '20:trainer', + component: training.component.ViewSentRequests, + componentProps: { + _class: training.class.TrainingRequest, + icon: training.icon.ViewSentRequests, + label: training.string.ViewSentRequests, + descriptors: [training.viewlet.SentRequests] + } + }, + { + id: TrainingSpecialIds.TraineesResults, + icon: training.icon.TrainingAttempt, + label: training.string.ViewTraineesResults, + position: '20:trainer', + component: training.component.ViewTraineesResults, + componentProps: { + _class: training.class.TrainingAttempt, + icon: training.icon.TrainingAttempt, + label: training.string.ViewTraineesResults, + descriptors: [training.viewlet.TrainingAttempts] + } + } + ] + } + }, + training.app.Training + ) +} + +function definePermission (builder: Builder, id: Ref, data: Data): void { + builder.createDoc(core.class.Permission, core.space.Model, data, id) +} + +function defineTableBrowserViewletDescriptor ( + builder: Builder, + icon: Asset, + label: IntlString, + ref: Ref +): void { + builder.createDoc( + view.class.ViewletDescriptor, + core.space.Model, + { + icon, + label, + component: view.component.TableBrowser + }, + ref + ) +} + +function defineSettings (builder: Builder): void { + builder.createDoc( + setting.class.WorkspaceSettingCategory, + core.space.Model, + { + name: 'trainings', + label: training.string.Trainings, + icon: training.icon.Training, + component: training.component.Settings, + order: 1150, + role: AccountRole.Maintainer + }, + training.setting.Trainings + ) +} + +const columns = { + trainingTitle: { + key: 'title', + label: training.string.TrainingTitle, + props: { accent: true }, + displayProps: { grow: true, align: 'left' } + }, + trainingRevision: { + key: 'revision', + label: training.string.Revision, + presenter: view.component.NumberPresenter, + displayProps: { align: 'center' } + }, + owner: { + key: 'owner', + label: training.string.Owner, + presenter: contacts.component.EmployeePresenter, + props: { shouldShowName: true }, + displayProps: { align: 'center' } + } +} as const diff --git a/models/training/src/migration.ts b/models/training/src/migration.ts new file mode 100644 index 0000000000..a440dd53db --- /dev/null +++ b/models/training/src/migration.ts @@ -0,0 +1,117 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { + tryMigrate, + type MigrateOperation, + type MigrationClient, + type MigrationUpgradeClient +} from '@hcengineering/model' +import { type Ref, TxOperations, type TypedSpace } from '@hcengineering/core' +import core, { DOMAIN_SPACE } from '@hcengineering/model-core' +import training, { trainingId, type Sequence } from '@hcengineering/training' +import contact, { DOMAIN_CONTACT } from '@hcengineering/model-contact' +import { Person, PersonAccount } from '@hcengineering/contact' + +export const trainingOperation: MigrateOperation = { + async migrate (client: MigrationClient): Promise { + await tryMigrate(client, trainingId, [ + { + state: 'migrateDefaultSpaceMembers', + func: migrateDefaultSpaceMembers + } + ]) + }, + async upgrade (client: MigrationUpgradeClient): Promise { + const tx = new TxOperations(client, core.account.System) + await ensureTypedSpace(tx) + await ensureSequence(tx) + } +} + +async function migrateDefaultSpaceMembers (client: MigrationClient): Promise { + await client.update( + DOMAIN_SPACE, + { + _id: training.space.Trainings, + autoJoin: false + }, + { + $set: { + autoJoin: true + } + } + ) + + const employees = await client.find(DOMAIN_CONTACT, { + _class: contact.class.Person, + [contact.mixin.Employee]: { $exists: true } + }) + const empAccs = await client.model.findAll(contact.class.PersonAccount, { + person: { $in: employees.map((e) => e._id) } + }) + + await client.update( + DOMAIN_SPACE, + { + _id: training.space.Trainings + }, + { + $addToSet: { + members: { $each: empAccs.map((e) => e._id) } + } + } + ) +} + +async function ensureTypedSpace (tx: TxOperations): Promise> { + const existing = await tx.findOne(core.class.TypedSpace, { + _id: training.space.Trainings + }) + + if (existing !== undefined) { + return existing._id + } + + return await tx.createDoc( + core.class.TypedSpace, + core.space.Space, + { + name: 'Trainings', + description: 'Space for all trainings', + private: false, + archived: false, + autoJoin: true, + members: [], + type: training.spaceType.Trainings + }, + training.space.Trainings + ) +} + +async function ensureSequence (tx: TxOperations): Promise> { + const existing = await tx.findOne(training.class.Sequence, { + attachedTo: training.class.Training + }) + + if (existing !== undefined) { + return existing._id + } + + return await tx.createDoc(training.class.Sequence, training.space.Trainings, { + attachedTo: training.class.Training, + sequence: 0 + }) +} diff --git a/models/training/src/plugin.ts b/models/training/src/plugin.ts new file mode 100644 index 0000000000..2f78c449d9 --- /dev/null +++ b/models/training/src/plugin.ts @@ -0,0 +1,57 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import type { NotificationGroup, NotificationType } from '@hcengineering/notification' +import { trainingId } from '@hcengineering/training' +import training from '@hcengineering/training-resources/src/plugin' +import { mergeIds, type Resource } from '@hcengineering/platform' +import { type Doc, type Ref } from '@hcengineering/core' +import { type Location, type ResolvedLocation } from '@hcengineering/ui' +import { type Application } from '@hcengineering/workbench' +import { type ActionCategory } from '@hcengineering/view' + +export default mergeIds(trainingId, training, { + app: { + Training: '' as Ref + }, + actionCategory: { + Training: '' as Ref + }, + notification: { + TrainingGroup: '' as Ref, + TrainingRequest: '' as Ref + }, + + // TODO: Move function resources declarations to plugins/*-resources + // Currently, dependencies look like this: + // models/*/src/index.ts + // → models/*/src/plugin.ts + // → plugins/*-resources/src/plugin.ts + // → plugins/*/src/plugin.ts + // For unknown reasons in plugins/*-resources we can declare component resources that depend on Svelte, + // but not function (incl. location resolver) resources that depend on UI (particularly on Location and ResolvedLocation). + // The build fails saying: + // > The inferred type of 'default' cannot be named without a reference to '[..]/node_modules/@hcengineering/ui/src'. + // > This is likely not portable. A type annotation is necessary. + + function: { + TrainingAttemptLinkProviderEncode: '' as Resource<(doc: Doc, props: Record) => Promise>, + TrainingLinkProviderEncode: '' as Resource<(doc: Doc, props: Record) => Promise>, + TrainingRequestLinkProviderEncode: '' as Resource<(doc: Doc, props: Record) => Promise> + }, + resolver: { + Location: '' as Resource<(location: Location) => Promise> + } +}) diff --git a/models/training/src/roles.ts b/models/training/src/roles.ts new file mode 100644 index 0000000000..e063932378 --- /dev/null +++ b/models/training/src/roles.ts @@ -0,0 +1,49 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import training from './plugin' + +export const roles = [ + { + _id: training.role.QARA, + name: 'QARA', + permissions: [ + training.permission.CreateTraining, + training.permission.ChangeSomeoneElsesTrainingOwner, + training.permission.ViewSomeoneElsesTrainingOverview, + training.permission.ViewSomeoneElsesTrainingQuestions, + training.permission.CreateRequestOnSomeoneElsesTraining, + training.permission.ChangeSomeoneElsesSentRequestOwner, + training.permission.ViewSomeoneElsesSentRequest, + training.permission.ViewSomeoneElsesTraineesResults + ] + }, + { + _id: training.role.Manager, + name: 'Managers', + permissions: [ + training.permission.CreateTraining, + training.permission.ViewSomeoneElsesTrainingOverview, + training.permission.ViewSomeoneElsesTrainingQuestions, + training.permission.CreateRequestOnSomeoneElsesTraining, + training.permission.ViewSomeoneElsesSentRequest, + training.permission.ViewSomeoneElsesTraineesResults + ] + }, + { + _id: training.role.QualifiedUser, + name: 'Qualified Users', + permissions: [training.permission.CreateTraining] + } +] diff --git a/models/training/src/types.ts b/models/training/src/types.ts new file mode 100644 index 0000000000..5f05543a79 --- /dev/null +++ b/models/training/src/types.ts @@ -0,0 +1,277 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import type { Attachment } from '@hcengineering/attachment' +import attachments from '@hcengineering/model-attachment' +import questions, { TypePercentage } from '@hcengineering/model-questions' +import type { Answer, Percentage, Question } from '@hcengineering/questions' +import { + type Sequence, + type Training, + type TrainingAttempt, + TrainingAttemptState, + type TrainingRequest, + TrainingState +} from '@hcengineering/training' +import contact, { type Employee } from '@hcengineering/contact' +import core, { + type Class, + type CollectionSize, + type Doc, + type Domain, + IndexKind, + type Markup, + type Ref, + type Timestamp, + type Type, + type TypedSpace, + RolesAssignment, + Role, + Account +} from '@hcengineering/core' +import { + ArrOf, + Collection, + Hidden, + Index, + Mixin, + Model, + Prop, + TypeMarkup, + TypeNumber, + TypeRef, + TypeString, + TypeTimestamp, + UX +} from '@hcengineering/model' +import { TAttachedDoc, TDoc, TType, TTypedSpace } from '@hcengineering/model-core' +import training from './plugin' +import { getEmbeddedLabel } from '@hcengineering/platform' + +export const DOMAIN_TRAINING = 'training' as Domain + +/** @public */ +export function TypeTrainingState (): Type { + return { _class: training.class.TypeTrainingState, label: training.string.State } +} + +@UX(training.string.State) +@Model(training.class.TypeTrainingState, core.class.Type) +export class TTrainingState extends TType {} + +/** @public */ +export function TypeTrainingAttemptState (): Type { + return { _class: training.class.TypeTrainingAttemptState, label: training.string.State } +} + +@UX(training.string.State) +@Model(training.class.TypeTrainingAttemptState, core.class.Type) +export class TTrainingAttemptState extends TType {} + +/** @public */ +@Model(training.class.Training, core.class.Doc, DOMAIN_TRAINING) +@UX(training.string.Training, training.icon.Training) +export class TTraining extends TDoc implements Training { + @Prop(TypeRef(core.class.TypedSpace), core.string.Space) + @Index(IndexKind.Indexed) + @Hidden() + declare space: Ref + + @Prop(TypeString(), training.string.TrainingTitle) + @Index(IndexKind.FullText) + title!: string + + @Prop(TypeString(), core.string.Id) + @Index(IndexKind.FullText) + code!: string + + @Prop(TypeNumber(), training.string.Revision) + @Index(IndexKind.Indexed) + revision!: number + + @Prop(TypeRef(contact.mixin.Employee), training.string.TrainingAuthor, { + editor: training.component.EmployeeEditor + }) + author!: Ref + + @Prop(TypeRef(contact.mixin.Employee), training.string.Owner, { + editor: training.component.EmployeeEditor + }) + owner!: Ref + + @Prop(TypeTrainingState(), training.string.State) + state!: TrainingState + + @Prop(TypeMarkup(), core.string.Description, { defaultValue: 0 }) + @Index(IndexKind.Indexed) + description: Markup = '' + + @Prop(Collection(attachments.class.Attachment), attachments.string.Attachments, { defaultValue: 0 }) + attachments: CollectionSize = 0 + + @Prop(TypePercentage(), training.string.TrainingPassingScore, { defaultValue: 100 }) + @Index(IndexKind.Indexed) + passingScore: Percentage = 100 + + @Prop(TypeTimestamp(), training.string.TrainingReleasedOn, { defaultValue: null }) + @Index(IndexKind.Indexed) + releasedOn: Timestamp | null = null + + @Prop(TypeRef(contact.mixin.Employee), training.string.TrainingReleasedBy, { + defaultValue: null, + editor: training.component.EmployeeEditor + }) + releasedBy: Ref | null = null + + @Prop(Collection(questions.class.Question), training.string.TrainingQuestions, { defaultValue: 0 }) + questions: CollectionSize> = 0 + + @Prop(Collection(training.class.TrainingRequest), training.string.TrainingRequests, { defaultValue: 0 }) + requests: CollectionSize = 0 +} + +@Model(training.class.TrainingRequest, core.class.AttachedDoc, DOMAIN_TRAINING) +@UX(training.string.TrainingRequest, training.icon.TrainingRequest) +export class TTrainingRequest extends TAttachedDoc implements TrainingRequest { + @Prop(TypeRef(core.class.TypedSpace), core.string.Space) + @Index(IndexKind.Indexed) + @Hidden() + declare space: Ref + + @Prop(TypeRef(training.class.Training), core.string.AttachedTo) + @Index(IndexKind.Indexed) + @Hidden() + declare attachedTo: Ref + + @Prop(TypeRef(training.class.Training), core.string.AttachedToClass) + @Index(IndexKind.Indexed) + @Hidden() + declare attachedToClass: Ref> + + @Prop(TypeString(), core.string.Collection) + @Hidden() + override collection: 'requests' = 'requests' + + @Prop(TypeRef(contact.mixin.Employee), training.string.Owner, { + editor: training.component.EmployeeEditor + }) + @Index(IndexKind.Indexed) + owner!: Ref + + @Prop(ArrOf(TypeRef(contact.mixin.Employee)), training.string.TrainingRequestTrainees, { + editor: training.component.TrainingRequestTraineesEditor + }) + trainees: Ref[] = [] + + @Prop(TypeTimestamp(), training.string.TrainingRequestDueDate, { + defaultValue: null, + editor: training.component.TrainingRequestDueDateEditor + }) + @Index(IndexKind.Indexed) + dueDate: Timestamp | null = null + + @Prop(TypeNumber(), training.string.TrainingRequestMaxAttempts, { + defaultValue: null, + editor: training.component.TrainingRequestMaxAttemptsEditor + }) + maxAttempts: number | null = null + + @Prop(Collection(training.class.TrainingAttempt), training.string.TrainingAttempts, { defaultValue: 0 }) + attempts: CollectionSize = 0 + + @Prop(TypeTimestamp(), training.string.TrainingRequestCanceledDate, { defaultValue: null }) + canceledOn: Timestamp | null = null + + @Prop(TypeRef(contact.mixin.Employee), training.string.TrainingRequestCanceledBy, { + defaultValue: null, + editor: training.component.EmployeeEditor + }) + canceledBy: Ref | null = null +} + +@Model(training.class.TrainingAttempt, core.class.AttachedDoc, DOMAIN_TRAINING) +@UX(training.string.TrainingAttempt, training.icon.TrainingAttempt) +export class TTrainingAttempt extends TAttachedDoc implements TrainingAttempt { + @Prop(TypeRef(core.class.TypedSpace), core.string.Space) + @Index(IndexKind.Indexed) + @Hidden() + declare space: Ref + + @Prop(TypeRef(training.class.TrainingRequest), core.string.AttachedTo) + @Index(IndexKind.Indexed) + @Hidden() + declare attachedTo: Ref + + @Prop(TypeRef(training.class.TrainingRequest), core.string.AttachedToClass) + @Index(IndexKind.Indexed) + @Hidden() + declare attachedToClass: Ref> + + @Prop(TypeString(), core.string.Collection) + @Hidden() + override collection: 'attempts' = 'attempts' + + @Prop(TypeRef(contact.mixin.Employee), training.string.Owner, { + editor: training.component.EmployeeEditor + }) + @Index(IndexKind.Indexed) + owner!: Ref + + @Prop(TypeNumber(), training.string.TrainingAttempt, { defaultValue: 1 }) + @Index(IndexKind.Indexed) + seqNumber: number = 1 + + @Prop(TypeTrainingAttemptState(), training.string.State) + @Index(IndexKind.Indexed) + state: TrainingAttemptState = TrainingAttemptState.Draft + + @Prop(Collection(questions.class.Answer), questions.string.Answers, { defaultValue: 0 }) + answers: CollectionSize> = 0 + + @Prop(TypeTimestamp(), training.string.TrainingAttemptSubmittedDate, { defaultValue: null }) + @Index(IndexKind.Indexed) + submittedOn: Timestamp | null = null + + @Prop(TypeRef(contact.mixin.Employee), training.string.TrainingAttemptSubmittedBy, { + defaultValue: null, + editor: training.component.EmployeeEditor + }) + submittedBy: Ref | null = null + + @Prop(TypePercentage(), questions.string.Score, { defaultValue: null }) + @Index(IndexKind.Indexed) + score: Percentage | null = null + + @Prop(TypeNumber(), training.string.TrainingAttemptAssessmentsTotal, { defaultValue: null }) + assessmentsTotal: number | null = null + + @Prop(TypeNumber(), training.string.TrainingAttemptAssessmentsPassed, { defaultValue: null }) + assessmentsPassed: number | null = null +} + +@Model(training.class.Sequence, core.class.Doc, DOMAIN_TRAINING) +export class TSequence extends TDoc implements Sequence { + @Prop(TypeRef(core.class.Doc), core.string.AttachedTo) + @Index(IndexKind.Indexed) + attachedTo!: Ref> + + sequence!: number +} + +@Mixin(training.mixin.TrainingsTypeData, core.class.TypedSpace) +@UX(getEmbeddedLabel('Default Trainings'), training.icon.TrainingApplication) +export class TTrainingsTypeData extends TTypedSpace implements RolesAssignment { + [key: Ref]: Ref[] +} diff --git a/models/training/tsconfig.json b/models/training/tsconfig.json new file mode 100644 index 0000000000..59e4fd4297 --- /dev/null +++ b/models/training/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/plugins/controlled-documents-assets/.eslintrc.js b/plugins/controlled-documents-assets/.eslintrc.js new file mode 100644 index 0000000000..e73094bc7e --- /dev/null +++ b/plugins/controlled-documents-assets/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/assets/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/plugins/controlled-documents-assets/assets/icons.svg b/plugins/controlled-documents-assets/assets/icons.svg new file mode 100644 index 0000000000..216f9718ef --- /dev/null +++ b/plugins/controlled-documents-assets/assets/icons.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/controlled-documents-assets/config/rig.json b/plugins/controlled-documents-assets/config/rig.json new file mode 100644 index 0000000000..b75800b9b7 --- /dev/null +++ b/plugins/controlled-documents-assets/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "assets" +} diff --git a/plugins/controlled-documents-assets/jest.config.js b/plugins/controlled-documents-assets/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/plugins/controlled-documents-assets/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/plugins/controlled-documents-assets/lang/en.json b/plugins/controlled-documents-assets/lang/en.json new file mode 100644 index 0000000000..b893d9d65c --- /dev/null +++ b/plugins/controlled-documents-assets/lang/en.json @@ -0,0 +1,316 @@ +{ + "string": { + "ID": "ID", + "Code": "Code", + "Number": "Number", + "Category": "Category", + "CollaborativeDocument": "Collaborative document", + "ControlledDocument": "Controlled document", + "Requests": "Requests", + "EffectiveDate": "Effective date", + "PlannedEffectiveDate": "Planned effective date", + "Section": "Section", + "Rank": "Rang", + "CollaborativeSection": "Collaborative section", + "CollaboratorSectionId": "Collaborator section ID", + "AttachmentsSection": "Attachments section", + "DocumentRequest": "Request", + "DocumentReviewRequest": "Document review request", + "DocumentApprovalRequest": "Document approval request", + "ControlledStatus": "Controlled status", + "Categories": "Categories", + "Guidance": "Guidance", + "Required": "Required", + "Description": "Description", + "SectionTemplate": "Section template", + "Major": "Major", + "Minor": "Minor", + "Patch": "Patch", + "DocumentApprovals": "Reviews and Approvals", + "ChangeOwner": "Change document owner", + "ChangeOwnerHintBeginning": "Transfer ownership of the", + "ChangeOwnerHintEnd": "to another person.", + "ChangeOwnerWarning": "You will not be able to edit this document after this action.", + "SelectOwner": "Select new owner", + "CreateDocument": "Create new document", + "CreateTemplate": "Create new template", + "Documents": "Documents", + "Document": "Document", + "NewDocumentDialogClose": "Do you want to close this dialog?", + "NewDocumentCloseNote": "All changes will be lost", + "Cancel": "Cancel", + "EditorPlaceholder": "type to start editing...", + "Version": "Version", + "TemplateVersion": "Template version", + "VersionValue": "v{major}.{minor}", + "SearchDocument": "Search document...", + "CreateEnVersion": "Create version for review", + "Approvers": "Approvers", + "CoAuthors": "Co-Authors", + "Status": "Status", + "TemplateName": "Template name", + "DocumentApplication": "Controlled Documents", + "MyDocuments": "My documents", + "Library": "Library", + "Labels": "Labels", + "Author": "Author", + "Review": "Review", + "Approval": "Approval", + "Reviewer": "Reviewer", + "Approver": "Approver", + + "DeleteCategory": "Delete category?", + "DeleteCategoryHint": "That action cannot be undone", + "DeleteCategoryWarning": "The category cannot be deleted because it is in use", + + "Latest": "Actual", + "Draft": "Draft", + + "Reviewers": "Reviewers", + + "ViewMode": "Viewing", + "EditMode": "Editing", + "ComparisonMode": "Comparing", + + "Compare": "Compare:", + "Against": "Against:", + + "ComparisonModeNotSupported": "Comparison mode is not supported.", + + "CreateDraft": "Create Draft", + "SendForApproval": "Send for approval", + "SendForReview": "Send for review", + "CompleteReview": "Complete review", + + "Approve": "Approve", + "Reject": "Reject", + "ConfirmApproval": "Confirm approval", + "ConfirmRejection": "Confirm rejection", + "ProvideRejectionReason": "Type rejection reason...", + "RejectionReason": "Rejection reason", + "ConfirmReviewCompletion": "Confirm review completion", + "AddApprovalTitle": "Send your document to approvers", + "AddApprovalDescription1": "To prepare your document for approval it should fit few requirements:", + "AddApprovalDescription2": "has status 'Draft' or 'Reviewed'", + "AddApprovalDescription3": "has no unresolved comments", + "AddApprovalDescription4": "if training is planned, the training has status 'Released'", + "NoApprovalsDescription": "There are no approvals for this version of document", + "CurrentVersion": "Current version", + + "DocumentTemplate": "Template", + "DocumentTemplates": "Templates", + "DocumentCode": "Document code", + "TemplateCode": "Template code", + "DocumentCodePlaceholder": "DOC-1", + "DocumentPrefixPlaceholder": "DOC", + "DocumentPrefix": "Documents prefix", + "Sections": "Sections", + "TemplateSectionTitle": "Title", + "DocumentTemplateCreateLabel": "Template", + "DocumentCategoryCreateLabel": "Category", + "CreateDocumentCategory": "Create a category", + "DocumentCategoryAlreadyExists": "Category \"{title}\" already exists. ", + "DocumentCategoryCodeAlreadyExists": "Code \"{code}\" already in use. ", + "AttachmentsMax": "Maximum allowed attachments", + "Resolve": "Resolve", + "Unresolve": "Unresolve", + "Resolved": "Resolved", + "ShowResolved": "Show resolved comments", + "Ordering": "Ordering", + "Title": "Title", + + "Effective": "Effective", + "Archived": "Archived", + "Deleted": "Deleted", + "MetaAbstract": "Abstract", + + "ContentTab": "Content", + "TeamTab": "Team", + "MetaTab": "Metadata", + "ChangeControlTab": "Change control", + "ReleaseTab": "Release", + "HistoryTab": "History", + + "ModificationDate": "Modified at", + "Modified": "Modified", + "Owner": "Owner", + "AssignedTo": "Assigneed to", + "Unassigned": "Unassigned", + "Untitled": "Untitled", + "Copy": "copy", + + "AccessWorkarea": "Access Workarea", + "EffectiveLibrary": "Effective library", + "WorkingLibrary": "Working library", + "CreateDraftQmsDocuments": "Create draft QMS documents", + "OwnDocumentAskReviewGetApproval": "Own a QMS document, ask for review, get approval", + "ApproveDocuments": "Approve documents", + "ReassignOwnershipToAnotherUser": "Reassign ownership to another user", + "MakeDocumentEffective": "Make document effective", + "CreateDraftQmsTemplates": "Create draft QMS templates", + + "ChangeControl": "Change Control", + "ReviewInterval": "Review interval", + + "DocTemplateDeleteSectionTitle": "Confirm deletion of section", + "DocTemplateDeleteSectionConfirm": "Are you sure that you want to permanently remove section \"{section}\" from the template?", + + "AddSectionAbove": "Add new section above", + "AddSectionBelow": "Add new section below", + + "SelectReviewers": "Select reviewers", + "SelectApprovers": "Select approvers", + "RequestsToReviewTheDoc": "requests you to review the document", + "RequestsToApproveTheDoc": "requests you to approve the document", + "Parent": "Parent", + "Template": "Template", + "GeneralInfo": "General Info", + "InProgress": "In progress", + + "EditDescription": "Edit description", + "EditGuidance": "Edit guidance", + + "NewDocument": "New document", + "NewDocumentCategory": "New category", + "NewDocumentTemplate": "New template", + + "LocationStepTitle": "Location", + "TemplateStepTitle": "Template", + "InfoStepTitle": "Info", + "TeamStepTitle": "Team", + + "Duplicate": "Duplicate", + + "TitleAndDescr": "Title and description", + "Reason": "Reason", + "AbstractPlaceholder": "What is this document about? Who will need it and when? ...", + "NewDocCreation": "New document creation", + "NewTemplateCreation": "New template creation", + "NewTemplatePlaceholder": "What is this template about? Describe how to properly use it...", + "CustomReason": "Custom", + "ReasonPlaceholder": "Specify the reason...", + + "EditDocument": "Edit document", + + "Key": "Key", + "SectionKey": "Section key", + "CommentsSequence": "Comments sequence", + + "Email": "Email", + "Password": "Password", + "FieldIsEmpty": "{field} is empty", + "ValidatingCredentials": "Validating credentials...", + + "Organisation": "Organisation", + "UnsortedTemplates": "Unsorted templates", + "Project": "Project", + "Projects": "Projects", + "ExternalSpace": "Project space", + "DocumentSpaceType": "Documents space type", + + "EffectiveImmediately": "Immediately upon approval", + "EffectiveOn": "Make effective on", + + "PeriodicReviewToBeCompleted": "Periodic review to be completed within", + "MonthsAfterEffectiveDate": "months after its effective date", + "ToBePassedWithin": "To be passed within", + "AttemptsAnd": "attempts and", + "DaysAfterEffectiveDate": "days after effective date", + + "Index": "Index", + "Path": "Path", + + "CreateChildDocument": "Create child document", + "CreateChildTemplate": "Create child template", + + "All": "All", + + "Space": "Space", + "SelectParent": "Select the parent object for your document", + + "PrefixInUse": "This prefix is already in use by", + "CodeInUse": "This code is already in use by", + "ChangeCode": "Change Code", + "ChangePrefix": "Change Prefix", + + "MarkDocAsDeleted": "Marking document as deleted", + "MarkDocsAsDeleted": "Marking multiple documents as deleted", + "MarkDocAsDeletedConfirm": "Do you really want to mark the following documents as deleted: {titles}?", + + "DocumentInHierarchy": "Document in hierarchy", + "FirstDraftVersion": "This is the first draft version of the document. There's no history yet.", + + "EffectiveDocumentLifecycle": "Effective document lifecycle", + + "ReasonAndImpact": "Reason & Impact", + "ImpactAnalysis": "Impact analysis", + "ImpactedDocuments": "Impacted documents", + + "CreateDocumentFailed": "Failed to create document", + "CreateDocumentTemplateFailed": "Failed to create template", + "TryAgain": "Please try again", + + "DescribeChanges": "Describe what was changed...", + "DescribeReason": "Describe why it was changed...", + "DescribeImpact": "Has an impact on...", + "AddDocument": "Add document", + "NoDocuments": "No documents", + "SysTemplate": "System template", + "DocumentTrainingDueDays": "Due days", + "DocumentTrainingEnabled": "Enabled", + "Own": "Own", + "Snapshot": "Snapshot", + "Snapshots": "Snapshots", + "ControlledSnapshot": "Controlled snapshot", + "Name": "Name", + "DraftRevision": "Draft revision", + "CreateNewDraft": "Draft new version", + "ChangeSeverity": "Change severity", + "Reference": "Reference", + "History": "History", + "Signatories": "Signatories", + "Page": "Page ", + "Of": " of ", + "CreatedFromTemplate": "Created from template: ", + + "ViewAll": "View all", + "Readonly": "Readonly", + + "NewDocumentSpace": "New documents space", + "EditDocumentSpace": "Edit documents space", + + "DocSpaceDescriptionPlaceholder": "Describe your space...", + "Members": "Members", + "RoleLabel": "Role: {role}", + "CreateOrgSpace": "Create organisation space", + + "ReviewDocumentPermission": "Review document", + "ReviewDocumentDescription": "Grants users ability to review a document", + "ApproveDocumentPermission": "Approve document", + "ApproveDocumentDescription": "Grants users ability to approve a document", + "CoAuthorDocumentPermission": "Co-author document", + "CoAuthorDocumentDescription": "Grants users ability to co-author a document", + "CreateDocumentPermission": "Create document", + "CreateDocumentDescription": "Grants users ability to create a document", + "UpdateDocumentOwnerPermission": "Update document owner", + "UpdateDocumentOwnerDescription": "Grants users ability to update a document owner", + + "CreateDocumentCategoryPermission": "Create document category", + "CreateDocumentCategoryDescription": "Grants users ability to create a document category", + "UpdateDocumentCategoryPermission": "Update document category", + "UpdateDocumentCategoryDescription": "Grants users ability to update a document category", + "DeleteDocumentCategoryPermission": "Delete document category", + "DeleteDocumentCategoryDescription": "Grants users ability to delete a document category", + "ConfigLabel": "Controlled Documents", + "ConfigDescription": "Extension to manage controlled documents" + }, + "controlledDocStates": { + "Empty": "", + "Approved": "Approved", + "InApproval": "In Approval", + "InReview": "In Review", + "Reviewed": "Reviewed", + "Rejected": "Rejected", + "ToReview": "To Review" + } +} diff --git a/plugins/controlled-documents-assets/lang/ru.json b/plugins/controlled-documents-assets/lang/ru.json new file mode 100644 index 0000000000..6cc7f1819f --- /dev/null +++ b/plugins/controlled-documents-assets/lang/ru.json @@ -0,0 +1,317 @@ +{ + "string": { + "ID": "ID", + "Code": "Код", + "Number": "Номер", + "Category": "Категория", + "CollaborativeDocument": "Коллаборативный документ", + "ControlledDocument": "Контролируемый документ", + "Requests": "Запросы", + "EffectiveDate": "Дата выпуска", + "PlannedEffectiveDate": "Планируемая дата выпуска", + "Section": "Section", + "Rank": "Ранг", + "CollaborativeSection": "Коллаборативная секция", + "CollaboratorSectionId": "ID коллаборационной секции", + "AttachmentsSection": "Секция прикрепленных файлов", + "DocumentRequest": "Запрос", + "DocumentReviewRequest": "Запрос рецензии документа", + "DocumentApprovalRequest": "Запрос утверждения документа", + "ControlledStatus": "Контролируемый статус", + "Categories": "Категории", + "Guidance": "Руководство", + "Required": "Обязательно", + "Description": "Описание", + "SectionTemplate": "Шаблон секции", + "Major": "Мажорная", + "Minor": "Минорная", + "Patch": "Патч", + "DocumentApprovals": "Рецензии и утверждения", + "ChangeOwner": "Изменить владельца документа", + "ChangeOwnerHintBeginning": "Передайте права владельца документа", + "ChangeOwnerHintEnd": "другому лицу.", + "ChangeOwnerWarning": "Вы не сможете редактировать документ после этой операции.", + "SelectOwner": "Выберите нового владельца", + "CreateDocument": "Создать документ", + "CreateTemplate": "Создать шаблон", + "Documents": "Документы", + "Document": "Документ", + "NewDocumentDialogClose": "Вы действительно хотите закрыть окно?", + "NewDocumentCloseNote": "Все внесенные изменения будут потеряны", + "Cancel": "Отмена", + "EditorPlaceholder": "введите для начала...", + "Version": "Версия", + "TemplateVersion": "Версия шаблона", + "VersionValue": "v{major}.{minor}", + "SearchDocument": "Найти документ...", + "CreateEnVersion": "Создать версию для оценки", + "Approvers": "Утверждающие", + "CoAuthors": "Соавторы", + "Status": "Статус", + "TemplateName": "Имя шаблона", + "DocumentApplication": "Управляемые Документы", + "MyDocuments": "Мои документы", + "Library": "Библиотека", + "Labels": "Метки", + "Author": "Автор", + "Review": "Рецензия", + "Approval": "Утверждение", + "Reviewer": "Рецензент", + "Approver": "Утверждающий", + + "DeleteCategory": "Удалить категорию?", + "DeleteCategoryHint": "Это действие не может быть отменено", + "DeleteCategoryWarning": "Эта категория используется, поэтому не может быть удалена", + + "Latest": "Актуально", + "Draft": "Рабочая копия", + + "Reviewers": "Рецензенты", + + "ViewMode": "Просмотр", + "EditMode": "Редактирование", + "ComparisonMode": "Сравнение", + + "Compare": "Сравнить:", + "Against": "с:", + + "ComparisonModeNotSupported": "Сравнение не поддерживается.", + + "CreateDraft": "Создать рабочую копию", + "SendForApproval": "Попросить утверждения", + "SendForReview": "Попросить рецензию", + "CompleteReview": "Закончить рецензию", + + "Approve": "Утвердить", + "Reject": "Отказать", + "ConfirmApproval": "Подтвердите согласие", + "ConfirmRejection": "Подтвердите отказ", + "ProvideRejectionReason": "Напишите причину отказа", + "RejectionReason": "Причина отказа", + "ConfirmReviewCompletion": "Подтвердите окончание рецензии", + "AddApprovalTitle": "Отправьте ваш документ на утверждение", + "AddApprovalDescription1": "Для отправки вашего документа на утверждение, он должен удовлетворять следующим требованиям:", + "AddApprovalDescription2": "иметь статус 'Рабочая копия' или 'Рецензии получены'", + "AddApprovalDescription3": "не иметь незавершенных обсуждений", + "AddApprovalDescription4": "если запланирован тренинг, то этот тренинг должен иметь статус 'Опубликован'", + "NoApprovalsDescription": "Нет утверждений для этой версии документа", + "CurrentVersion": "Текущая версия", + + "DocumentTemplate": "Шаблон", + "DocumentTemplates": "Шаблоны", + "DocumentCode": "Код документа", + "TemplateCode": "Код шаблона", + "DocumentCodePlaceholder": "ДОК-1", + "DocumentPrefixPlaceholder": "ДОК", + "DocumentPrefix": "Префикс документов", + "Sections": "Разделы", + "TemplateSectionTitle": "Заголовок", + "DocumentTemplateCreateLabel": "Шалблон", + "DocumentCategoryCreateLabel": "Категория", + "CreateDocumentCategory": "Создать категорию документа", + "DocumentCategoryAlreadyExists": "Категория \"{title}\" уже существует. ", + "DocumentCategoryCodeAlreadyExists": "Код \"{code}\" уже используется. ", + "AttachmentsMax": "Максимальное число допустимых файлов", + "Resolve": "Пометить выполненным", + "Unresolve": "Пометить невыполненным", + "Resolved": "Выполненный", + "ShowResolved": "Показать выполненные", + "Ordering": "Сортировка", + "Title": "Заголовок", + + "Effective": "Актуальная", + "Archived": "Архивированный", + "Deleted": "Удаленный", + "MetaAbstract": "Описание", + + "ContentTab": "Документ", + "TeamTab": "Команда", + "MetaTab": "Метаданные", + "ChangeControlTab": "Контроль изменений", + "ReleaseTab": "Выпуск", + "HistoryTab": "История", + + "ModificationDate": "Модифицированно", + "Modified": "Модифицированно", + "Owner": "Владелец", + "AssignedTo": "Назначано на", + "Unassigned": "Не назначено", + "Untitled": "Без названия", + "Copy": "копия", + + "AccessWorkarea": "Доступ к рабочей области", + "EffectiveLibrary": "Действующая библиотека", + "WorkingLibrary": "Рабочая библиотека", + "CreateDraftQmsDocuments": "Создание черновых QMS документов", + "OwnDocumentAskReviewGetApproval": "Владение QMS документами, запрос рецензии, получение утверждения", + "ApproveDocuments": "Утверждение документов", + "ReassignOwnershipToAnotherUser": "Переназначение права владения другому пользователю", + "MakeDocumentEffective": "Сделать документ действующим", + "CreateDraftQmsTemplates": "Создание черновых QMS шаблонов", + + "ChangeControl": "Контроль Изменений", + "ReviewInterval": "Интервал ревью", + + "DocTemplateDeleteSectionTitle": "Подтверждение удаления секции", + "DocTemplateDeleteSectionConfirm": "Вы действительно хотите навсегда удалить секцию \"{section}\" из шаблона?", + + "AddSectionAbove": "Добавить новую секцию сверху", + "AddSectionBelow": "Добавить новую секцию снизу", + + "SelectReviewers": "Выберите рецензентов", + "SelectApprovers": "Выберите утверждающих", + "RequestsToReviewTheDoc": "запрашивает у вас рецензию на документ", + "RequestsToApproveTheDoc": "запрашивает у вас утверждение документа", + "Parent": "Родитель", + "Template": "Шаблон", + "GeneralInfo": "Общая информация", + "InProgress": "В прогресссе", + + "EditDescription": "Редактировать описание", + "EditGuidance": "Редактировать руководство", + + "NewDocument": "Новый документ", + "NewDocumentCategory": "Новая категория", + "NewDocumentTemplate": "Новый шаблон", + + "LocationStepTitle": "Расположение", + "TemplateStepTitle": "Шаблон", + "InfoStepTitle": "Инфо", + "TeamStepTitle": "Команда", + + "Duplicate": "Дублировать", + + "TitleAndDescr": "Заголовок и описание", + "Reason": "Причина", + "AbstractPlaceholder": "О чем этот документ? Кто будет его использовать и как? ...", + "NewDocCreation": "Создание нового документа", + "NewTemplateCreation": "Создание нового шаблона", + "NewTemplatePlaceholder": "О чем этот шаблон? Опишите как правильно его использовать...", + "CustomReason": "Другая", + "ReasonPlaceholder": "Укажите причину...", + + "EditDocument": "Редактировать документ", + + "Key": "Ключ", + "SectionKey": "Ключ секции", + "CommentsSequence": "Последовательность комментариев", + + "Email": "Email", + "Password": "Пароль", + "FieldIsEmpty": "{field} не задан", + "ValidatingCredentials": "Проверка информации...", + + "Organisation": "Организация", + "UnsortedTemplates": "Несортированные шаблоны", + "Project": "Проект", + "Projects": "Проекты", + "ExternalSpace": "Пространства проектов", + "DocumentSpaceType": "Тип пространства документов", + + "EffectiveImmediately": "Сразу после утверждения", + "EffectiveOn": "Сделать эффективным:", + + "PeriodicReviewToBeCompleted": "Периодическое ревью документа в течение", + "MonthsAfterEffectiveDate": "месяцев после даты, когда документ стал эффективным", + "ToBePassedWithin": "Пройти в течение", + "AttemptsAnd": "попыток и", + "DaysAfterEffectiveDate": "дней после даты, когда документ стал эффективным", + + "Index": "Индекс", + "Path": "Путь", + + "CreateChildDocument": "Создать дочерний документ", + "CreateChildTemplate": "Создать дочерний шаблон", + + "All": "Все", + + "Space": "Пространство", + "SelectParent": "Выберите родительский объект для Вашего документа", + + "PrefixInUse": "Этот префикс уже используется", + "CodeInUse": "Этот код уже используется", + "ChangeCode": "Изменить код", + "ChangePrefix": "Изменить префикс", + + "MarkDocAsDeleted": "Пометить документ как удаленный", + "MarkDocsAsDeleted": "Пометить несколько документов как удаленные", + "MarkDocAsDeletedConfirm": "Вы действительно хотите пометить следующие документы как удаленные: {titles}?", + + "DocumentInHierarchy": "Документ в иерархии", + "FirstDraftVersion": "Это первая рабочая копия документа. Для него пока нет истории.", + + "EffectiveDocumentLifecycle": "Жизненный цикл эффективного документа", + + "ReasonAndImpact": "Причина & Воздействие", + "ImpactAnalysis": "Анализ воздействия", + "ImpactedDocuments": "Подверженные документы", + + "CreateDocumentFailed": "Ошибка при создании документа", + "CreateDocumentTemplateFailed": "Ошибка при создании шаблона", + "TryAgain": "Пожалуйста, попробуйте снова", + + "DescribeChanges": "Опишите, что поменялось...", + "DescribeReason": "Опишите, почему это поменялось...", + "DescribeImpact": "Воздействует на...", + "AddDocument": "Добавить документ", + "NoDocuments": "Нет документов", + "SysTemplate": "Системный шаблон", + + "DocumentTrainingDueDays": "Срок", + "DocumentTrainingEnabled": "Включен", + "Own": "Собственный", + "Snapshot": "Мгновенный снимок", + "Snapshots": "Мгновенные снимки", + "ControlledSnapshot": "Контролируемый мгновенный снимок", + "Name": "Название", + "DraftRevision": "Ревизия рабочей копии", + "CreateNewDraft": "Создать новую рабочую копию", + "ChangeSeverity": "Серьезность изменений", + "Reference": "Референс", + "History": "История", + "Signatories": "Подписавшие", + "Page": "Страница ", + "Of": " из ", + "CreatedFromTemplate": "Создан из шаблона: ", + + "ViewAll": "Показать все", + "Readonly": "Только для чтения", + + "NewDocumentSpace": "Новое пространство документов", + "EditDocumentSpace": "Редактировать пространство документов", + + "DocSpaceDescriptionPlaceholder": "Опишите Ваше пространство...", + "Members": "Участники", + "RoleLabel": "Роль: {role}", + "CreateOrgSpace": "Создать пространство организации", + + "ReviewDocumentPermission": "Рецензировать документ", + "ReviewDocumentDescription": "Предоставляет пользователям разрешение рецензировать документ", + "ApproveDocumentPermission": "Утверждать документ", + "ApproveDocumentDescription": "Предоставляет пользователям разрешение утверждать документ", + "CoAuthorDocumentPermission": "Быть со-автором документа", + "CoAuthorDocumentDescription": "Предоставляет пользователям разрешение быть со-автором документа", + "CreateDocumentPermission": "Создавать документ", + "CreateDocumentDescription": "Предоставляет пользователям разрешение создавать документ", + "UpdateDocumentOwnerPermission": "Изменять владельца документа", + "UpdateDocumentOwnerDescription": "Предоставляет пользователям разрешение изменять владельца документа", + + "CreateDocumentCategoryPermission": "Создавать категорию", + "CreateDocumentCategoryDescription": "Предоставляет пользователям разрешение создавать категорию", + "UpdateDocumentCategoryPermission": "Обновлять категорию", + "UpdateDocumentCategoryDescription": "Предоставляет пользователям разрешение обновлять категорию", + "DeleteDocumentCategoryPermission": "Удалять категорию", + "DeleteDocumentCategoryDescription": "Предоставляет пользователям разрешение удалять категорию", + "ConfigLabel": "Управляемые Документы", + "ConfigDescription": "Расширение для управления управялемыми документами" + }, + "controlledDocStates": { + "Empty": "", + "Approved": "Утвержден", + "InApproval": "В процессе утверждения", + "InReview": "Рецензируется", + "Reviewed": "Рецензии получены", + "Rejected": "Отказано", + "ToReview": "Для рецензии" + } +} diff --git a/plugins/controlled-documents-assets/package.json b/plugins/controlled-documents-assets/package.json new file mode 100644 index 0000000000..79c193f407 --- /dev/null +++ b/plugins/controlled-documents-assets/package.json @@ -0,0 +1,38 @@ +{ + "name": "@hcengineering/controlled-documents-assets", + "version": "0.1.0", + "main": "src/index.ts", + "template": "@hcengineering/assets-package", + "scripts": { + "build": "compile", + "test": "jest --passWithNoTests --silent", + "build:docs": "", + "format": "format src", + "build:watch": "compile", + "_phase:build": "compile transpile src", + "_phase:test": "jest --passWithNoTests --silent", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.4.0", + "eslint-plugin-promise": "^6.1.1", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.8.0", + "prettier": "^3.1.0", + "@types/node": "~20.11.16", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/platform": "^0.6.9", + "@hcengineering/controlled-documents": "^0.1.0" + } +} diff --git a/plugins/controlled-documents-assets/src/__tests__/lang.test.ts b/plugins/controlled-documents-assets/src/__tests__/lang.test.ts new file mode 100644 index 0000000000..cf96c6dcf7 --- /dev/null +++ b/plugins/controlled-documents-assets/src/__tests__/lang.test.ts @@ -0,0 +1,6 @@ +import { makeLocalesTest } from '@hcengineering/platform' + +it( + 'Locales are equale', + makeLocalesTest((lang) => import(`../../lang/${lang}.json`)) +) diff --git a/plugins/controlled-documents-assets/src/index.ts b/plugins/controlled-documents-assets/src/index.ts new file mode 100644 index 0000000000..c0d0c5ea68 --- /dev/null +++ b/plugins/controlled-documents-assets/src/index.ts @@ -0,0 +1,37 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import documents from '@hcengineering/controlled-documents' +import { loadMetadata } from '@hcengineering/platform' + +const icons = require('../assets/icons.svg') as string // eslint-disable-line +loadMetadata(documents.icon, { + CheckmarkCircle: `${icons}#checkmark-circle`, + Approvals: `${icons}#approvals`, + DocumentApplication: `${icons}#documentapplication`, + NewDocument: `${icons}#newdocument`, + Document: `${icons}#document`, + Library: `${icons}#library`, + StateApproved: `${icons}#state-approved`, + StateDraft: `${icons}#state-draft`, + StateEffective: `${icons}#state-effective`, + StateRejected: `${icons}#state-rejected`, + StateObsolete: `${icons}#state-obsolete`, + ArrowUp: `${icons}#arrow-up`, + ArrowDown: `${icons}#arrow-down`, + Duplicate: `${icons}#duplicate`, + EditDescription: `${icons}#edit-description`, + EditGuidance: `${icons}#edit-guidance`, + Configure: `${icons}#configure` +}) diff --git a/plugins/controlled-documents-assets/tsconfig.json b/plugins/controlled-documents-assets/tsconfig.json new file mode 100644 index 0000000000..4449c73536 --- /dev/null +++ b/plugins/controlled-documents-assets/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/assets/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "types": ["node", "jest"], + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/plugins/controlled-documents-resources/.eslintrc.js b/plugins/controlled-documents-resources/.eslintrc.js new file mode 100644 index 0000000000..bb8fd7450d --- /dev/null +++ b/plugins/controlled-documents-resources/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'], + parserOptions: { tsconfigRootDir: __dirname } +} diff --git a/plugins/controlled-documents-resources/.prettierrc b/plugins/controlled-documents-resources/.prettierrc new file mode 100644 index 0000000000..792942803a --- /dev/null +++ b/plugins/controlled-documents-resources/.prettierrc @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "trailingComma": "none", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "printWidth": 120, + "useTabs": false, + "bracketSpacing": true, + "proseWrap": "preserve", + "plugins": [ + "prettier-plugin-svelte" + ], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} \ No newline at end of file diff --git a/plugins/controlled-documents-resources/config/rig.json b/plugins/controlled-documents-resources/config/rig.json new file mode 100644 index 0000000000..1a4b944f83 --- /dev/null +++ b/plugins/controlled-documents-resources/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "ui" +} diff --git a/plugins/controlled-documents-resources/jest.config.js b/plugins/controlled-documents-resources/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/plugins/controlled-documents-resources/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/plugins/controlled-documents-resources/package.json b/plugins/controlled-documents-resources/package.json new file mode 100644 index 0000000000..507def5a22 --- /dev/null +++ b/plugins/controlled-documents-resources/package.json @@ -0,0 +1,74 @@ +{ + "name": "@hcengineering/controlled-documents-resources", + "version": "0.1.0", + "main": "src/index.ts", + "scripts": { + "build": "compile ui", + "build:docs": "api-extractor run --local", + "format": "format src", + "svelte-check": "do-svelte-check", + "_phase:svelte-check": "do-svelte-check", + "build:watch": "compile ui", + "_phase:build": "compile ui", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "svelte-loader": "^3.2.0", + "sass": "^1.53.0", + "svelte-preprocess": "^5.1.3", + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.4.0", + "eslint-plugin-promise": "^6.1.1", + "prettier-plugin-svelte": "^3.2.2", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.8.0", + "prettier": "^3.1.0", + "svelte-check": "^3.6.9", + "typescript": "^5.3.3", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5", + "eslint-plugin-svelte": "^2.35.1", + "svelte-eslint-parser": "^0.33.1" + }, + "dependencies": { + "@hcengineering/platform": "^0.6.9", + "@hcengineering/login": "^0.6.8", + "@hcengineering/ui": "^0.6.11", + "@hcengineering/setting": "^0.6.11", + "@hcengineering/presentation": "^0.6.2", + "@hcengineering/core": "^0.6.28", + "@hcengineering/view": "^0.6.9", + "@hcengineering/attachment-resources": "^0.6.0", + "@hcengineering/notification-resources": "^0.6.0", + "@hcengineering/panel": "^0.6.15", + "@hcengineering/view-resources": "^0.6.0", + "@hcengineering/attachment": "^0.6.9", + "@hcengineering/notification": "^0.6.16", + "@hcengineering/contact": "^0.6.20", + "@hcengineering/contact-resources": "^0.6.0", + "@hcengineering/tags": "^0.6.12", + "@hcengineering/text-editor": "^0.6.0", + "@hcengineering/activity": "^0.6.0", + "@hcengineering/request": "^0.6.6", + "@hcengineering/request-resources": "^0.6.0", + "@hcengineering/chunter": "^0.6.12", + "@hcengineering/chunter-resources": "^0.6.0", + "@hcengineering/workbench": "^0.6.9", + "@hcengineering/theme": "^0.6.3", + "@hcengineering/controlled-documents": "^0.1.0", + "@hcengineering/training": "^0.1.0", + "@hcengineering/training-resources": "^0.1.0", + "@tiptap/core": "^2.2.4", + "effector": "~22.8.7", + "svelte": "^4.2.12", + "yjs": "^13.5.52", + "slugify": "^1.6.6", + "fast-equals": "^5.0.1" + } +} diff --git a/plugins/controlled-documents-resources/postcss.config.js b/plugins/controlled-documents-resources/postcss.config.js new file mode 100644 index 0000000000..88752c6cb0 --- /dev/null +++ b/plugins/controlled-documents-resources/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: [ + require('autoprefixer') + ] +} diff --git a/plugins/controlled-documents-resources/src/components/Categories.svelte b/plugins/controlled-documents-resources/src/components/Categories.svelte new file mode 100644 index 0000000000..0b47af8872 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/Categories.svelte @@ -0,0 +1,89 @@ + + + + +
+ +
+ {#if canCreate} +
+
+ + {#if loading} + + {:else if viewlet && viewOptions} + + {/if} +
diff --git a/plugins/controlled-documents-resources/src/components/CreateDocument.svelte b/plugins/controlled-documents-resources/src/components/CreateDocument.svelte new file mode 100644 index 0000000000..d15be6a931 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/CreateDocument.svelte @@ -0,0 +1,176 @@ + + + + + + 0 && (templateId !== undefined || !isTemplateMandatory)} + on:close={() => { + dispatch('close') + }} +> + + + +
+ +
+
diff --git a/plugins/controlled-documents-resources/src/components/CreateDocumentCategory.svelte b/plugins/controlled-documents-resources/src/components/CreateDocumentCategory.svelte new file mode 100644 index 0000000000..9e5f981710 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/CreateDocumentCategory.svelte @@ -0,0 +1,216 @@ + + + + { + dispatch('close') + }} +> +
+
+ { + code = _title + .split(' ') + .map((it) => it[0]?.toUpperCase()) + .filter((it) => it) + .join('') + }} + /> +
+ {#if isTitleInUse} +
+ +
+ {/if} +
+
+
+ { + code = code.trim() + }} + /> +
+ {#if isCodeInUse} +
+ +
+ {/if} +
+
+
+
+ { + if (ev.detail.size > 0) attachments = ev.detail.values + else if (ev.detail.size === 0 && ev.detail.values === true) { + attachments.clear() + attachments = attachments + } + }} + /> +
+ + {#if attachments.size > 0} + {#each Array.from(attachments.values()) as attachment} + { + if (result.detail !== undefined) descriptionBox.removeAttachmentById(result.detail._id) + }} + /> + {/each} + {/if} + + + + + diff --git a/plugins/controlled-documents-resources/src/components/Documents.svelte b/plugins/controlled-documents-resources/src/components/Documents.svelte new file mode 100644 index 0000000000..1d7e4a415e --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/Documents.svelte @@ -0,0 +1,56 @@ + + + +
+ {#if viewlet !== undefined && viewOptions} + + {/if} + {#if $$slots.aside !== undefined && asideShown} +
+ +
+ {/if} +
diff --git a/plugins/controlled-documents-resources/src/components/DocumentsContainer.svelte b/plugins/controlled-documents-resources/src/components/DocumentsContainer.svelte new file mode 100644 index 0000000000..90c26d66a1 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/DocumentsContainer.svelte @@ -0,0 +1,80 @@ + + + + diff --git a/plugins/controlled-documents-resources/src/components/DocumentsPopup.svelte b/plugins/controlled-documents-resources/src/components/DocumentsPopup.svelte new file mode 100644 index 0000000000..8cad1e3b78 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/DocumentsPopup.svelte @@ -0,0 +1,93 @@ + + + + + dispatch('close', doc.detail)} + {readonly} +> + +
+ +
+
+ + + {@const cl = hierarchy.getClass(doc._class)} + + +
diff --git a/plugins/controlled-documents-resources/src/components/EditDoc.svelte b/plugins/controlled-documents-resources/src/components/EditDoc.svelte new file mode 100644 index 0000000000..0928235862 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/EditDoc.svelte @@ -0,0 +1,62 @@ + + + +{#if project !== undefined} + +{:else} + +{/if} diff --git a/plugins/controlled-documents-resources/src/components/EditDocPanel.svelte b/plugins/controlled-documents-resources/src/components/EditDocPanel.svelte new file mode 100644 index 0000000000..9e715a230e --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/EditDocPanel.svelte @@ -0,0 +1,450 @@ + + + +{#if $controlledDocument !== null && collaborativeDoc !== undefined} + rightPanelTabChanged(ev.detail)} + > + +
+ {#if project !== documents.ids.NoProject} + { + project = e.detail + }} + /> + {/if} + +
+
+ +
+ {#if $isProjectEditable} + {#if $isDocumentOwner && !$documentReviewIsActive && !$documentApprovalIsActive} + {#if $canSendForReview} +
+
+ +
+
+
+ + + + + + + {#if $editorMode === 'comparing'} + + {:else} + + {/if} + + + + +
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/EditDocumentCategory.svelte b/plugins/controlled-documents-resources/src/components/EditDocumentCategory.svelte new file mode 100644 index 0000000000..39964bd879 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/EditDocumentCategory.svelte @@ -0,0 +1,87 @@ + + + + + + saveField(evt, category, 'code')} + /> + +
-
+ + saveField(evt, title, 'title')} + /> +
+ + {#if !canEdit} + + {:else} + + {/if} +
diff --git a/plugins/controlled-documents-resources/src/components/EditProjectDoc.svelte b/plugins/controlled-documents-resources/src/components/EditProjectDoc.svelte new file mode 100644 index 0000000000..bade0c058c --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/EditProjectDoc.svelte @@ -0,0 +1,58 @@ + + + +{#if doc !== undefined && controlledDoc !== undefined} + +{:else} + +{/if} diff --git a/plugins/controlled-documents-resources/src/components/FailedToCreateDocument.svelte b/plugins/controlled-documents-resources/src/components/FailedToCreateDocument.svelte new file mode 100644 index 0000000000..f011d98582 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/FailedToCreateDocument.svelte @@ -0,0 +1,32 @@ + + + + + + + + diff --git a/plugins/controlled-documents-resources/src/components/FieldSectionEditor.svelte b/plugins/controlled-documents-resources/src/components/FieldSectionEditor.svelte new file mode 100644 index 0000000000..585502da69 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/FieldSectionEditor.svelte @@ -0,0 +1,120 @@ + + + +
+ + +
+
+ +
+
+
+
+ +
+ + + + {#if sectionType} + +
+ +
+
+ +
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff --git a/plugins/controlled-documents-resources/src/components/MyDocuments.svelte b/plugins/controlled-documents-resources/src/components/MyDocuments.svelte new file mode 100644 index 0000000000..4822905ecb --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/MyDocuments.svelte @@ -0,0 +1,28 @@ + + + dispatch('action', event.detail)} +/> diff --git a/plugins/controlled-documents-resources/src/components/NewDocumentHeader.svelte b/plugins/controlled-documents-resources/src/components/NewDocumentHeader.svelte new file mode 100644 index 0000000000..cd1e925209 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/NewDocumentHeader.svelte @@ -0,0 +1,89 @@ + + + +
+ {#if dropdownItems.length > 0} + { + dropdownItemSelected(ev.detail) + }} + /> + {:else} +
diff --git a/plugins/controlled-documents-resources/src/components/SignatureDialog.svelte b/plugins/controlled-documents-resources/src/components/SignatureDialog.svelte new file mode 100644 index 0000000000..36ec1ee8c7 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/SignatureDialog.svelte @@ -0,0 +1,151 @@ + + + + +
+ object.email.trim()} + disabled={disableEmailField} + /> + object.email.trim()} + /> + {#if isRejection} +
+ { + rejectionNote = detail + void validate() + }} + /> +
+ {/if} +
+ +
+ +
+
diff --git a/plugins/controlled-documents-resources/src/components/TeamPopup.svelte b/plugins/controlled-documents-resources/src/components/TeamPopup.svelte new file mode 100644 index 0000000000..8ad31df9b6 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/TeamPopup.svelte @@ -0,0 +1,118 @@ + + + + +
+
+
+
+
+ {users?.length} +
+
+ (users = detail)} + /> +
+
+
+
+ + diff --git a/plugins/controlled-documents-resources/src/components/activity/TxCreateApprovalRequest.svelte b/plugins/controlled-documents-resources/src/components/activity/TxCreateApprovalRequest.svelte new file mode 100644 index 0000000000..507dbed9bd --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/activity/TxCreateApprovalRequest.svelte @@ -0,0 +1,40 @@ + + + +{#if controlledDoc} + + +{/if} diff --git a/plugins/controlled-documents-resources/src/components/activity/TxCreateReviewRequest.svelte b/plugins/controlled-documents-resources/src/components/activity/TxCreateReviewRequest.svelte new file mode 100644 index 0000000000..ee4a83d86e --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/activity/TxCreateReviewRequest.svelte @@ -0,0 +1,40 @@ + + + +{#if controlledDoc} + + +{/if} diff --git a/plugins/controlled-documents-resources/src/components/category/popups/DeleteCategoryPopup.svelte b/plugins/controlled-documents-resources/src/components/category/popups/DeleteCategoryPopup.svelte new file mode 100644 index 0000000000..6de358ba02 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/category/popups/DeleteCategoryPopup.svelte @@ -0,0 +1,82 @@ + + + + +{#if object} +
+
+
+
+ {#if canDelete} +
+
+ {/if} + {#if !canDelete} +
+ +
+ {/if} +
+ +
+
+
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/category/presenters/CategoryPresenter.svelte b/plugins/controlled-documents-resources/src/components/category/presenters/CategoryPresenter.svelte new file mode 100644 index 0000000000..bfcf1578a4 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/category/presenters/CategoryPresenter.svelte @@ -0,0 +1,73 @@ + + + +{#if category} + +
+ +
+ {category.code} +
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/create-doc/QmsDocumentWizard.svelte b/plugins/controlled-documents-resources/src/components/create-doc/QmsDocumentWizard.svelte new file mode 100644 index 0000000000..0dc46d6281 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/create-doc/QmsDocumentWizard.svelte @@ -0,0 +1,225 @@ + + + + +
+ +
+
+ + diff --git a/plugins/controlled-documents-resources/src/components/create-doc/QmsTemplateWizard.svelte b/plugins/controlled-documents-resources/src/components/create-doc/QmsTemplateWizard.svelte new file mode 100644 index 0000000000..398886838d --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/create-doc/QmsTemplateWizard.svelte @@ -0,0 +1,242 @@ + + + + +
+ +
+
+ + diff --git a/plugins/controlled-documents-resources/src/components/create-doc/steps/InfoStep.svelte b/plugins/controlled-documents-resources/src/components/create-doc/steps/InfoStep.svelte new file mode 100644 index 0000000000..5d96b4d7de --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/create-doc/steps/InfoStep.svelte @@ -0,0 +1,336 @@ + + + + +{#if ccRecord !== undefined} +
+
+
+
+ + {#if codeNotUnique} +
+ +
+ {/if} +
+
+
+ { + if (isTemplate) { + docObject.docPrefix = + docObject.title + .split(' ') + .map((it) => it[0]?.toUpperCase()) + .filter((it) => it) + .join('') ?? '' + } + }} + /> +
+
+ +
+ {#if isTemplate} +
+
+ + {#if prefixNotUnique && docObject.docPrefix !== undefined} +
+ +
+ {/if} +
+
+
+ ({ id: cat._id, label: cat.title }))} + showDropdownIcon + bind:selected={docObject.category} + /> +
+ {/if} +
+
+ +
+ {#if $infoStep.selectedReason === 'custom'} +
+ { + handleReasonUpdated(e.detail) + }} + /> +
+ {/if} +
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/create-doc/steps/LocationStep.svelte b/plugins/controlled-documents-resources/src/components/create-doc/steps/LocationStep.svelte new file mode 100644 index 0000000000..ba06880fe8 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/create-doc/steps/LocationStep.svelte @@ -0,0 +1,174 @@ + + + + +
+
+
+
+ +
+ {#if space && spaceType && spaceType.projects} +
+ { + handleProjectSelected(e.detail) + }} + /> +
+ {/if} +
+ {#if hasParentSelector} +
+
+
+ {#if space} + { + handleParentSelected(e.detail) + }} + /> + {/if} +
+ {/if} +
+ + diff --git a/plugins/controlled-documents-resources/src/components/create-doc/steps/TeamStep.svelte b/plugins/controlled-documents-resources/src/components/create-doc/steps/TeamStep.svelte new file mode 100644 index 0000000000..e7fd88f411 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/create-doc/steps/TeamStep.svelte @@ -0,0 +1,52 @@ + + + + +{#if docObject !== undefined} +
+ +
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/create-doc/steps/TemplateStep.svelte b/plugins/controlled-documents-resources/src/components/create-doc/steps/TemplateStep.svelte new file mode 100644 index 0000000000..e48728cd82 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/create-doc/steps/TemplateStep.svelte @@ -0,0 +1,218 @@ + + + + +{#if docObject !== undefined} +
+ {#each categoriesIds as catId} + {@const expanded = $templateStep.collapsedCategories[catId] === undefined} +
+ + +
{ + handleExpanderToggled(catId) + }} + > +
+ +
+ {categoriesById[catId]?.title} +
+ + {#if expanded} +
+ {#each templatesByCategory[catId] as tmp} + { + handleTemplateSelected(tmp._id, tmp.docPrefix, tmp.sequence + 1) + }} + > +
+
{`${tmp.docPrefix} (${tmp.title})`}
+ {#if tmp.abstract} +
{tmp.abstract}
+ {/if} +
+
+ {/each} +
+ {/if} +
+ {/each} +
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/docspace/CreateDocumentsSpace.svelte b/plugins/controlled-documents-resources/src/components/docspace/CreateDocumentsSpace.svelte new file mode 100644 index 0000000000..ad3fa8fc10 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/docspace/CreateDocumentsSpace.svelte @@ -0,0 +1,329 @@ + + + + +
+
+
+
+ + +
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+ +
+ + {#each roles as role} +
+
+
+ { + handleRoleAssignmentChanged(role._id, refs) + }} + kind={'regular'} + size={'large'} + /> +
+ {/each} +
+
diff --git a/plugins/controlled-documents-resources/src/components/document/DocSectionEditor.svelte b/plugins/controlled-documents-resources/src/components/document/DocSectionEditor.svelte new file mode 100644 index 0000000000..022dc2caf6 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocSectionEditor.svelte @@ -0,0 +1,204 @@ + + + + documentSectionToggled(value._id)} +> + + + + + {index + 1} + + + + +
+ {#if $isEditable && !dragging} + updateSectionTitle(title)} /> + {:else} + {title} + {/if} +
+
+ +
+ {#if $canAddDocumentComments} +
+
+ + {#if isEditingDescription || descr.length > 0} +
+ +
+ {/if} +
+ + + +
diff --git a/plugins/controlled-documents-resources/src/components/document/DocTeam.svelte b/plugins/controlled-documents-resources/src/components/document/DocTeam.svelte new file mode 100644 index 0000000000..4a04aefd41 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocTeam.svelte @@ -0,0 +1,197 @@ + + + +
+
+
+
+ {reviewers?.length} +
+
+ { + handleUsersUpdated('coAuthors', detail) + }} + /> +
+
+
+
+
+ {reviewers?.length} +
+
+ { + handleUsersUpdated('reviewers', detail) + }} + /> +
+
+
+
+
+ {approvers?.length} +
+
+ { + handleUsersUpdated('approvers', detail) + }} + /> +
+
+ + diff --git a/plugins/controlled-documents-resources/src/components/document/DocumentDiffViewer.svelte b/plugins/controlled-documents-resources/src/components/document/DocumentDiffViewer.svelte new file mode 100644 index 0000000000..c05494b709 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocumentDiffViewer.svelte @@ -0,0 +1,202 @@ + + +
+
+{#if loading || $isLoadPending} + +{:else} + +
+
+ + + +
+ {#each sectionDiffPairs as pair, index} + { + if (collapsedPairIndices.has(index)) { + collapsedPairIndices.delete(index) + } else { + collapsedPairIndices.add(index) + } + collapsedPairIndices = new Set(collapsedPairIndices) + }} + /> + {/each} +
+
+{/if} diff --git a/plugins/controlled-documents-resources/src/components/document/DocumentHistory.svelte b/plugins/controlled-documents-resources/src/components/document/DocumentHistory.svelte new file mode 100644 index 0000000000..ea9293a2ec --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocumentHistory.svelte @@ -0,0 +1,136 @@ + + + + +
+ {#if orderedVersions.length > 0} +
+ {#each orderedVersions as version} +
+
+
+ {getDocumentVersionString(version)} +
+ {#if version.effectiveDate !== undefined} +
+ {new Date(version.effectiveDate).toLocaleDateString('default', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} +
+ {/if} +
+
+ {getDescription(changeControls[version.changeControl])} +
+
+ {/each} +
+ {:else} +
+
+ + diff --git a/plugins/controlled-documents-resources/src/components/document/DocumentHistoryTab.svelte b/plugins/controlled-documents-resources/src/components/document/DocumentHistoryTab.svelte new file mode 100644 index 0000000000..4cfc877a1e --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocumentHistoryTab.svelte @@ -0,0 +1,27 @@ + + + +
+ +
+ + diff --git a/plugins/controlled-documents-resources/src/components/document/DocumentSectionPairDiffViewer.svelte b/plugins/controlled-documents-resources/src/components/document/DocumentSectionPairDiffViewer.svelte new file mode 100644 index 0000000000..acd7bd440b --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocumentSectionPairDiffViewer.svelte @@ -0,0 +1,46 @@ + + + + + + + + + + + {#if _class && hierarchy.isDerived(_class, documents.class.CollaborativeDocumentSection)} + + {:else} + {plugin.string.ComparisonModeNotSupported} + {/if} + + diff --git a/plugins/controlled-documents-resources/src/components/document/DocumentSignatories.svelte b/plugins/controlled-documents-resources/src/components/document/DocumentSignatories.svelte new file mode 100644 index 0000000000..74351ba58a --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocumentSignatories.svelte @@ -0,0 +1,215 @@ + + + + +
+
+ {#each signers as signer} +
+
+
+
+ +
+ {signer.date} +
+
+
+
+ {signer.name} +
+
+ {signer.id} +
+
+
+ {/each} +
+
+
+ + diff --git a/plugins/controlled-documents-resources/src/components/document/DocumentTitle.svelte b/plugins/controlled-documents-resources/src/components/document/DocumentTitle.svelte new file mode 100644 index 0000000000..b27af12a23 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocumentTitle.svelte @@ -0,0 +1,10 @@ +
+ +
+ + diff --git a/plugins/controlled-documents-resources/src/components/document/DocumentsContent.svelte b/plugins/controlled-documents-resources/src/components/document/DocumentsContent.svelte new file mode 100644 index 0000000000..7ef56b80c2 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/DocumentsContent.svelte @@ -0,0 +1,35 @@ + + + +{#if viewlet?.$lookup?.descriptor?.component} + +{/if} diff --git a/plugins/controlled-documents-resources/src/components/document/EditDocContent.svelte b/plugins/controlled-documents-resources/src/components/document/EditDocContent.svelte new file mode 100644 index 0000000000..8ef32e75c9 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/EditDocContent.svelte @@ -0,0 +1,274 @@ + + + +{#if $controlledDocument} + + +
+
+ handleShowHeading(ev.detail)} /> +
+ {#if headings.length > 0} +
+ +
+
+ {/if} + +
+
+ + {#if $isEditable} + { + title = event.detail + }} + on:blur={handleUpdateTitle} + /> + {:else} + {$controlledDocument.title} + {/if} + +
+ {#if $sections} + {#each $sections as section, i (section._id)} + +
{ + handleDragStart(ev, section._id) + }} + on:dragleave|preventDefault={() => { + if (dragOverId === section._id) dragOverId = null + return false + }} + on:dragover|preventDefault={() => { + dragOverId = section._id + return false + }} + on:dragend={resetDrag} + on:drop|preventDefault={(ev) => handleDrop(ev, section._id)} + animate:flip={{ duration: 400 }} + > + +
+ + +
{ + openSectionMenu(ev, section) + }} + > + +
+
+
+
+ {/each} + {/if} +
+
+
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/document/EditDocReasonAndImpact.svelte b/plugins/controlled-documents-resources/src/components/document/EditDocReasonAndImpact.svelte new file mode 100644 index 0000000000..6a2fbfb0a0 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/EditDocReasonAndImpact.svelte @@ -0,0 +1,173 @@ + + + +{#if $controlledDocument !== undefined && changeControl} + +
+
+
+
+ {#if $isEditable} + { + void handleFieldUpdated('description', ev) + }} + /> + {:else} + {changeControl.description ?? '—'} + {/if} +
+ +
+
+
+ {#if $isEditable} + { + void handleFieldUpdated('reason', ev) + }} + /> + {:else} + {changeControl.reason ?? '—'} + {/if} +
+ +
+
+
+ {#if $isEditable} + { + void handleFieldUpdated('impact', ev) + }} + /> + {:else} + {changeControl.impact ?? '—'} + {/if} +
+ +
+
+
+ {#if $isEditable || changeControl.impactedDocuments.length > 0} + updateCCField('impactedDocuments', detail)} + /> + {:else} +
+
+
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/document/EditDocRelease.svelte b/plugins/controlled-documents-resources/src/components/document/EditDocRelease.svelte new file mode 100644 index 0000000000..44456f32e4 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/EditDocRelease.svelte @@ -0,0 +1,428 @@ + + + + +
+
+
+
+
+ { + void handleSeverityChanged(Severity.Minor) + }} + gap="large" + /> + { + void handleSeverityChanged(Severity.Major) + }} + gap="none" + /> +
+
+
+ + +
+
+ { + selected = documentsRes.string.EffectiveImmediately + changePlannedEffectiveDate(0) + }} + gap="large" + /> + { + selected = documentsRes.string.EffectiveOn + changePlannedEffectiveDate(selectedDate) + }} + gap="none" + > +
+
+
+
+
+
+ + + changePeriodicReviewInterval(detail)} + disabled={!canEdit} + /> + + +
+
+ +
+
+ + + + toggleTraining(event.detail)} + /> +
+ + {#if $documentTraining !== null && $documentTraining.enabled} + {@const trainingAttribute = hierarchy.getAttribute(documentTrainingClass._id, 'training')} + + + { + void updateTraining({ training: trainingRef ?? null }) + }} + /> + + {@const rolesAttribute = hierarchy.getAttribute(documentTrainingClass._id, 'roles')} + + + { + void updateTraining({ roles }) + }} + /> + + {@const traineesAttribute = hierarchy.getAttribute(documentTrainingClass._id, 'trainees')} + + + { + void updateTraining({ trainees: event.detail }) + }} + /> + +
+ + + { + void updateTraining({ maxAttempts }) + }} + /> +
+ {/if} +
+
+
+ + diff --git a/plugins/controlled-documents-resources/src/components/document/EditDocTeam.svelte b/plugins/controlled-documents-resources/src/components/document/EditDocTeam.svelte new file mode 100644 index 0000000000..a1014dd9c6 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/EditDocTeam.svelte @@ -0,0 +1,69 @@ + + + +{#if controlledDoc} + +
+ +
+
+{/if} + + diff --git a/plugins/controlled-documents-resources/src/components/document/common/DocumentStatusTag.svelte b/plugins/controlled-documents-resources/src/components/document/common/DocumentStatusTag.svelte new file mode 100644 index 0000000000..5939ec0f64 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/common/DocumentStatusTag.svelte @@ -0,0 +1,72 @@ + + +
+ +
+ + diff --git a/plugins/controlled-documents-resources/src/components/document/editors/AbstractEditor.svelte b/plugins/controlled-documents-resources/src/components/document/editors/AbstractEditor.svelte new file mode 100644 index 0000000000..20c1e04ac9 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/editors/AbstractEditor.svelte @@ -0,0 +1,58 @@ + + + + +{#if value} + { + abstract = event.detail + }} + on:blur={handleUpdateAbstract} + /> +{/if} diff --git a/plugins/controlled-documents-resources/src/components/document/editors/AttachmentsSectionEditor.svelte b/plugins/controlled-documents-resources/src/components/document/editors/AttachmentsSectionEditor.svelte new file mode 100644 index 0000000000..52bf556a46 --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/editors/AttachmentsSectionEditor.svelte @@ -0,0 +1,50 @@ + + + +{#if withScroll} + +
+ +
+
+{:else} + +{/if} diff --git a/plugins/controlled-documents-resources/src/components/document/editors/CollaborativeSectionEditor.svelte b/plugins/controlled-documents-resources/src/components/document/editors/CollaborativeSectionEditor.svelte new file mode 100644 index 0000000000..adbb98715b --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/editors/CollaborativeSectionEditor.svelte @@ -0,0 +1,300 @@ + + + +{#if collaborativeDoc} + {#key value._id} + { + const doc = await client.findOne(event.detail._class, { _id: event.detail._id }) + if (doc != null) { + const location = await getObjectLinkFragment(client.getHierarchy(), doc, {}, view.component.EditDoc) + navigate(location) + } + }} + attachFile={async (file) => { + return await createEmbedding(file, value) + }} + /> + {/key} +{/if} diff --git a/plugins/controlled-documents-resources/src/components/document/editors/DescriptionEditor.svelte b/plugins/controlled-documents-resources/src/components/document/editors/DescriptionEditor.svelte new file mode 100644 index 0000000000..2660cc6e6e --- /dev/null +++ b/plugins/controlled-documents-resources/src/components/document/editors/DescriptionEditor.svelte @@ -0,0 +1,87 @@ + + + +
+