mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 02:51:54 +03:00
Hr (#2100)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
da07828036
commit
5e38409a88
@ -41,6 +41,9 @@ specifiers:
|
||||
'@rush-temp/gmail': file:./projects/gmail.tgz
|
||||
'@rush-temp/gmail-assets': file:./projects/gmail-assets.tgz
|
||||
'@rush-temp/gmail-resources': file:./projects/gmail-resources.tgz
|
||||
'@rush-temp/hr': file:./projects/hr.tgz
|
||||
'@rush-temp/hr-assets': file:./projects/hr-assets.tgz
|
||||
'@rush-temp/hr-resources': file:./projects/hr-resources.tgz
|
||||
'@rush-temp/image-cropper': file:./projects/image-cropper.tgz
|
||||
'@rush-temp/image-cropper-resources': file:./projects/image-cropper-resources.tgz
|
||||
'@rush-temp/inventory': file:./projects/inventory.tgz
|
||||
@ -65,6 +68,7 @@ specifiers:
|
||||
'@rush-temp/model-core': file:./projects/model-core.tgz
|
||||
'@rush-temp/model-demo': file:./projects/model-demo.tgz
|
||||
'@rush-temp/model-gmail': file:./projects/model-gmail.tgz
|
||||
'@rush-temp/model-hr': file:./projects/model-hr.tgz
|
||||
'@rush-temp/model-inventory': file:./projects/model-inventory.tgz
|
||||
'@rush-temp/model-lead': file:./projects/model-lead.tgz
|
||||
'@rush-temp/model-notification': file:./projects/model-notification.tgz
|
||||
@ -79,6 +83,7 @@ specifiers:
|
||||
'@rush-temp/model-server-contact': file:./projects/model-server-contact.tgz
|
||||
'@rush-temp/model-server-core': file:./projects/model-server-core.tgz
|
||||
'@rush-temp/model-server-gmail': file:./projects/model-server-gmail.tgz
|
||||
'@rush-temp/model-server-hr': file:./projects/model-server-hr.tgz
|
||||
'@rush-temp/model-server-inventory': file:./projects/model-server-inventory.tgz
|
||||
'@rush-temp/model-server-lead': file:./projects/model-server-lead.tgz
|
||||
'@rush-temp/model-server-notification': file:./projects/model-server-notification.tgz
|
||||
@ -131,6 +136,8 @@ specifiers:
|
||||
'@rush-temp/server-core': file:./projects/server-core.tgz
|
||||
'@rush-temp/server-gmail': file:./projects/server-gmail.tgz
|
||||
'@rush-temp/server-gmail-resources': file:./projects/server-gmail-resources.tgz
|
||||
'@rush-temp/server-hr': file:./projects/server-hr.tgz
|
||||
'@rush-temp/server-hr-resources': file:./projects/server-hr-resources.tgz
|
||||
'@rush-temp/server-inventory': file:./projects/server-inventory.tgz
|
||||
'@rush-temp/server-inventory-resources': file:./projects/server-inventory-resources.tgz
|
||||
'@rush-temp/server-lead': file:./projects/server-lead.tgz
|
||||
@ -331,6 +338,9 @@ dependencies:
|
||||
'@rush-temp/gmail': file:projects/gmail.tgz
|
||||
'@rush-temp/gmail-assets': file:projects/gmail-assets.tgz
|
||||
'@rush-temp/gmail-resources': file:projects/gmail-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c
|
||||
'@rush-temp/hr': file:projects/hr.tgz
|
||||
'@rush-temp/hr-assets': file:projects/hr-assets.tgz_typescript@4.7.2
|
||||
'@rush-temp/hr-resources': file:projects/hr-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c
|
||||
'@rush-temp/image-cropper': file:projects/image-cropper.tgz
|
||||
'@rush-temp/image-cropper-resources': file:projects/image-cropper-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c
|
||||
'@rush-temp/inventory': file:projects/inventory.tgz
|
||||
@ -355,6 +365,7 @@ dependencies:
|
||||
'@rush-temp/model-core': file:projects/model-core.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-demo': file:projects/model-demo.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-gmail': file:projects/model-gmail.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-hr': file:projects/model-hr.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-inventory': file:projects/model-inventory.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-lead': file:projects/model-lead.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-notification': file:projects/model-notification.tgz_typescript@4.7.2
|
||||
@ -369,6 +380,7 @@ dependencies:
|
||||
'@rush-temp/model-server-contact': file:projects/model-server-contact.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-server-core': file:projects/model-server-core.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-server-gmail': file:projects/model-server-gmail.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-server-hr': file:projects/model-server-hr.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-server-inventory': file:projects/model-server-inventory.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-server-lead': file:projects/model-server-lead.tgz_typescript@4.7.2
|
||||
'@rush-temp/model-server-notification': file:projects/model-server-notification.tgz_typescript@4.7.2
|
||||
@ -421,6 +433,8 @@ dependencies:
|
||||
'@rush-temp/server-core': file:projects/server-core.tgz
|
||||
'@rush-temp/server-gmail': file:projects/server-gmail.tgz
|
||||
'@rush-temp/server-gmail-resources': file:projects/server-gmail-resources.tgz
|
||||
'@rush-temp/server-hr': file:projects/server-hr.tgz
|
||||
'@rush-temp/server-hr-resources': file:projects/server-hr-resources.tgz
|
||||
'@rush-temp/server-inventory': file:projects/server-inventory.tgz
|
||||
'@rush-temp/server-inventory-resources': file:projects/server-inventory-resources.tgz
|
||||
'@rush-temp/server-lead': file:projects/server-lead.tgz
|
||||
@ -10925,6 +10939,89 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/hr-assets.tgz_typescript@4.7.2:
|
||||
resolution: {integrity: sha512-Or7JUoPjJeVKIbJt6RExTLlWhvCJxWe1dZBA0332S7HIUgWDnev8Qx0EzBxV1FKXne/XV1w8ZEusJXPReGz3mw==, tarball: file:projects/hr-assets.tgz}
|
||||
id: file:projects/hr-assets.tgz
|
||||
name: '@rush-temp/hr-assets'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.45.5
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@types/node': 16.11.38
|
||||
'@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5
|
||||
'@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a
|
||||
eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.6.2
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/hr-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c:
|
||||
resolution: {integrity: sha512-sYnkYAs2h/gH6VD8c6+QBHMemRZfIMVthpYzi1+TXliv/EmebzxdFyPCPjLbxgQxpq4mUwuMLlAusMIrmpVNrg==, tarball: file:projects/hr-resources.tgz}
|
||||
id: file:projects/hr-resources.tgz
|
||||
name: '@rush-temp/hr-resources'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5
|
||||
'@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a
|
||||
eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
eslint-plugin-svelte3: 4.0.0_eslint@7.32.0+svelte@3.48.0
|
||||
prettier: 2.6.2
|
||||
prettier-plugin-svelte: 2.7.0_prettier@2.6.2+svelte@3.48.0
|
||||
sass: 1.52.2
|
||||
svelte: 3.48.0
|
||||
svelte-check: 2.7.2_c1788f0bf13b393830d6c30602bd01af
|
||||
svelte-loader: 3.1.3_svelte@3.48.0
|
||||
svelte-preprocess: 4.10.6_0757fe126296bf9639251bcd13609b29
|
||||
typescript: 4.7.2
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- coffeescript
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- less
|
||||
- node-sass
|
||||
- postcss
|
||||
- postcss-load-config
|
||||
- pug
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/hr.tgz:
|
||||
resolution: {integrity: sha512-8jSKJNCIhxfPjP8hislK8NLocwO4VwDJ/jjYzq6tlRwgiI3pMAuftX/NsZEKARgHcHebssy63acqJNImdp2ALQ==, tarball: file:projects/hr.tgz}
|
||||
name: '@rush-temp/hr'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.45.5
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5
|
||||
'@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a
|
||||
eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.6.2
|
||||
typescript: 4.7.2
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/image-cropper-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c:
|
||||
resolution: {integrity: sha512-8whE1ZAsPDruwYl6MT6HEWi5zFPGLg3fsYmvdDL2cx1KmDH421bm+M5kkq+Ok5HUV8jYoQRK00zvX8Awz4e+lQ==, tarball: file:projects/image-cropper-resources.tgz}
|
||||
id: file:projects/image-cropper-resources.tgz
|
||||
@ -11318,7 +11415,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-all.tgz_typescript@4.7.2:
|
||||
resolution: {integrity: sha512-3KLZ8VdyN6qL7q5SOOeD9sU9xUoDeqJRxAbyLtFhAgaTU1tX9n3C+cnmlDZhXq8kcK/Ss/xK2hrHfq0kOI7ILw==, tarball: file:projects/model-all.tgz}
|
||||
resolution: {integrity: sha512-VrR3QpQKo2qizq7xi/uS9Jj+OyrB/mPDLnAc8/pvwEx8jF5PwySSsP63uSNGcO3fwv7ZjZvAiuvEnaIHUZ5Ivg==, tarball: file:projects/model-all.tgz}
|
||||
id: file:projects/model-all.tgz
|
||||
name: '@rush-temp/model-all'
|
||||
version: 0.0.0
|
||||
@ -11528,6 +11625,29 @@ packages:
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-hr.tgz_typescript@4.7.2:
|
||||
resolution: {integrity: sha512-HtAgUigvoyvjSpGUHwcRfTqM+RpMKyvDrieHpRBIvFans/2Kg3FPlacTHa9vhBilcze0pNBDrlSXyGuKMBLl8g==, tarball: file:projects/model-hr.tgz}
|
||||
id: file:projects/model-hr.tgz
|
||||
name: '@rush-temp/model-hr'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.45.5
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5
|
||||
'@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a
|
||||
eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.6.2
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-inventory.tgz_typescript@4.7.2:
|
||||
resolution: {integrity: sha512-ZrDM3hzsPJZPI1EGZpP4sEkEnJmmfzW77c2g7JejOCROBy6gd7237XN/SPjPk85CcSg7iHyueJiG9QGgoRB5SQ==, tarball: file:projects/model-inventory.tgz}
|
||||
id: file:projects/model-inventory.tgz
|
||||
@ -11847,6 +11967,29 @@ packages:
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-server-hr.tgz_typescript@4.7.2:
|
||||
resolution: {integrity: sha512-jSk/2YD9iHX6gPWUlc+RstSQVCpJGeoEsRNyaI3MMPtwytRCNa6Bdp1FjwFr3WId2wFQ+eHdpg9EtGr5ooGhUg==, tarball: file:projects/model-server-hr.tgz}
|
||||
id: file:projects/model-server-hr.tgz
|
||||
name: '@rush-temp/model-server-hr'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.45.5
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5
|
||||
'@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a
|
||||
eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.6.2
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-server-inventory.tgz_typescript@4.7.2:
|
||||
resolution: {integrity: sha512-fEZjpHFjw7k95RXySO8e9RpoaicF9jI2fVN9SSDEFUSmBsUKz3yR1wa1JrALrsH0YjcKzL1dWQK6AIJj4PuMCA==, tarball: file:projects/model-server-inventory.tgz}
|
||||
id: file:projects/model-server-inventory.tgz
|
||||
@ -12193,7 +12336,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-tracker.tgz_typescript@4.7.2:
|
||||
resolution: {integrity: sha512-3giT5Ql8N6I6UaeU038P16HSmmhnQdy2eIYaKok1cF+AX0Z/0dXcR/bSvgQ88WJxMrtAsusZkOkF+PyvUUJOYg==, tarball: file:projects/model-tracker.tgz}
|
||||
resolution: {integrity: sha512-3NvQ5qG0MQi1q/7nydGTluAJYjDlwLDG+AZXSG3I0tMEG5YpQguM399GXlGlWZzhFAc++KUK1nDR7JWtaPmXcA==, tarball: file:projects/model-tracker.tgz}
|
||||
id: file:projects/model-tracker.tgz
|
||||
name: '@rush-temp/model-tracker'
|
||||
version: 0.0.0
|
||||
@ -12679,7 +12822,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod.tgz_d1c3762ecb2c185353d3f02936f6ec22:
|
||||
resolution: {integrity: sha512-PehFsaG21mA1q8ou+ldHAfMX0O+nPv2zVXTuj/K5TPMZ/DEkCtQHZtaVElryGq4xD1PlMyaID/1mqppbYEpbPQ==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-Ruj8noHdrzO1bGVEYxLSgza5VdloFea3VbtIutq8UnZThqIzyBmJRsEo1lLmN5WAKBW50oEBKsKDZxuXB/ZuYQ==, tarball: file:projects/prod.tgz}
|
||||
id: file:projects/prod.tgz
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
@ -13182,6 +13325,51 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-hr-resources.tgz:
|
||||
resolution: {integrity: sha512-D073FS8B1EjB1D7fFgFm1Tdq6u69DaNcLVx65szIypERkUyFKyNr5KXqSyHSeDbwPWwz7nyOw2ecN8rlCPHnJg==, tarball: file:projects/server-hr-resources.tgz}
|
||||
name: '@rush-temp/server-hr-resources'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.45.5
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5
|
||||
'@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a
|
||||
eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.6.2
|
||||
typescript: 4.7.2
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-hr.tgz:
|
||||
resolution: {integrity: sha512-VPn9I/49Q/15y/R3yinhsKHb5CwTxzHZFemEfj0b/sALUamdf3S3sJpdMorZkNI44hFOns6LzHRRKUf/ka9zOA==, tarball: file:projects/server-hr.tgz}
|
||||
name: '@rush-temp/server-hr'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.45.5
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@types/node': 16.11.38
|
||||
'@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5
|
||||
'@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a
|
||||
eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.6.2
|
||||
typescript: 4.7.2
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-inventory-resources.tgz:
|
||||
resolution: {integrity: sha512-QXv09ktLX1fL3VlWS5ofrZxuL21p9T0IWM0X+hLCpoqmxPA/Bhpi52CtRM/89DsgohhEPPpWNG9byQci8A5vOw==, tarball: file:projects/server-inventory-resources.tgz}
|
||||
name: '@rush-temp/server-inventory-resources'
|
||||
@ -13693,7 +13881,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server.tgz:
|
||||
resolution: {integrity: sha512-YMw6QleaQlQlzkQhIncun4YVr77XcZSRLy8xzfJiy6X1PaBSve9WY9mGkKiiw119bo2/72HEHEHMnsHQU3VtSQ==, tarball: file:projects/server.tgz}
|
||||
resolution: {integrity: sha512-xJg2GMws1k8PADTrUVUbp6eEynFM+iEVYmsaU5lWpPk2nz4MrDTnirbDITNKPbmKlTdODDWJSaT6zIjhi+GRMg==, tarball: file:projects/server.tgz}
|
||||
name: '@rush-temp/server'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -14252,7 +14440,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tool.tgz:
|
||||
resolution: {integrity: sha512-cLyyAtdkGqG4pxA7QhbNhoNwhIoxpUktmo6OA8kJzcjisF23dXsuuDv097gM6Y0IOdIRGWET8XRaM1sIE/4ogQ==, tarball: file:projects/tool.tgz}
|
||||
resolution: {integrity: sha512-gvdRnHRhzst5gkHEFg9GVc3D04b+BAks8qPdrVxHQSWvWiiUmh3kG+uNWQKklSa9rtRzlSHUrf6NvUvgEg2c5w==, tarball: file:projects/tool.tgz}
|
||||
name: '@rush-temp/tool'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -14321,7 +14509,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tracker-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c:
|
||||
resolution: {integrity: sha512-z3TfQ4BzAQK48+svzVwLJMfJP8kF7Skk6XgUYiW6NGYmWTsauxTAO43pbnH0+zrx112vGahLduOnBsYNURRLXQ==, tarball: file:projects/tracker-resources.tgz}
|
||||
resolution: {integrity: sha512-bNqQvwzdPa61s2z+M0xrwW5bSvv46yBY8BXt6cqKEHwp1Zv78P5NHU/ScAgVRuJSvwknhrykBK1mK7zghMr3Hg==, tarball: file:projects/tracker-resources.tgz}
|
||||
id: file:projects/tracker-resources.tgz
|
||||
name: '@rush-temp/tracker-resources'
|
||||
version: 0.0.0
|
||||
@ -14358,7 +14546,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tracker.tgz:
|
||||
resolution: {integrity: sha512-fj497PawVB5mdi7yIw96vQs81XXIcJ3SzrQe0UHyb4IqTbFCoN4/HHGnZ6JXrUY/wdPLHfJzUVMlZKZVKcjD6A==, tarball: file:projects/tracker.tgz}
|
||||
resolution: {integrity: sha512-wGhZrDr9Vkg1fm9mUcBfkmQXKQlFpiUUOVIa3WEzoIlCzuAzSs4RiAk5XtVeryvQziu6G0dAzbOka9lV9Fxa+A==, tarball: file:projects/tracker.tgz}
|
||||
name: '@rush-temp/tracker'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
@ -144,6 +144,11 @@
|
||||
"@anticrm/text-editor": "~0.6.0",
|
||||
"@anticrm/board": "~0.6.0",
|
||||
"@anticrm/board-assets": "~0.6.0",
|
||||
"@anticrm/board-resources": "~0.6.0"
|
||||
"@anticrm/board-resources": "~0.6.0",
|
||||
"@anticrm/hr": "~0.6.0",
|
||||
"@anticrm/hr-assets": "~0.6.0",
|
||||
"@anticrm/hr-resources": "~0.6.0",
|
||||
"@anticrm/server-hr": "~0.6.0",
|
||||
"@anticrm/server-hr-resources": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import { serverTagsId } from '@anticrm/server-tags'
|
||||
import { serverCalendarId } from '@anticrm/server-calendar'
|
||||
import { serverGmailId } from '@anticrm/server-gmail'
|
||||
import { serverTelegramId } from '@anticrm/server-telegram'
|
||||
|
||||
import { serverHrId } from '@anticrm/server-hr'
|
||||
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
|
||||
@ -60,6 +60,7 @@ export function configurePlatformDev() {
|
||||
addLocation(serverCalendarId, () => import/* webpackChunkName: "server-calendar" */ ('@anticrm/server-calendar-resources'))
|
||||
addLocation(serverGmailId, () => import/* webpackChunkName: "server-gmail" */ ('@anticrm/server-gmail-resources'))
|
||||
addLocation(serverTelegramId, () => import/* webpackChunkName: "server-telegram" */ ('@anticrm/server-telegram-resources'))
|
||||
addLocation(serverHrId, () => import(/* webpackChunkName: "server-attachment" */ '@anticrm/server-hr-resources'))
|
||||
// Set devmodel to hook client to be able to present all activity
|
||||
enableDevModel()
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import { tagsId } from '@anticrm/tags'
|
||||
import { calendarId } from '@anticrm/calendar'
|
||||
import { trackerId } from '@anticrm/tracker'
|
||||
import { boardId } from '@anticrm/board'
|
||||
import { hrId } from '@anticrm/hr'
|
||||
import rekoni from '@anticrm/rekoni'
|
||||
|
||||
import '@anticrm/login-assets'
|
||||
@ -62,6 +63,7 @@ import '@anticrm/calendar-assets'
|
||||
import '@anticrm/tracker-assets'
|
||||
import '@anticrm/board-assets'
|
||||
import '@anticrm/preference-assets'
|
||||
import '@anticrm/hr-assets'
|
||||
import presentation, { presentationId } from '@anticrm/presentation'
|
||||
import { coreId } from '@anticrm/core'
|
||||
import { textEditorId } from '@anticrm/text-editor'
|
||||
@ -115,6 +117,7 @@ export async function configurePlatform() {
|
||||
|
||||
addLocation(trackerId, () => import(/* webpackChunkName: "tracker" */ '@anticrm/tracker-resources'))
|
||||
addLocation(boardId, () => import(/* webpackChunkName: "board" */ '@anticrm/board-resources'))
|
||||
addLocation(hrId, () => import(/* webpackChunkName: "hr" */ '@anticrm/hr-resources'))
|
||||
|
||||
setMetadata(workbench.metadata.PlatformTitle, 'Platform')
|
||||
}
|
||||
|
@ -103,6 +103,8 @@
|
||||
"@anticrm/server-gmail-resources": "~0.6.0",
|
||||
"@anticrm/server-telegram": "~0.6.0",
|
||||
"@anticrm/server-telegram-resources": "~0.6.0",
|
||||
"@anticrm/server-hr": "~0.6.0",
|
||||
"@anticrm/server-hr-resources": "~0.6.0",
|
||||
"@anticrm/rekoni": "~0.6.0",
|
||||
"got": "^11.8.3",
|
||||
"@anticrm/tags": "~0.6.2",
|
||||
|
@ -70,6 +70,7 @@ import { serverTagsId } from '@anticrm/server-tags'
|
||||
import { serverTaskId } from '@anticrm/server-task'
|
||||
import { serverTrackerId } from '@anticrm/server-tracker'
|
||||
import { serverTelegramId } from '@anticrm/server-telegram'
|
||||
import { serverHrId } from '@anticrm/server-hr'
|
||||
import { Client as ElasticClient } from '@elastic/elasticsearch'
|
||||
import { Client } from 'minio'
|
||||
import { Db, MongoClient } from 'mongodb'
|
||||
@ -139,6 +140,7 @@ export class ElasticTool {
|
||||
addLocation(serverCalendarId, () => import('@anticrm/server-calendar-resources'))
|
||||
addLocation(serverGmailId, () => import('@anticrm/server-gmail-resources'))
|
||||
addLocation(serverTelegramId, () => import('@anticrm/server-telegram-resources'))
|
||||
addLocation(serverHrId, () => import('@anticrm/server-hr-resources'))
|
||||
this.mongoClient = new MongoClient(mongoUrl)
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,8 @@
|
||||
"@anticrm/model-server-telegram": "~0.6.0",
|
||||
"@anticrm/model-tracker": "~0.6.0",
|
||||
"@anticrm/model-board": "~0.6.0",
|
||||
"@anticrm/model-preference": "~0.6.0"
|
||||
"@anticrm/model-preference": "~0.6.0",
|
||||
"@anticrm/model-hr": "~0.6.0",
|
||||
"@anticrm/model-server-hr": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,8 @@ import { createModel as serverTelegramModel } from '@anticrm/model-server-telegr
|
||||
import { createModel as trackerModel } from '@anticrm/model-tracker'
|
||||
import { createModel as boardModel } from '@anticrm/model-board'
|
||||
import { createModel as preferenceModel } from '@anticrm/model-preference'
|
||||
import { createModel as hrModel } from '@anticrm/model-hr'
|
||||
import { createModel as serverHrModel } from '@anticrm/model-server-hr'
|
||||
|
||||
export const version: Data<Version> = jsonVersion as Data<Version>
|
||||
|
||||
@ -82,6 +84,7 @@ const builders: [(b: Builder) => void, string][] = [
|
||||
[textEditorModel, 'text-editor'],
|
||||
[notificationModel, 'notification'],
|
||||
[preferenceModel, 'preference'],
|
||||
[hrModel, 'hr'],
|
||||
|
||||
[serverCoreModel, 'server-core'],
|
||||
[serverAttachmentModel, 'server-attachment'],
|
||||
@ -99,6 +102,7 @@ const builders: [(b: Builder) => void, string][] = [
|
||||
[serverCalendarModel, 'server-calendar'],
|
||||
[serverGmailModel, 'server-gmail'],
|
||||
[serverTelegramModel, 'server-telegram'],
|
||||
[serverHrModel, 'server-hr'],
|
||||
[trackerModel, 'tracker'],
|
||||
[boardModel, 'board'],
|
||||
[calendarModel, 'calendar']
|
||||
|
@ -32,6 +32,7 @@ import { viewOperation } from '@anticrm/model-view'
|
||||
import { trackerOperation } from '@anticrm/model-tracker'
|
||||
import { boardOperation } from '@anticrm/model-board'
|
||||
import { demoOperation } from '@anticrm/model-demo'
|
||||
import { hrOperation } from '@anticrm/model-hr'
|
||||
|
||||
export const migrateOperations: MigrateOperation[] = [
|
||||
coreOperation,
|
||||
@ -50,5 +51,6 @@ export const migrateOperations: MigrateOperation[] = [
|
||||
notificationOperation,
|
||||
settingOperation,
|
||||
trackerOperation,
|
||||
boardOperation
|
||||
boardOperation,
|
||||
hrOperation
|
||||
]
|
||||
|
7
models/hr/.eslintrc.js
Normal file
7
models/hr/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/model-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
models/hr/.npmignore
Normal file
4
models/hr/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
18
models/hr/config/rig.json
Normal file
18
models/hr/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/model-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
41
models/hr/package.json
Normal file
41
models/hr/package.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@anticrm/model-hr",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/model-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.45.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/model": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/contact": "~0.6.5",
|
||||
"@anticrm/platform": "~0.6.6",
|
||||
"@anticrm/model-core": "~0.6.0",
|
||||
"@anticrm/model-view": "~0.6.0",
|
||||
"@anticrm/model-workbench": "~0.6.1",
|
||||
"@anticrm/model-contact": "~0.6.1",
|
||||
"@anticrm/hr": "~0.6.0",
|
||||
"@anticrm/hr-resources": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0"
|
||||
}
|
||||
}
|
137
models/hr/src/index.ts
Normal file
137
models/hr/src/index.ts
Normal file
@ -0,0 +1,137 @@
|
||||
//
|
||||
// Copyright © 2022 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 { Employee } from '@anticrm/contact'
|
||||
import contact, { TEmployee } from '@anticrm/model-contact'
|
||||
import { IndexKind, Ref } from '@anticrm/core'
|
||||
import type { Department, Staff } from '@anticrm/hr'
|
||||
import { Builder, Index, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import core, { TSpace } from '@anticrm/model-core'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import hr from './plugin'
|
||||
import view, { createAction } from '@anticrm/model-view'
|
||||
|
||||
@Model(hr.class.Department, core.class.Space)
|
||||
@UX(hr.string.Department, hr.icon.Department)
|
||||
export class TDepartment extends TSpace implements Department {
|
||||
@Prop(TypeRef(hr.class.Department), hr.string.ParentDepartmentLabel)
|
||||
declare space: Ref<Department>
|
||||
|
||||
@Prop(TypeString(), core.string.Name)
|
||||
@Index(IndexKind.FullText)
|
||||
name!: string
|
||||
|
||||
avatar?: string | null
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), hr.string.TeamLead)
|
||||
teamLead!: Ref<Employee> | null
|
||||
}
|
||||
|
||||
@Mixin(hr.mixin.Staff, contact.class.Employee)
|
||||
@UX(contact.string.Employee, hr.icon.HR)
|
||||
export class TStaff extends TEmployee implements Staff {
|
||||
@Prop(TypeRef(hr.class.Department), hr.string.Department)
|
||||
department!: Ref<Department>
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TDepartment, TStaff)
|
||||
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: hr.string.HRApplication,
|
||||
icon: hr.icon.HR,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
specials: [
|
||||
{
|
||||
id: 'structure',
|
||||
component: hr.component.Structure,
|
||||
icon: hr.icon.Structure,
|
||||
label: hr.string.Structure,
|
||||
position: 'top'
|
||||
}
|
||||
],
|
||||
spaces: []
|
||||
}
|
||||
},
|
||||
hr.app.HR
|
||||
)
|
||||
|
||||
builder.mixin(hr.class.Department, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: hr.component.DepartmentEditor
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.ShowPanel,
|
||||
actionProps: {
|
||||
component: hr.component.EditDepartment
|
||||
},
|
||||
label: view.string.Open,
|
||||
icon: view.icon.Open,
|
||||
keyBinding: ['e'],
|
||||
input: 'any',
|
||||
category: hr.category.HR,
|
||||
target: hr.class.Department,
|
||||
context: { mode: 'context', application: hr.app.HR, group: 'top' }
|
||||
},
|
||||
hr.action.EditDepartment
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: hr.component.DepartmentStaff,
|
||||
element: 'float'
|
||||
},
|
||||
label: hr.string.ShowEmployees,
|
||||
icon: contact.icon.Person,
|
||||
keyBinding: ['m'],
|
||||
input: 'any',
|
||||
category: hr.category.HR,
|
||||
target: hr.class.Department,
|
||||
context: { mode: 'context', application: hr.app.HR, group: 'top' }
|
||||
},
|
||||
hr.action.ShowEmployees
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.Delete,
|
||||
label: view.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
input: 'any',
|
||||
category: hr.category.HR,
|
||||
keyBinding: ['Meta + Backspace', 'Ctrl + Backspace'],
|
||||
query: {
|
||||
'members.length': 0,
|
||||
_id: { $nin: [hr.ids.Head] }
|
||||
},
|
||||
target: hr.class.Department,
|
||||
context: { mode: 'context', application: hr.app.HR, group: 'top' }
|
||||
},
|
||||
hr.action.DeleteDepartment
|
||||
)
|
||||
}
|
||||
|
||||
export { hrOperation } from './migration'
|
||||
export { default } from './plugin'
|
48
models/hr/src/migration.ts
Normal file
48
models/hr/src/migration.ts
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// Copyright © 2022 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 { TxOperations } from '@anticrm/core'
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||
import core from '@anticrm/model-core'
|
||||
import hr from './index'
|
||||
|
||||
async function createSpace (tx: TxOperations): Promise<void> {
|
||||
const current = await tx.findOne(core.class.Space, {
|
||||
_id: hr.ids.Head
|
||||
})
|
||||
if (current === undefined) {
|
||||
await tx.createDoc(
|
||||
hr.class.Department,
|
||||
core.space.Space,
|
||||
{
|
||||
name: 'Organization',
|
||||
description: '',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: [],
|
||||
teamLead: null
|
||||
},
|
||||
hr.ids.Head
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const hrOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const tx = new TxOperations(client, core.account.System)
|
||||
await createSpace(tx)
|
||||
}
|
||||
}
|
43
models/hr/src/plugin.ts
Normal file
43
models/hr/src/plugin.ts
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright © 2022 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 { Ref } from '@anticrm/core'
|
||||
import { hrId } from '@anticrm/hr'
|
||||
import hr from '@anticrm/hr-resources/src/plugin'
|
||||
import { IntlString, mergeIds } from '@anticrm/platform'
|
||||
import { AnyComponent } from '@anticrm/ui'
|
||||
import { Action, ActionCategory } from '@anticrm/view'
|
||||
|
||||
export default mergeIds(hrId, hr, {
|
||||
string: {
|
||||
HRApplication: '' as IntlString,
|
||||
Departments: '' as IntlString,
|
||||
ShowEmployees: '' as IntlString
|
||||
},
|
||||
component: {
|
||||
Structure: '' as AnyComponent,
|
||||
EditDepartment: '' as AnyComponent,
|
||||
DepartmentStaff: '' as AnyComponent,
|
||||
DepartmentEditor: '' as AnyComponent
|
||||
},
|
||||
category: {
|
||||
HR: '' as Ref<ActionCategory>
|
||||
},
|
||||
action: {
|
||||
EditDepartment: '' as Ref<Action>,
|
||||
ShowEmployees: '' as Ref<Action>,
|
||||
DeleteDepartment: '' as Ref<Action>
|
||||
}
|
||||
})
|
8
models/hr/tsconfig.json
Normal file
8
models/hr/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/model-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
}
|
||||
}
|
7
models/server-hr/.eslintrc.js
Normal file
7
models/server-hr/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/model-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
models/server-hr/.npmignore
Normal file
4
models/server-hr/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
18
models/server-hr/config/rig.json
Normal file
18
models/server-hr/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/model-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
34
models/server-hr/package.json
Normal file
34
models/server-hr/package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@anticrm/model-server-hr",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/model-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.45.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/model": "~0.6.0",
|
||||
"@anticrm/platform": "~0.6.6",
|
||||
"@anticrm/server-hr": "~0.6.0",
|
||||
"@anticrm/server-core": "~0.6.1"
|
||||
}
|
||||
}
|
26
models/server-hr/src/index.ts
Normal file
26
models/server-hr/src/index.ts
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright © 2022 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 { Builder } from '@anticrm/model'
|
||||
|
||||
import serverCore from '@anticrm/server-core'
|
||||
import core from '@anticrm/core'
|
||||
import serverHr from '@anticrm/server-hr'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverHr.trigger.OnDepartmentStaff
|
||||
})
|
||||
}
|
8
models/server-hr/tsconfig.json
Normal file
8
models/server-hr/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/model-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
}
|
||||
}
|
@ -14,11 +14,11 @@
|
||||
//
|
||||
|
||||
import { PlatformError, Severity, Status } from '@anticrm/platform'
|
||||
import { Lookup, ReverseLookups } from '.'
|
||||
import { getObjectValue, Lookup, ReverseLookups } from '.'
|
||||
import type { Class, Doc, Ref } from './classes'
|
||||
import core from './component'
|
||||
import { Hierarchy } from './hierarchy'
|
||||
import { matchQuery, resultSort } from './query'
|
||||
import { matchQuery, resultSort, checkMixinKey } from './query'
|
||||
import type { DocumentQuery, FindOptions, FindResult, LookupData, Storage, TxResult, WithLookup } from './storage'
|
||||
import type { Tx, TxCreateDoc, TxMixin, TxPutBag, TxRemoveDoc, TxUpdateDoc } from './tx'
|
||||
import { TxProcessor } from './tx'
|
||||
@ -77,25 +77,31 @@ export abstract class MemDb extends TxProcessor {
|
||||
return doc as T
|
||||
}
|
||||
|
||||
private async getLookupValue<T extends Doc>(doc: T, lookup: Lookup<T>, result: LookupData<T>): Promise<void> {
|
||||
private async getLookupValue<T extends Doc>(
|
||||
_class: Ref<Class<T>>,
|
||||
doc: T,
|
||||
lookup: Lookup<T>,
|
||||
result: LookupData<T>
|
||||
): Promise<void> {
|
||||
for (const key in lookup) {
|
||||
if (key === '_id') {
|
||||
await this.getReverseLookupValue(doc, lookup, result)
|
||||
continue
|
||||
}
|
||||
const value = (lookup as any)[key]
|
||||
const tkey = checkMixinKey(key, _class, this.hierarchy)
|
||||
if (Array.isArray(value)) {
|
||||
const [_class, nested] = value
|
||||
const objects = await this.findAll(_class, { _id: (doc as any)[key] })
|
||||
const objects = await this.findAll(_class, { _id: getObjectValue(tkey, doc) })
|
||||
;(result as any)[key] = objects[0]
|
||||
const nestedResult = {}
|
||||
const parent = (result as any)[key]
|
||||
await this.getLookupValue(parent, nested, nestedResult)
|
||||
await this.getLookupValue(_class, parent, nested, nestedResult)
|
||||
Object.assign(parent, {
|
||||
$lookup: nestedResult
|
||||
})
|
||||
} else {
|
||||
const objects = await this.findAll(value, { _id: (doc as any)[key] })
|
||||
const objects = await this.findAll(value, { _id: getObjectValue(tkey, doc) })
|
||||
;(result as any)[key] = objects[0]
|
||||
}
|
||||
}
|
||||
@ -118,11 +124,11 @@ export abstract class MemDb extends TxProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private async lookup<T extends Doc>(docs: T[], lookup: Lookup<T>): Promise<WithLookup<T>[]> {
|
||||
private async lookup<T extends Doc>(_class: Ref<Class<T>>, docs: T[], lookup: Lookup<T>): Promise<WithLookup<T>[]> {
|
||||
const withLookup: WithLookup<T>[] = []
|
||||
for (const doc of docs) {
|
||||
const result: LookupData<T> = {}
|
||||
await this.getLookupValue(doc, lookup, result)
|
||||
await this.getLookupValue(_class, doc, lookup, result)
|
||||
withLookup.push(Object.assign({}, doc, { $lookup: result }))
|
||||
}
|
||||
return withLookup
|
||||
@ -152,7 +158,7 @@ export abstract class MemDb extends TxProcessor {
|
||||
}
|
||||
|
||||
if (options?.lookup !== undefined) {
|
||||
result = await this.lookup(result as T[], options.lookup)
|
||||
result = await this.lookup(_class, result as T[], options.lookup)
|
||||
result = matchQuery(result, query, _class, this.hierarchy)
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,9 @@ function $push (document: Doc, keyval: Record<string, PropertyType>): void {
|
||||
function $pull (document: Doc, keyval: Record<string, PropertyType>): void {
|
||||
const doc = document as any
|
||||
for (const key in keyval) {
|
||||
if (doc[key] === undefined) {
|
||||
doc[key] = []
|
||||
}
|
||||
const arr = doc[key] as Array<any>
|
||||
if (typeof keyval[key] === 'object') {
|
||||
const { $in } = keyval[key] as PullArray<PropertyType>
|
||||
@ -55,6 +58,9 @@ function $pull (document: Doc, keyval: Record<string, PropertyType>): void {
|
||||
function $move (document: Doc, keyval: Record<string, PropertyType>): void {
|
||||
const doc = document as any
|
||||
for (const key in keyval) {
|
||||
if (doc[key] === undefined) {
|
||||
doc[key] = []
|
||||
}
|
||||
const arr = doc[key] as Array<any>
|
||||
const desc = keyval[key]
|
||||
doc[key] = arr.filter((val) => val !== desc.$value)
|
||||
|
@ -14,15 +14,17 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { IconSize, showPopup } from '@anticrm/ui'
|
||||
import { AnySvelteComponent, IconSize, showPopup } from '@anticrm/ui'
|
||||
|
||||
import Avatar from './Avatar.svelte'
|
||||
import EditAvatarPopup from './EditAvatarPopup.svelte'
|
||||
import { getFileUrl } from '../utils'
|
||||
import { Asset } from '@anticrm/platform'
|
||||
|
||||
export let avatar: string | null | undefined = undefined
|
||||
export let size: IconSize
|
||||
export let direct: Blob | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -75,6 +77,6 @@
|
||||
</script>
|
||||
|
||||
<div class="cursor-pointer" on:click={onClick}>
|
||||
<Avatar {avatar} {direct} {size} />
|
||||
<Avatar {avatar} {direct} {size} {icon} />
|
||||
<input style="display: none;" type="file" bind:this={inputRef} on:change={onSelect} accept={targetMimes.join(',')} />
|
||||
</div>
|
||||
|
@ -25,11 +25,14 @@
|
||||
getFocusManager,
|
||||
AnyComponent,
|
||||
Tooltip,
|
||||
TooltipAlignment
|
||||
TooltipAlignment,
|
||||
ButtonKind,
|
||||
ButtonSize
|
||||
} from '@anticrm/ui'
|
||||
import SpacesPopup from './SpacesPopup.svelte'
|
||||
|
||||
import type { Ref, Class, Space, DocumentQuery } from '@anticrm/core'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let _class: Ref<Class<Space>>
|
||||
export let spaceQuery: DocumentQuery<Space> | undefined = { archived: false }
|
||||
@ -44,10 +47,15 @@
|
||||
}
|
||||
| undefined = undefined
|
||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
|
||||
let selected: Space | undefined
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const mgr = getFocusManager()
|
||||
async function updateSelected (value: Ref<Space> | undefined) {
|
||||
@ -71,6 +79,7 @@
|
||||
(result) => {
|
||||
if (result) {
|
||||
value = result._id
|
||||
dispatch('change', value)
|
||||
mgr?.setFocusPos(focusIndex)
|
||||
}
|
||||
}
|
||||
@ -79,7 +88,7 @@
|
||||
</script>
|
||||
|
||||
<Tooltip {label} fill={false} direction={labelDirection}>
|
||||
<Button {focus} {focusIndex} icon={IconFolder} size={'small'} kind={'no-border'} on:click={showSpacesPopup}>
|
||||
<Button {focus} {focusIndex} icon={IconFolder} {size} {kind} {justify} {width} on:click={showSpacesPopup}>
|
||||
<span slot="content" class="overflow-label disabled text-sm">
|
||||
{#if selected}{selected.name}{:else}<Label {label} />{/if}
|
||||
</span>
|
||||
|
@ -15,13 +15,17 @@
|
||||
<script lang="ts">
|
||||
import type { Class, DocumentQuery, Ref, Space } from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { AnyComponent } from '@anticrm/ui'
|
||||
import { AnyComponent, ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
import SpaceSelect from './SpaceSelect.svelte'
|
||||
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
export let _class: Ref<Class<Space>>
|
||||
export let query: DocumentQuery<Space> = { archived: false }
|
||||
export let label: IntlString
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
|
||||
export let create:
|
||||
| {
|
||||
@ -31,4 +35,17 @@
|
||||
| undefined = undefined
|
||||
</script>
|
||||
|
||||
<SpaceSelect {create} focus focusIndex={-10} {_class} spaceQuery={query} {label} bind:value={space} />
|
||||
<SpaceSelect
|
||||
{create}
|
||||
focus
|
||||
focusIndex={-10}
|
||||
{_class}
|
||||
spaceQuery={query}
|
||||
{label}
|
||||
{size}
|
||||
{kind}
|
||||
{justify}
|
||||
{width}
|
||||
bind:value={space}
|
||||
on:change
|
||||
/>
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
checkMixinKey,
|
||||
Class,
|
||||
Client,
|
||||
Doc,
|
||||
@ -26,11 +27,13 @@ import core, {
|
||||
Hierarchy,
|
||||
Lookup,
|
||||
LookupData,
|
||||
matchQuery,
|
||||
ModelDb,
|
||||
Ref,
|
||||
resultSort,
|
||||
ReverseLookups,
|
||||
SortingQuery,
|
||||
toFindResult,
|
||||
Tx,
|
||||
TxBulkWrite,
|
||||
TxCollectionCUD,
|
||||
@ -41,10 +44,7 @@ import core, {
|
||||
TxRemoveDoc,
|
||||
TxResult,
|
||||
TxUpdateDoc,
|
||||
WithLookup,
|
||||
toFindResult,
|
||||
checkMixinKey,
|
||||
matchQuery
|
||||
WithLookup
|
||||
} from '@anticrm/core'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
@ -195,7 +195,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
})
|
||||
|
||||
this.queries.set(_class, queries)
|
||||
if (this.queue.length > CACHE_SIZE) {
|
||||
while (this.queue.length > CACHE_SIZE) {
|
||||
this.remove()
|
||||
}
|
||||
return q
|
||||
@ -271,6 +271,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
if (updatedDoc !== undefined) {
|
||||
// Create or apply mixin value
|
||||
updatedDoc = TxProcessor.updateMixin4Doc(updatedDoc, tx)
|
||||
await this.__updateLookup(q, updatedDoc, tx.attributes)
|
||||
await this.updatedDocCallback(updatedDoc, q)
|
||||
} else if (isMixin) {
|
||||
// Mixin potentially added to object we doesn't have in out results
|
||||
@ -477,25 +478,31 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
return false
|
||||
}
|
||||
|
||||
private async getLookupValue<T extends Doc>(doc: T, lookup: Lookup<T>, result: LookupData<T>): Promise<void> {
|
||||
private async getLookupValue<T extends Doc>(
|
||||
_class: Ref<Class<T>>,
|
||||
doc: T,
|
||||
lookup: Lookup<T>,
|
||||
result: LookupData<T>
|
||||
): Promise<void> {
|
||||
for (const key in lookup) {
|
||||
if (key === '_id') {
|
||||
await this.getReverseLookupValue(doc, lookup, result)
|
||||
continue
|
||||
}
|
||||
const value = (lookup as any)[key]
|
||||
const tkey = checkMixinKey(key, _class, this.client.getHierarchy())
|
||||
if (Array.isArray(value)) {
|
||||
const [_class, nested] = value
|
||||
const objects = await this.findAll(_class, { _id: (doc as any)[key] })
|
||||
const objects = await this.findAll(_class, { _id: getObjectValue(tkey, doc) })
|
||||
;(result as any)[key] = objects[0]
|
||||
const nestedResult = {}
|
||||
const parent = (result as any)[key]
|
||||
await this.getLookupValue(parent, nested, nestedResult)
|
||||
await this.getLookupValue(_class, parent, nested, nestedResult)
|
||||
Object.assign(parent, {
|
||||
$lookup: nestedResult
|
||||
})
|
||||
} else {
|
||||
const objects = await this.findAll(value, { _id: (doc as any)[key] })
|
||||
const objects = await this.findAll(value, { _id: getObjectValue(tkey, doc) })
|
||||
;(result as any)[key] = objects[0]
|
||||
}
|
||||
}
|
||||
@ -523,9 +530,9 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
}
|
||||
}
|
||||
|
||||
private async lookup<T extends Doc>(doc: T, lookup: Lookup<T>): Promise<void> {
|
||||
private async lookup<T extends Doc>(_class: Ref<Class<T>>, doc: T, lookup: Lookup<T>): Promise<void> {
|
||||
const result: LookupData<Doc> = {}
|
||||
await this.getLookupValue(doc, lookup, result)
|
||||
await this.getLookupValue(_class, doc, lookup, result)
|
||||
;(doc as WithLookup<Doc>).$lookup = result
|
||||
}
|
||||
|
||||
@ -547,7 +554,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
}
|
||||
|
||||
if (q.options?.lookup !== undefined) {
|
||||
await this.lookup(doc, q.options.lookup)
|
||||
await this.lookup(q._class, doc, q.options.lookup)
|
||||
}
|
||||
// We could already have document inside results, if query is created during processing of document create transaction and not yet handled on client.
|
||||
const pos = q.result.findIndex((p) => p._id === doc._id)
|
||||
@ -751,14 +758,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
return await super.tx(tx)
|
||||
}
|
||||
|
||||
private async __updateDoc (q: Query, updatedDoc: WithLookup<Doc>, tx: TxUpdateDoc<Doc>): Promise<void> {
|
||||
TxProcessor.updateDoc2Doc(updatedDoc, tx)
|
||||
|
||||
const ops = {
|
||||
...tx.operations,
|
||||
modifiedBy: tx.modifiedBy,
|
||||
modifiedOn: tx.modifiedOn
|
||||
} as any
|
||||
private async __updateLookup (q: Query, updatedDoc: WithLookup<Doc>, ops: any): Promise<void> {
|
||||
for (const key in ops) {
|
||||
if (!key.startsWith('$')) {
|
||||
if (q.options !== undefined) {
|
||||
@ -783,7 +783,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
}
|
||||
} else {
|
||||
if (key === '$push') {
|
||||
const pops = tx.operations[key] ?? {}
|
||||
const pops = ops[key] ?? {}
|
||||
for (const pkey of Object.keys(pops)) {
|
||||
if (q.options !== undefined) {
|
||||
const lookup = (q.options.lookup as any)?.[pkey]
|
||||
@ -794,28 +794,26 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
if (pp[pkey] === undefined) {
|
||||
pp[pkey] = []
|
||||
}
|
||||
if (Array.isArray((pops as any)[pkey])) {
|
||||
if (Array.isArray(pops[pkey])) {
|
||||
const pushData = await this.client.findAll(
|
||||
lookupClass,
|
||||
{ _id: { $in: (pops as any)[pkey] } },
|
||||
{ _id: { $in: pops[pkey] } },
|
||||
{ lookup: nestedLookup }
|
||||
)
|
||||
pp[pkey].push(...pushData)
|
||||
} else {
|
||||
pp[pkey].push(
|
||||
await this.client.findOne(lookupClass, { _id: (pops as any)[pkey] }, { lookup: nestedLookup })
|
||||
)
|
||||
pp[pkey].push(await this.client.findOne(lookupClass, { _id: pops[pkey] }, { lookup: nestedLookup }))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (key === '$pull') {
|
||||
const pops = tx.operations[key] ?? {}
|
||||
const pops = ops[key] ?? {}
|
||||
for (const pkey of Object.keys(pops)) {
|
||||
if (q.options !== undefined) {
|
||||
const lookup = (q.options.lookup as any)?.[pkey]
|
||||
if (lookup !== undefined) {
|
||||
const pid = (pops as any)[pkey]
|
||||
const pid = pops[pkey]
|
||||
const pp = updatedDoc.$lookup as any
|
||||
if (pp[pkey] === undefined) {
|
||||
pp[pkey] = []
|
||||
@ -833,6 +831,17 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
}
|
||||
}
|
||||
|
||||
private async __updateDoc (q: Query, updatedDoc: WithLookup<Doc>, tx: TxUpdateDoc<Doc>): Promise<void> {
|
||||
TxProcessor.updateDoc2Doc(updatedDoc, tx)
|
||||
|
||||
const ops = {
|
||||
...tx.operations,
|
||||
modifiedBy: tx.modifiedBy,
|
||||
modifiedOn: tx.modifiedOn
|
||||
}
|
||||
await this.__updateLookup(q, updatedDoc, ops)
|
||||
}
|
||||
|
||||
private sort (q: Query, tx: TxUpdateDoc<Doc>): void {
|
||||
const sort = q.options?.sort
|
||||
if (sort === undefined) return
|
||||
|
@ -24,7 +24,7 @@
|
||||
export let direction: TooltipAlignment | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
export let action: (ev: Event) => Promise<void> | void = async () => {}
|
||||
export let action: (ev: MouseEvent) => Promise<void> | void = async () => {}
|
||||
export let invisible: boolean = false
|
||||
</script>
|
||||
|
||||
|
7
plugins/hr-assets/.eslintrc.js
Normal file
7
plugins/hr-assets/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/assets/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
13
plugins/hr-assets/assets/icons.svg
Normal file
13
plugins/hr-assets/assets/icons.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- ALL HASHTAGs -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="structure" viewBox="0 0 24 24">
|
||||
<path d="M17.2,7.8h3.6c1.1,0,2-0.9,2-2V4.2c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0H9.8V4c0-1.5-1.2-2.8-2.8-2.8H4 C2.5,1.2,1.2,2.5,1.2,4v2c0,1.5,1.2,2.8,2.8,2.8h3c1.5,0,2.8-1.2,2.8-2.8V5.8h2V18c0,1.5,1.2,2.8,2.8,2.8h0.8v0c0,1.1,0.9,2,2,2h3.6 c1.1,0,2-0.9,2-2v-1.6c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0h-0.8c-0.7,0-1.2-0.6-1.2-1.2v-4.8h2v0c0,1.1,0.9,2,2,2h3.6 c1.1,0,2-0.9,2-2v-1.6c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0h-2v-6h2v0C15.2,6.9,16.1,7.8,17.2,7.8z M8.2,6 c0,0.7-0.6,1.2-1.2,1.2H4C3.3,7.2,2.8,6.7,2.8,6V4c0-0.7,0.6-1.2,1.2-1.2h3c0.7,0,1.2,0.6,1.2,1.2V6z M16.8,19.2 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V19.2z M16.8,11.7 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V11.7z M16.8,4.2 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V4.2z" />
|
||||
</symbol>
|
||||
<symbol id="department" viewBox="0 0 24 24">
|
||||
<path d="M17.2,7.8h3.6c1.1,0,2-0.9,2-2V4.2c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0H9.8V4c0-1.5-1.2-2.8-2.8-2.8H4 C2.5,1.2,1.2,2.5,1.2,4v2c0,1.5,1.2,2.8,2.8,2.8h3c1.5,0,2.8-1.2,2.8-2.8V5.8h2V18c0,1.5,1.2,2.8,2.8,2.8h0.8v0c0,1.1,0.9,2,2,2h3.6 c1.1,0,2-0.9,2-2v-1.6c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0h-0.8c-0.7,0-1.2-0.6-1.2-1.2v-4.8h2v0c0,1.1,0.9,2,2,2h3.6 c1.1,0,2-0.9,2-2v-1.6c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0h-2v-6h2v0C15.2,6.9,16.1,7.8,17.2,7.8z M8.2,6 c0,0.7-0.6,1.2-1.2,1.2H4C3.3,7.2,2.8,6.7,2.8,6V4c0-0.7,0.6-1.2,1.2-1.2h3c0.7,0,1.2,0.6,1.2,1.2V6z M16.8,19.2 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V19.2z M16.8,11.7 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V11.7z M16.8,4.2 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V4.2z" />
|
||||
</symbol>
|
||||
<symbol id="hr" viewBox="0 0 24 24">
|
||||
<path d="M12,10.8c2.6,0,4.8-2.1,4.8-4.8S14.6,1.2,12,1.2C9.4,1.2,7.2,3.4,7.2,6S9.4,10.8,12,10.8z M12,2.8 c1.8,0,3.2,1.5,3.2,3.2S13.8,9.2,12,9.2S8.8,7.8,8.8,6S10.2,2.8,12,2.8z" />
|
||||
<path d="M12,12.2c-5.4,0-9.8,4.4-9.8,9.8c0,0.4,0.3,0.8,0.8,0.8s0.8-0.3,0.8-0.8c0-4.2,3.1-7.7,7.2-8.2l-1.7,5.5 c-0.1,0.2,0,0.5,0.1,0.7l2,2.5c0.1,0.2,0.4,0.3,0.6,0.3s0.4-0.1,0.6-0.3l2-2.5c0.2-0.2,0.2-0.5,0.1-0.7L13,13.8 c4.1,0.5,7.2,4,7.2,8.2c0,0.4,0.3,0.8,0.8,0.8s0.8-0.3,0.8-0.8C21.8,16.6,17.4,12.2,12,12.2z M12,20.8l-1.2-1.5l1.2-3.8l1.2,3.8 L12,20.8z" />
|
||||
</symbol>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
5
plugins/hr-assets/config/rig.json
Normal file
5
plugins/hr-assets/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@anticrm/platform-rig",
|
||||
"rigProfile": "assets"
|
||||
}
|
21
plugins/hr-assets/lang/en.json
Normal file
21
plugins/hr-assets/lang/en.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"string": {
|
||||
"Department": "Department",
|
||||
"ParentDepartmentLabel": "Parent department",
|
||||
"Structure":"Structure",
|
||||
"CreateDepartment": "Create department",
|
||||
"CreateDepartmentLabel": "Create department",
|
||||
"DepartmentPlaceholder": "Department",
|
||||
"TeamLead": "Team lead",
|
||||
"UnAssignLead": "Unassign team lead",
|
||||
"MemberCount": "{count, plural, =0 {no employees} =1 {1 employee} other {# employees}}",
|
||||
"AssignLead": "Assign team lead",
|
||||
"TeamLeadTooltip": "{value}",
|
||||
"HRApplication": "Human resources",
|
||||
"MoveStaff": "Employee transfer",
|
||||
"MoveStaffDescr": "Do you want to transfer employee from {current} to {department}",
|
||||
"Departments": "Departments",
|
||||
"ShowEmployees": "Show employees",
|
||||
"AddEmployee": "Add employee"
|
||||
}
|
||||
}
|
21
plugins/hr-assets/lang/ru.json
Normal file
21
plugins/hr-assets/lang/ru.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"string": {
|
||||
"Department": "Департамент",
|
||||
"ParentDepartmentLabel": "Родительский департамент",
|
||||
"Structure":"Структура",
|
||||
"CreateDepartment": "Создать департамент",
|
||||
"CreateDepartmentLabel": "Создать департамент",
|
||||
"DepartmentPlaceholder": "Департамент",
|
||||
"TeamLead": "Менеджер",
|
||||
"UnAssignLead": "Отменить назначение менеджера",
|
||||
"MemberCount": "{count, plural, =0 {нет сотрудников} =1 {1 сотрудник} =2 {2 сотрудника} =3 {3 сотрудника} =4 {4 сотрудника} other {# сотрудников}}",
|
||||
"AssignLead": "Назначить менеджера",
|
||||
"TeamLeadTooltip": "{value}",
|
||||
"HRApplication": "Human resources",
|
||||
"MoveStaff": "Перевод сотрудника",
|
||||
"MoveStaffDescr": "Вы действительно хотите перевести сотрудника из {current} в {department}",
|
||||
"Departments": "Департаменты",
|
||||
"ShowEmployees": "Просмотреть сотрудников",
|
||||
"AddEmployee": "Добавить сотрудника"
|
||||
}
|
||||
}
|
34
plugins/hr-assets/package.json
Normal file
34
plugins/hr-assets/package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@anticrm/hr-assets",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"template": "@anticrm/assets-package",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:docs": "",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"format": "prettier --write src && eslint --fix src",
|
||||
"build:watch": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint": "^7.32.0",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.45.5",
|
||||
"@types/node": "~16.11.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.6",
|
||||
"@anticrm/hr": "~0.6.0"
|
||||
}
|
||||
}
|
26
plugins/hr-assets/src/index.ts
Normal file
26
plugins/hr-assets/src/index.ts
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright © 2022 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 { addStringsLoader, loadMetadata } from '@anticrm/platform'
|
||||
import hr, { hrId } from '@anticrm/hr'
|
||||
|
||||
const icons = require('../assets/icons.svg') as string // eslint-disable-line
|
||||
loadMetadata(hr.icon, {
|
||||
HR: `${icons}#hr`,
|
||||
Department: `${icons}#department`,
|
||||
Structure: `${icons}#structure`
|
||||
})
|
||||
|
||||
addStringsLoader(hrId, async (lang: string) => await import(`../lang/${lang}.json`))
|
16
plugins/hr-assets/tsconfig.json
Normal file
16
plugins/hr-assets/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
7
plugins/hr-resources/.eslintrc.js
Normal file
7
plugins/hr-resources/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/ui/config/eslint.config.json'],
|
||||
parserOptions: { tsconfigRootDir: __dirname },
|
||||
settings: {
|
||||
'svelte3/ignore-styles': () => true
|
||||
}
|
||||
}
|
5
plugins/hr-resources/config/rig.json
Normal file
5
plugins/hr-resources/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@anticrm/platform-rig",
|
||||
"rigProfile": "ui"
|
||||
}
|
46
plugins/hr-resources/package.json
Normal file
46
plugins/hr-resources/package.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@anticrm/hr-resources",
|
||||
"version": "0.6.0",
|
||||
"main": "src/index.ts",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "tsc --noEmit",
|
||||
"build:docs": "api-extractor run --local",
|
||||
"lint": "svelte-check && eslint",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"format": "prettier --write --plugin-search-dir=. src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"svelte-loader": "^3.1.2",
|
||||
"sass": "^1.37.5",
|
||||
"svelte-preprocess": "^4.10.5",
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"prettier-plugin-svelte": "^2.7.0",
|
||||
"eslint": "^7.32.0",
|
||||
"prettier": "^2.4.1",
|
||||
"svelte-check": "^2.7.0",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.6",
|
||||
"svelte": "^3.47",
|
||||
"@anticrm/hr": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/presentation": "~0.6.2",
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/panel": "~0.6.0",
|
||||
"@anticrm/contact": "~0.6.5",
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/contact-resources": "~0.6.0",
|
||||
"@anticrm/setting": "~0.6.1",
|
||||
"@anticrm/attachment": "~0.6.1"
|
||||
}
|
||||
}
|
5
plugins/hr-resources/postcss.config.js
Normal file
5
plugins/hr-resources/postcss.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')
|
||||
]
|
||||
}
|
89
plugins/hr-resources/src/components/CreateDepartment.svelte
Normal file
89
plugins/hr-resources/src/components/CreateDepartment.svelte
Normal file
@ -0,0 +1,89 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { Card, getClient, SpaceSelector, UserBox } from '@anticrm/presentation'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import hr from '../plugin'
|
||||
|
||||
export let space = hr.ids.Head
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let name: string = ''
|
||||
let lead: Ref<Employee> | null = null
|
||||
|
||||
export function canClose (): boolean {
|
||||
return name === ''
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function createDepartment () {
|
||||
const id = await client.createDoc(hr.class.Department, space, {
|
||||
name,
|
||||
description: '',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: [],
|
||||
teamLead: lead
|
||||
})
|
||||
|
||||
dispatch('close', id)
|
||||
}
|
||||
const manager = createFocusManager()
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
<Card
|
||||
label={hr.string.CreateDepartment}
|
||||
okAction={createDepartment}
|
||||
canSave={!!name}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="flex-row-center clear-mins">
|
||||
<div class="mr-3">
|
||||
<Button focusIndex={1} icon={hr.icon.Department} size={'medium'} kind={'link-bordered'} disabled />
|
||||
</div>
|
||||
<EditBox
|
||||
focusIndex={2}
|
||||
bind:value={name}
|
||||
placeholder={hr.string.DepartmentPlaceholder}
|
||||
maxWidth={'37.5rem'}
|
||||
kind={'large-style'}
|
||||
focus
|
||||
/>
|
||||
</div>
|
||||
<svelte:fragment slot="header">
|
||||
<SpaceSelector _class={hr.class.Department} label={hr.string.ParentDepartmentLabel} bind:space />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="pool">
|
||||
<UserBox
|
||||
focusIndex={3}
|
||||
_class={contact.class.Employee}
|
||||
label={hr.string.TeamLead}
|
||||
placeholder={hr.string.TeamLead}
|
||||
bind:value={lead}
|
||||
allowDeselect
|
||||
titleDeselect={hr.string.UnAssignLead}
|
||||
kind={'no-border'}
|
||||
size={'small'}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
151
plugins/hr-resources/src/components/DepartmentCard.svelte
Normal file
151
plugins/hr-resources/src/components/DepartmentCard.svelte
Normal file
@ -0,0 +1,151 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, WithLookup } from '@anticrm/core'
|
||||
import { Department } from '@anticrm/hr'
|
||||
import { Avatar, getClient, UsersPopup } from '@anticrm/presentation'
|
||||
import CreateDepartment from './CreateDepartment.svelte'
|
||||
import DepartmentCard from './DepartmentCard.svelte'
|
||||
import hr from '../plugin'
|
||||
import { IconAdd, IconMoreV, Button, eventToHTMLElement, Label, showPopup, ActionIcon } from '@anticrm/ui'
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import { EmployeePresenter } from '@anticrm/contact-resources'
|
||||
import DepartmentStaff from './DepartmentStaff.svelte'
|
||||
import { Menu } from '@anticrm/view-resources'
|
||||
|
||||
export let value: WithLookup<Department>
|
||||
export let descendants: Map<Ref<Department>, WithLookup<Department>[]>
|
||||
|
||||
$: currentDescendants = descendants.get(value._id) ?? []
|
||||
let expand = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
function toggle () {
|
||||
if (currentDescendants.length === 0) return
|
||||
expand = !expand
|
||||
}
|
||||
|
||||
async function changeLead (result: Employee | null | undefined): Promise<void> {
|
||||
if (result === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const newLead = result === null ? null : result._id
|
||||
if (newLead !== value.teamLead) {
|
||||
await client.update(value, { teamLead: newLead })
|
||||
}
|
||||
}
|
||||
|
||||
function openLeadEditor (event: MouseEvent) {
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{
|
||||
_class: contact.class.Employee,
|
||||
selected: value.$lookup?.teamLead,
|
||||
allowDeselect: true,
|
||||
placeholder: hr.string.TeamLead
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
changeLead
|
||||
)
|
||||
}
|
||||
|
||||
function createChild (e: MouseEvent) {
|
||||
showPopup(CreateDepartment, { space: value._id }, eventToHTMLElement(e), (res) => {
|
||||
if (res && !expand) expand = true
|
||||
})
|
||||
}
|
||||
|
||||
function editMembers (e: MouseEvent) {
|
||||
showPopup(DepartmentStaff, { _id: value._id }, 'float')
|
||||
}
|
||||
|
||||
function showMenu (e: MouseEvent) {
|
||||
showPopup(
|
||||
Menu,
|
||||
{ object: value, baseMenuClass: value._class },
|
||||
{
|
||||
getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: e.clientX, y: e.clientY })
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-center w-full px-4">
|
||||
<div
|
||||
class="w-full mt-2 mb-2 container flex"
|
||||
class:cursor-pointer={currentDescendants.length}
|
||||
on:click|stopPropagation={toggle}
|
||||
on:contextmenu|preventDefault={showMenu}
|
||||
>
|
||||
{#if currentDescendants.length}
|
||||
<div class="verticalDivider" />
|
||||
<div class="verticalDivider" />
|
||||
{/if}
|
||||
<div class="flex-between pt-4 pb-4 pr-4 pl-2 w-full">
|
||||
<div class="flex-center">
|
||||
<Avatar size={'medium'} avatar={value.avatar} icon={hr.icon.Department} />
|
||||
<div class="flex-row ml-2">
|
||||
<div class="fs-title">
|
||||
{value.name}
|
||||
</div>
|
||||
<div class="cursor-pointer" on:click|stopPropagation={editMembers}>
|
||||
<Label label={hr.string.MemberCount} params={{ count: value.members.length }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-center mr-2">
|
||||
<div class="mr-2">
|
||||
<EmployeePresenter
|
||||
value={value.$lookup?.teamLead}
|
||||
avatarSize={'small'}
|
||||
shouldShowAvatar
|
||||
shouldShowPlaceholder
|
||||
shouldShowName={false}
|
||||
tooltipLabels={{
|
||||
personLabel: hr.string.TeamLeadTooltip,
|
||||
placeholderLabel: hr.string.AssignLead
|
||||
}}
|
||||
onEmployeeEdit={openLeadEditor}
|
||||
/>
|
||||
</div>
|
||||
<Button icon={IconAdd} on:click={createChild} />
|
||||
<ActionIcon icon={IconMoreV} size={'medium'} action={showMenu} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if expand && currentDescendants.length}
|
||||
<div class="ml-8">
|
||||
{#each descendants.get(value._id) ?? [] as nested}
|
||||
<DepartmentCard value={nested} {descendants} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--theme-zone-border);
|
||||
background-color: var(--board-card-bg-color);
|
||||
}
|
||||
|
||||
.verticalDivider {
|
||||
width: 1px;
|
||||
margin-left: 0.125rem;
|
||||
background-color: var(--theme-zone-border);
|
||||
}
|
||||
</style>
|
41
plugins/hr-resources/src/components/DepartmentEditor.svelte
Normal file
41
plugins/hr-resources/src/components/DepartmentEditor.svelte
Normal file
@ -0,0 +1,41 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { Department } from '@anticrm/hr'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { SpaceSelector } from '@anticrm/presentation'
|
||||
import { ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
import hr from '../plugin'
|
||||
|
||||
export let value: Ref<Department> | undefined
|
||||
export let label: IntlString = hr.string.Department
|
||||
export let onChange: (value: any) => void
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
<SpaceSelector
|
||||
_class={hr.class.Department}
|
||||
label={hr.string.ParentDepartmentLabel}
|
||||
{size}
|
||||
{kind}
|
||||
{justify}
|
||||
{width}
|
||||
bind:space={value}
|
||||
on:change={(e) => onChange(e.detail)}
|
||||
/>
|
162
plugins/hr-resources/src/components/DepartmentStaff.svelte
Normal file
162
plugins/hr-resources/src/components/DepartmentStaff.svelte
Normal file
@ -0,0 +1,162 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
|
||||
import { Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
||||
import { Department, Staff } from '@anticrm/hr'
|
||||
import { Avatar, createQuery, MessageBox, getClient, UsersPopup } from '@anticrm/presentation'
|
||||
import { Scroller, Panel, Button, showPopup, eventToHTMLElement } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import StaffPresenter from './StaffPresenter.svelte'
|
||||
import hr from '../plugin'
|
||||
|
||||
export let _id: Ref<Department> | undefined
|
||||
let value: Department | undefined
|
||||
let employees: WithLookup<Staff>[] = []
|
||||
let accounts: EmployeeAccount[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const departmentQuery = createQuery()
|
||||
const query = createQuery()
|
||||
const accountsQuery = createQuery()
|
||||
const client = getClient()
|
||||
|
||||
$: _id &&
|
||||
value === undefined &&
|
||||
departmentQuery.query(
|
||||
hr.class.Department,
|
||||
{
|
||||
_id
|
||||
},
|
||||
(res) => ([value] = res)
|
||||
)
|
||||
|
||||
$: value &&
|
||||
accountsQuery.query(
|
||||
contact.class.EmployeeAccount,
|
||||
{
|
||||
_id: { $in: value.members as Ref<EmployeeAccount>[] }
|
||||
},
|
||||
(res) => {
|
||||
accounts = res
|
||||
}
|
||||
)
|
||||
|
||||
$: accounts.length &&
|
||||
query.query(
|
||||
hr.mixin.Staff,
|
||||
{
|
||||
_id: { $in: accounts.map((p) => p.employee) as Ref<Staff>[] }
|
||||
},
|
||||
(res) => {
|
||||
employees = res
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
name: SortingOrder.Descending
|
||||
},
|
||||
lookup: {
|
||||
department: hr.class.Department
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function add (e: MouseEvent) {
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{
|
||||
_class: contact.class.Employee,
|
||||
ignoreUsers: employees.filter((p) => p.department === _id).map((p) => p._id)
|
||||
},
|
||||
eventToHTMLElement(e),
|
||||
addMember
|
||||
)
|
||||
}
|
||||
|
||||
async function addMember (employee: Employee | undefined): Promise<void> {
|
||||
if (employee === undefined || value === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const hierarchy = client.getHierarchy()
|
||||
if (!hierarchy.hasMixin(employee, hr.mixin.Staff)) {
|
||||
await client.createMixin(employee._id, employee._class, employee.space, hr.mixin.Staff, {
|
||||
department: value._id
|
||||
})
|
||||
} else {
|
||||
const staff = hierarchy.as(employee, hr.mixin.Staff)
|
||||
if (staff.department === value._id) return
|
||||
const current = await client.findOne(hr.class.Department, {
|
||||
_id: staff.department
|
||||
})
|
||||
if (current !== undefined) {
|
||||
showPopup(
|
||||
MessageBox,
|
||||
{
|
||||
label: hr.string.MoveStaff,
|
||||
message: hr.string.MoveStaffDescr,
|
||||
params: {
|
||||
current: current.name,
|
||||
department: value.name
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
async (res?: boolean) => {
|
||||
if (res === true && value !== undefined) {
|
||||
await client.updateMixin(employee._id, employee._class, employee.space, hr.mixin.Staff, {
|
||||
department: value._id
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
await client.updateMixin(employee._id, employee._class, employee.space, hr.mixin.Staff, {
|
||||
department: value._id
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Panel
|
||||
isHeader={true}
|
||||
isAside={false}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="antiTitle icon-wrapper">
|
||||
{#if value}
|
||||
<div class="wrapped-icon"><Avatar size={'medium'} avatar={value.avatar} icon={hr.icon.Department} /></div>
|
||||
<div class="title-wrapper">
|
||||
<span class="wrapped-title">{value.name}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="utils">
|
||||
<Button label={hr.string.AddEmployee} kind={'primary'} on:click={add} />
|
||||
</svelte:fragment>
|
||||
|
||||
<Scroller>
|
||||
{#each employees as value}
|
||||
<StaffPresenter {value} />
|
||||
{/each}
|
||||
</Scroller>
|
||||
</Panel>
|
164
plugins/hr-resources/src/components/EditDepartment.svelte
Normal file
164
plugins/hr-resources/src/components/EditDepartment.svelte
Normal file
@ -0,0 +1,164 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery, EditableAvatar, getClient } from '@anticrm/presentation'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { createFocusManager, EditBox, FocusHandler } from '@anticrm/ui'
|
||||
|
||||
import { ActionContext } from '@anticrm/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Department } from '@anticrm/hr'
|
||||
import core, { getCurrentAccount, Ref, Space } from '@anticrm/core'
|
||||
import hr from '../plugin'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import attachment from '@anticrm/attachment'
|
||||
import { ChannelsEditor } from '@anticrm/contact-resources'
|
||||
import setting, { IntegrationType } from '@anticrm/setting'
|
||||
|
||||
export let _id: Ref<Department>
|
||||
let object: Department | undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
query.query(
|
||||
hr.class.Department,
|
||||
{ _id },
|
||||
(res) => {
|
||||
object = res[0]
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
|
||||
async function onAvatarDone (e: any) {
|
||||
if (object === undefined) return
|
||||
const uploadFile = await getResource(attachment.helper.UploadFile)
|
||||
const deleteFile = await getResource(attachment.helper.DeleteFile)
|
||||
const { file: avatar } = e.detail
|
||||
|
||||
if (object.avatar != null) {
|
||||
await deleteFile(object.avatar)
|
||||
}
|
||||
const uuid = await uploadFile(avatar)
|
||||
await client.updateDoc(object._class, object.space, object._id, {
|
||||
avatar: uuid
|
||||
})
|
||||
}
|
||||
|
||||
async function removeAvatar (): Promise<void> {
|
||||
if (object === undefined) return
|
||||
const deleteFile = await getResource(attachment.helper.DeleteFile)
|
||||
if (object.avatar != null) {
|
||||
await client.updateDoc(object._class, object.space, object._id, {
|
||||
avatar: null
|
||||
})
|
||||
await deleteFile(object.avatar)
|
||||
}
|
||||
}
|
||||
|
||||
async function nameChange (): Promise<void> {
|
||||
if (object === undefined) return
|
||||
await client.update(object, {
|
||||
name: object.name
|
||||
})
|
||||
}
|
||||
|
||||
const manager = createFocusManager()
|
||||
|
||||
const _update = (result: any): void => {
|
||||
dispatch('update', result)
|
||||
}
|
||||
|
||||
let integrations: Set<Ref<IntegrationType>> = new Set<Ref<IntegrationType>>()
|
||||
const accountId = getCurrentAccount()._id
|
||||
const settingsQuery = createQuery()
|
||||
$: settingsQuery.query(
|
||||
setting.class.Integration,
|
||||
{ space: accountId as string as Ref<Space>, disabled: false },
|
||||
(res) => {
|
||||
integrations = new Set(res.map((p) => p.type))
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
context={{
|
||||
mode: 'editor'
|
||||
}}
|
||||
/>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
icon={hr.icon.Department}
|
||||
title={object.name}
|
||||
{object}
|
||||
isHeader={false}
|
||||
isAside={true}
|
||||
on:update={(ev) => _update(ev.detail)}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="flex-row-stretch flex-grow">
|
||||
<div class="mr-8">
|
||||
{#key object}
|
||||
<EditableAvatar
|
||||
avatar={object.avatar}
|
||||
size={'x-large'}
|
||||
icon={hr.icon.Department}
|
||||
on:done={onAvatarDone}
|
||||
on:remove={removeAvatar}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
<div class="flex-grow flex-col">
|
||||
<div class="name">
|
||||
<EditBox
|
||||
placeholder={core.string.Name}
|
||||
maxWidth="20rem"
|
||||
bind:value={object.name}
|
||||
on:change={nameChange}
|
||||
focusIndex={1}
|
||||
/>
|
||||
</div>
|
||||
<div class="separator" />
|
||||
<div class="flex-row-center">
|
||||
<ChannelsEditor
|
||||
attachedTo={object._id}
|
||||
attachedClass={object._class}
|
||||
{integrations}
|
||||
focusIndex={10}
|
||||
on:click
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.name {
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
.separator {
|
||||
margin: 1rem 0;
|
||||
height: 1px;
|
||||
background-color: var(--divider-color);
|
||||
}
|
||||
</style>
|
36
plugins/hr-resources/src/components/StaffPresenter.svelte
Normal file
36
plugins/hr-resources/src/components/StaffPresenter.svelte
Normal file
@ -0,0 +1,36 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatName } from '@anticrm/contact'
|
||||
import { WithLookup } from '@anticrm/core'
|
||||
import { Staff } from '@anticrm/hr'
|
||||
import { Avatar } from '@anticrm/presentation'
|
||||
|
||||
export let value: WithLookup<Staff>
|
||||
</script>
|
||||
|
||||
<div class="flex-between w-full p-4">
|
||||
<div class="flex-row-center">
|
||||
<Avatar avatar={value.avatar} size={'medium'} />
|
||||
<div class="fs-title ml-2">
|
||||
{formatName(value.name)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{#if value.$lookup?.department}
|
||||
{value.$lookup.department.name}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
85
plugins/hr-resources/src/components/Structure.svelte
Normal file
85
plugins/hr-resources/src/components/Structure.svelte
Normal file
@ -0,0 +1,85 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { DocumentQuery, Ref } from '@anticrm/core'
|
||||
import { Button, Icon, Label, Scroller, SearchEdit, showPopup, IconAdd, eventToHTMLElement } from '@anticrm/ui'
|
||||
import type { Department } from '@anticrm/hr'
|
||||
import hr from '../plugin'
|
||||
import CreateDepartment from './CreateDepartment.svelte'
|
||||
import DepartmentCard from './DepartmentCard.svelte'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import contact from '@anticrm/contact'
|
||||
|
||||
let search = ''
|
||||
let resultQuery: DocumentQuery<Department> = {}
|
||||
|
||||
function updateResultQuery (search: string): void {
|
||||
resultQuery = search === '' ? {} : { $search: search }
|
||||
}
|
||||
|
||||
function showCreateDialog (ev: MouseEvent) {
|
||||
showPopup(CreateDepartment, {}, eventToHTMLElement(ev))
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let descendants: Map<Ref<Department>, Department[]> = new Map<Ref<Department>, Department[]>()
|
||||
let head: Department | undefined
|
||||
|
||||
query.query(
|
||||
hr.class.Department,
|
||||
resultQuery,
|
||||
(res) => {
|
||||
head = res.find((p) => p._id === hr.ids.Head)
|
||||
descendants.clear()
|
||||
for (const doc of res) {
|
||||
const current = descendants.get(doc.space)
|
||||
if (!current) {
|
||||
descendants.set(doc.space, [doc])
|
||||
} else {
|
||||
current.push(doc)
|
||||
descendants.set(doc.space, current)
|
||||
}
|
||||
}
|
||||
descendants = descendants
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
teamLead: contact.class.Employee
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="ac-header full divide">
|
||||
<div class="ac-header__wrap-title">
|
||||
<div class="ac-header__icon"><Icon icon={hr.icon.Structure} size={'small'} /></div>
|
||||
<span class="ac-header__title"><Label label={hr.string.Structure} /></span>
|
||||
</div>
|
||||
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
updateResultQuery(search)
|
||||
}}
|
||||
/>
|
||||
<Button label={hr.string.CreateDepartmentLabel} icon={IconAdd} kind={'primary'} on:click={showCreateDialog} />
|
||||
</div>
|
||||
|
||||
<Scroller>
|
||||
{#if head}
|
||||
<DepartmentCard value={head} {descendants} />
|
||||
{/if}
|
||||
</Scroller>
|
29
plugins/hr-resources/src/index.ts
Normal file
29
plugins/hr-resources/src/index.ts
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright © 2022 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 { Resources } from '@anticrm/platform'
|
||||
import Structure from './components/Structure.svelte'
|
||||
import DepartmentStaff from './components/DepartmentStaff.svelte'
|
||||
import EditDepartment from './components/EditDepartment.svelte'
|
||||
import DepartmentEditor from './components/DepartmentEditor.svelte'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
Structure,
|
||||
EditDepartment,
|
||||
DepartmentStaff,
|
||||
DepartmentEditor
|
||||
}
|
||||
})
|
36
plugins/hr-resources/src/plugin.ts
Normal file
36
plugins/hr-resources/src/plugin.ts
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright © 2022 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 hr, { hrId } from '@anticrm/hr'
|
||||
import { IntlString, mergeIds } from '@anticrm/platform'
|
||||
|
||||
export default mergeIds(hrId, hr, {
|
||||
string: {
|
||||
Department: '' as IntlString,
|
||||
ParentDepartmentLabel: '' as IntlString,
|
||||
Structure: '' as IntlString,
|
||||
CreateDepartment: '' as IntlString,
|
||||
CreateDepartmentLabel: '' as IntlString,
|
||||
DepartmentPlaceholder: '' as IntlString,
|
||||
TeamLead: '' as IntlString,
|
||||
UnAssignLead: '' as IntlString,
|
||||
MemberCount: '' as IntlString,
|
||||
AssignLead: '' as IntlString,
|
||||
TeamLeadTooltip: '' as IntlString,
|
||||
MoveStaff: '' as IntlString,
|
||||
MoveStaffDescr: '' as IntlString,
|
||||
AddEmployee: '' as IntlString
|
||||
}
|
||||
})
|
5
plugins/hr-resources/svelte.config.js
Normal file
5
plugins/hr-resources/svelte.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
const sveltePreprocess = require('svelte-preprocess')
|
||||
|
||||
module.exports = {
|
||||
preprocess: sveltePreprocess()
|
||||
};
|
16
plugins/hr-resources/tsconfig.json
Normal file
16
plugins/hr-resources/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
7
plugins/hr/.eslintrc.js
Normal file
7
plugins/hr/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
plugins/hr/.npmignore
Normal file
4
plugins/hr/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
18
plugins/hr/config/rig.json
Normal file
18
plugins/hr/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
33
plugins/hr/package.json
Normal file
33
plugins/hr/package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@anticrm/hr",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.45.5",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/contact": "~0.6.5",
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/platform": "~0.6.6"
|
||||
}
|
||||
}
|
68
plugins/hr/src/index.ts
Normal file
68
plugins/hr/src/index.ts
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// Copyright © 2022 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 '@anticrm/contact'
|
||||
import type { Class, Doc, Mixin, Ref, Space } from '@anticrm/core'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Department extends Space {
|
||||
space: Ref<Department>
|
||||
avatar?: string | null
|
||||
teamLead: Ref<Employee> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Staff extends Employee {
|
||||
department: Ref<Department>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const hrId = 'hr' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const hr = plugin(hrId, {
|
||||
app: {
|
||||
HR: '' as Ref<Doc>
|
||||
},
|
||||
class: {
|
||||
Department: '' as Ref<Class<Department>>
|
||||
},
|
||||
mixin: {
|
||||
Staff: '' as Ref<Mixin<Staff>>
|
||||
},
|
||||
icon: {
|
||||
HR: '' as Asset,
|
||||
Department: '' as Asset,
|
||||
Structure: '' as Asset
|
||||
},
|
||||
ids: {
|
||||
Head: '' as Ref<Department>
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export default hr
|
9
plugins/hr/tsconfig.json
Normal file
9
plugins/hr/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"lib": ["esnext", "dom"]
|
||||
}
|
||||
}
|
35
rush.json
35
rush.json
@ -1327,6 +1327,41 @@
|
||||
"packageName": "@anticrm/pod-backup",
|
||||
"projectFolder": "pods/backup",
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/hr",
|
||||
"projectFolder": "plugins/hr",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/hr-assets",
|
||||
"projectFolder": "plugins/hr-assets",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/hr-resources",
|
||||
"projectFolder": "plugins/hr-resources",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/model-hr",
|
||||
"projectFolder": "models/hr",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server-hr",
|
||||
"projectFolder": "server-plugins/hr",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/model-server-hr",
|
||||
"projectFolder": "models/server-hr",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server-hr-resources",
|
||||
"projectFolder": "server-plugins/hr-resources",
|
||||
"shouldPublish": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
7
server-plugins/hr-resources/.eslintrc.js
Normal file
7
server-plugins/hr-resources/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
server-plugins/hr-resources/.npmignore
Normal file
4
server-plugins/hr-resources/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
18
server-plugins/hr-resources/config/rig.json
Normal file
18
server-plugins/hr-resources/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
35
server-plugins/hr-resources/package.json
Normal file
35
server-plugins/hr-resources/package.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@anticrm/server-hr-resources",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.45.5",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/platform": "~0.6.6",
|
||||
"@anticrm/server-core": "~0.6.1",
|
||||
"@anticrm/contact": "~0.6.5",
|
||||
"@anticrm/hr": "~0.6.0"
|
||||
}
|
||||
}
|
132
server-plugins/hr-resources/src/index.ts
Normal file
132
server-plugins/hr-resources/src/index.ts
Normal file
@ -0,0 +1,132 @@
|
||||
//
|
||||
// Copyright © 2022 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 contact, { Employee } from '@anticrm/contact'
|
||||
import core, { Account, Ref, SortingOrder, Tx, TxFactory, TxMixin } from '@anticrm/core'
|
||||
import hr, { Department, Staff } from '@anticrm/hr'
|
||||
import { extractTx, TriggerControl } from '@anticrm/server-core'
|
||||
|
||||
async function getOldDepartment (
|
||||
currentTx: TxMixin<Employee, Staff>,
|
||||
control: TriggerControl
|
||||
): Promise<Ref<Department> | undefined> {
|
||||
const txes = await control.findAll<TxMixin<Employee, Staff>>(
|
||||
core.class.TxMixin,
|
||||
{
|
||||
objectId: currentTx.objectId
|
||||
},
|
||||
{ sort: { modifiedOn: SortingOrder.Ascending } }
|
||||
)
|
||||
let lastDepartment: Ref<Department> | undefined
|
||||
for (const tx of txes) {
|
||||
if (tx._id === currentTx._id) continue
|
||||
if (tx.attributes?.department !== undefined) {
|
||||
lastDepartment = tx.attributes.department
|
||||
}
|
||||
}
|
||||
return lastDepartment
|
||||
}
|
||||
|
||||
async function buildHierarchy (_id: Ref<Department>, control: TriggerControl): Promise<Ref<Department>[]> {
|
||||
const res: Ref<Department>[] = []
|
||||
if (_id === hr.ids.Head) return [hr.ids.Head]
|
||||
const department = (
|
||||
await control.findAll(hr.class.Department, {
|
||||
_id
|
||||
})
|
||||
)[0]
|
||||
if (department !== undefined) {
|
||||
const ancestors = await buildHierarchy(department.space, control)
|
||||
return [department._id, ...ancestors]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
function exlude (first: Ref<Department>[], second: Ref<Department>[]): Ref<Department>[] {
|
||||
const set = new Set(first)
|
||||
const res: Ref<Department>[] = []
|
||||
for (const department of second) {
|
||||
if (!set.has(department)) {
|
||||
res.push(department)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
function getTxes (
|
||||
factory: TxFactory,
|
||||
account: Ref<Account>,
|
||||
added: Ref<Department>[],
|
||||
removed?: Ref<Department>[]
|
||||
): Tx[] {
|
||||
const pushTxes = added.map((dep) =>
|
||||
factory.createTxUpdateDoc(hr.class.Department, core.space.Space, dep, {
|
||||
$push: { members: account }
|
||||
})
|
||||
)
|
||||
if (removed === undefined) return pushTxes
|
||||
const pullTxes = removed.map((dep) =>
|
||||
factory.createTxUpdateDoc(hr.class.Department, core.space.Space, dep, {
|
||||
$pull: { members: account }
|
||||
})
|
||||
)
|
||||
return [...pullTxes, ...pushTxes]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnDepartmentStaff (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = extractTx(tx)
|
||||
if (core.class.TxMixin !== actualTx._class) {
|
||||
return []
|
||||
}
|
||||
const ctx = actualTx as TxMixin<Employee, Staff>
|
||||
if (ctx.mixin !== hr.mixin.Staff) {
|
||||
return []
|
||||
}
|
||||
|
||||
const targetAccount = (
|
||||
await control.modelDb.findAll(contact.class.EmployeeAccount, {
|
||||
employee: ctx.objectId
|
||||
})
|
||||
)[0]
|
||||
if (targetAccount === undefined) return []
|
||||
|
||||
if (ctx.attributes.department !== undefined) {
|
||||
const lastDepartment = await getOldDepartment(ctx, control)
|
||||
|
||||
const departmentId = ctx.attributes.department
|
||||
const push = await buildHierarchy(departmentId, control)
|
||||
|
||||
if (lastDepartment === undefined) {
|
||||
return getTxes(control.txFactory, targetAccount._id, push)
|
||||
}
|
||||
|
||||
let removed = await buildHierarchy(lastDepartment, control)
|
||||
const added = exlude(removed, push)
|
||||
removed = exlude(push, removed)
|
||||
return getTxes(control.txFactory, targetAccount._id, added, removed)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default async () => ({
|
||||
trigger: {
|
||||
OnDepartmentStaff
|
||||
}
|
||||
})
|
8
server-plugins/hr-resources/tsconfig.json
Normal file
8
server-plugins/hr-resources/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib"
|
||||
}
|
||||
}
|
7
server-plugins/hr/.eslintrc.js
Normal file
7
server-plugins/hr/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
server-plugins/hr/.npmignore
Normal file
4
server-plugins/hr/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
18
server-plugins/hr/config/rig.json
Normal file
18
server-plugins/hr/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
34
server-plugins/hr/package.json
Normal file
34
server-plugins/hr/package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@anticrm/server-hr",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@types/node": "~16.11.12",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.45.5",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/platform": "~0.6.6",
|
||||
"@anticrm/server-core": "~0.6.1"
|
||||
}
|
||||
}
|
32
server-plugins/hr/src/index.ts
Normal file
32
server-plugins/hr/src/index.ts
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// Copyright © 2022 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 { Plugin, Resource } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { TriggerFunc } from '@anticrm/server-core'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const serverHrId = 'server-hr' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export default plugin(serverHrId, {
|
||||
trigger: {
|
||||
OnDepartmentStaff: '' as Resource<TriggerFunc>
|
||||
}
|
||||
})
|
9
server-plugins/hr/tsconfig.json
Normal file
9
server-plugins/hr/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
@ -76,6 +76,8 @@
|
||||
"@anticrm/server-preference": "~0.6.0",
|
||||
"@anticrm/server-telegram": "~0.6.0",
|
||||
"@anticrm/server-telegram-resources": "~0.6.0",
|
||||
"@anticrm/server-hr": "~0.6.0",
|
||||
"@anticrm/server-hr-resources": "~0.6.0",
|
||||
"@anticrm/server-token": "~0.6.0",
|
||||
"@anticrm/middleware": "~0.6.0"
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import { serverTagsId } from '@anticrm/server-tags'
|
||||
import { serverTaskId } from '@anticrm/server-task'
|
||||
import { serverTrackerId } from '@anticrm/server-tracker'
|
||||
import { serverTelegramId } from '@anticrm/server-telegram'
|
||||
import { serverHrId } from '@anticrm/server-hr'
|
||||
import { Token } from '@anticrm/server-token'
|
||||
import { BroadcastCall, ClientSession, start as startJsonRpc } from '@anticrm/server-ws'
|
||||
import { Client as MinioClient } from 'minio'
|
||||
@ -137,6 +138,7 @@ export function start (
|
||||
addLocation(serverCalendarId, () => import('@anticrm/server-calendar-resources'))
|
||||
addLocation(serverGmailId, () => import('@anticrm/server-gmail-resources'))
|
||||
addLocation(serverTelegramId, () => import('@anticrm/server-telegram-resources'))
|
||||
addLocation(serverHrId, () => import('@anticrm/server-hr-resources'))
|
||||
|
||||
const middlewares: MiddlewareCreator[] = [ModifiedMiddleware.create, PrivateMiddleware.create]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user