mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 11:42:30 +03:00
Support Bitrix Lead import (#2445)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
fac40733f4
commit
27b003f35f
@ -17,6 +17,9 @@ specifiers:
|
|||||||
'@rush-temp/automation': file:./projects/automation.tgz
|
'@rush-temp/automation': file:./projects/automation.tgz
|
||||||
'@rush-temp/automation-assets': file:./projects/automation-assets.tgz
|
'@rush-temp/automation-assets': file:./projects/automation-assets.tgz
|
||||||
'@rush-temp/automation-resources': file:./projects/automation-resources.tgz
|
'@rush-temp/automation-resources': file:./projects/automation-resources.tgz
|
||||||
|
'@rush-temp/bitrix': file:./projects/bitrix.tgz
|
||||||
|
'@rush-temp/bitrix-assets': file:./projects/bitrix-assets.tgz
|
||||||
|
'@rush-temp/bitrix-resources': file:./projects/bitrix-resources.tgz
|
||||||
'@rush-temp/board': file:./projects/board.tgz
|
'@rush-temp/board': file:./projects/board.tgz
|
||||||
'@rush-temp/board-assets': file:./projects/board-assets.tgz
|
'@rush-temp/board-assets': file:./projects/board-assets.tgz
|
||||||
'@rush-temp/board-resources': file:./projects/board-resources.tgz
|
'@rush-temp/board-resources': file:./projects/board-resources.tgz
|
||||||
@ -70,6 +73,7 @@ specifiers:
|
|||||||
'@rush-temp/model-all': file:./projects/model-all.tgz
|
'@rush-temp/model-all': file:./projects/model-all.tgz
|
||||||
'@rush-temp/model-attachment': file:./projects/model-attachment.tgz
|
'@rush-temp/model-attachment': file:./projects/model-attachment.tgz
|
||||||
'@rush-temp/model-automation': file:./projects/model-automation.tgz
|
'@rush-temp/model-automation': file:./projects/model-automation.tgz
|
||||||
|
'@rush-temp/model-bitrix': file:./projects/model-bitrix.tgz
|
||||||
'@rush-temp/model-board': file:./projects/model-board.tgz
|
'@rush-temp/model-board': file:./projects/model-board.tgz
|
||||||
'@rush-temp/model-calendar': file:./projects/model-calendar.tgz
|
'@rush-temp/model-calendar': file:./projects/model-calendar.tgz
|
||||||
'@rush-temp/model-chunter': file:./projects/model-chunter.tgz
|
'@rush-temp/model-chunter': file:./projects/model-chunter.tgz
|
||||||
@ -238,6 +242,7 @@ specifiers:
|
|||||||
'@types/minio': ~7.0.11
|
'@types/minio': ~7.0.11
|
||||||
'@types/node': ~16.11.12
|
'@types/node': ~16.11.12
|
||||||
'@types/pdfkit': ~0.12.3
|
'@types/pdfkit': ~0.12.3
|
||||||
|
'@types/qs': ~6.9.7
|
||||||
'@types/request': ~2.48.8
|
'@types/request': ~2.48.8
|
||||||
'@types/sharp': ~0.30.4
|
'@types/sharp': ~0.30.4
|
||||||
'@types/tar-stream': ^2.2.2
|
'@types/tar-stream': ^2.2.2
|
||||||
@ -296,6 +301,7 @@ specifiers:
|
|||||||
mini-css-extract-plugin: ^2.2.0
|
mini-css-extract-plugin: ^2.2.0
|
||||||
minio: ^7.0.26
|
minio: ^7.0.26
|
||||||
mongodb: ^4.11.0
|
mongodb: ^4.11.0
|
||||||
|
p-queue: ~7.3.0
|
||||||
pdfkit: ~0.13.0
|
pdfkit: ~0.13.0
|
||||||
postcss: ^8.3.4
|
postcss: ^8.3.4
|
||||||
postcss-load-config: ^3.1.0
|
postcss-load-config: ^3.1.0
|
||||||
@ -308,6 +314,7 @@ specifiers:
|
|||||||
prosemirror-state: ~1.4.1
|
prosemirror-state: ~1.4.1
|
||||||
prosemirror-transform: ~1.7.0
|
prosemirror-transform: ~1.7.0
|
||||||
prosemirror-view: ~1.29.0
|
prosemirror-view: ~1.29.0
|
||||||
|
qs: ~6.11.0
|
||||||
rfc6902: ~5.0.1
|
rfc6902: ~5.0.1
|
||||||
sass: ^1.53.0
|
sass: ^1.53.0
|
||||||
sass-loader: ^12.1.0
|
sass-loader: ^12.1.0
|
||||||
@ -354,6 +361,9 @@ dependencies:
|
|||||||
'@rush-temp/automation': file:projects/automation.tgz
|
'@rush-temp/automation': file:projects/automation.tgz
|
||||||
'@rush-temp/automation-assets': file:projects/automation-assets.tgz_typescript@4.8.4
|
'@rush-temp/automation-assets': file:projects/automation-assets.tgz_typescript@4.8.4
|
||||||
'@rush-temp/automation-resources': file:projects/automation-resources.tgz_49b4785992daa3b61a639b2b31601e76
|
'@rush-temp/automation-resources': file:projects/automation-resources.tgz_49b4785992daa3b61a639b2b31601e76
|
||||||
|
'@rush-temp/bitrix': file:projects/bitrix.tgz
|
||||||
|
'@rush-temp/bitrix-assets': file:projects/bitrix-assets.tgz_typescript@4.8.4
|
||||||
|
'@rush-temp/bitrix-resources': file:projects/bitrix-resources.tgz_49b4785992daa3b61a639b2b31601e76
|
||||||
'@rush-temp/board': file:projects/board.tgz
|
'@rush-temp/board': file:projects/board.tgz
|
||||||
'@rush-temp/board-assets': file:projects/board-assets.tgz_typescript@4.8.4
|
'@rush-temp/board-assets': file:projects/board-assets.tgz_typescript@4.8.4
|
||||||
'@rush-temp/board-resources': file:projects/board-resources.tgz_49b4785992daa3b61a639b2b31601e76
|
'@rush-temp/board-resources': file:projects/board-resources.tgz_49b4785992daa3b61a639b2b31601e76
|
||||||
@ -407,6 +417,7 @@ dependencies:
|
|||||||
'@rush-temp/model-all': file:projects/model-all.tgz_typescript@4.8.4
|
'@rush-temp/model-all': file:projects/model-all.tgz_typescript@4.8.4
|
||||||
'@rush-temp/model-attachment': file:projects/model-attachment.tgz_typescript@4.8.4
|
'@rush-temp/model-attachment': file:projects/model-attachment.tgz_typescript@4.8.4
|
||||||
'@rush-temp/model-automation': file:projects/model-automation.tgz_typescript@4.8.4
|
'@rush-temp/model-automation': file:projects/model-automation.tgz_typescript@4.8.4
|
||||||
|
'@rush-temp/model-bitrix': file:projects/model-bitrix.tgz_typescript@4.8.4
|
||||||
'@rush-temp/model-board': file:projects/model-board.tgz_typescript@4.8.4
|
'@rush-temp/model-board': file:projects/model-board.tgz_typescript@4.8.4
|
||||||
'@rush-temp/model-calendar': file:projects/model-calendar.tgz_typescript@4.8.4
|
'@rush-temp/model-calendar': file:projects/model-calendar.tgz_typescript@4.8.4
|
||||||
'@rush-temp/model-chunter': file:projects/model-chunter.tgz_typescript@4.8.4
|
'@rush-temp/model-chunter': file:projects/model-chunter.tgz_typescript@4.8.4
|
||||||
@ -575,6 +586,7 @@ dependencies:
|
|||||||
'@types/minio': 7.0.14
|
'@types/minio': 7.0.14
|
||||||
'@types/node': 16.11.68
|
'@types/node': 16.11.68
|
||||||
'@types/pdfkit': 0.12.8
|
'@types/pdfkit': 0.12.8
|
||||||
|
'@types/qs': 6.9.7
|
||||||
'@types/request': 2.48.8
|
'@types/request': 2.48.8
|
||||||
'@types/sharp': 0.30.5
|
'@types/sharp': 0.30.5
|
||||||
'@types/tar-stream': 2.2.2
|
'@types/tar-stream': 2.2.2
|
||||||
@ -633,6 +645,7 @@ dependencies:
|
|||||||
mini-css-extract-plugin: 2.6.1_webpack@5.75.0
|
mini-css-extract-plugin: 2.6.1_webpack@5.75.0
|
||||||
minio: 7.0.32
|
minio: 7.0.32
|
||||||
mongodb: 4.11.0
|
mongodb: 4.11.0
|
||||||
|
p-queue: 7.3.0
|
||||||
pdfkit: 0.13.0
|
pdfkit: 0.13.0
|
||||||
postcss: 8.4.19
|
postcss: 8.4.19
|
||||||
postcss-load-config: 3.1.4_postcss@8.4.19+ts-node@10.9.1
|
postcss-load-config: 3.1.4_postcss@8.4.19+ts-node@10.9.1
|
||||||
@ -645,6 +658,7 @@ dependencies:
|
|||||||
prosemirror-state: 1.4.2
|
prosemirror-state: 1.4.2
|
||||||
prosemirror-transform: 1.7.0
|
prosemirror-transform: 1.7.0
|
||||||
prosemirror-view: 1.29.0
|
prosemirror-view: 1.29.0
|
||||||
|
qs: 6.11.0
|
||||||
rfc6902: 5.0.1
|
rfc6902: 5.0.1
|
||||||
sass: 1.56.1
|
sass: 1.56.1
|
||||||
sass-loader: 12.6.0_sass@1.56.1+webpack@5.75.0
|
sass-loader: 12.6.0_sass@1.56.1+webpack@5.75.0
|
||||||
@ -676,6 +690,21 @@ dependencies:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
/@2bad/bitrix/2.5.0:
|
||||||
|
resolution: {integrity: sha512-AXlbCsn5NV1x8H982JHk2yyNU/vtTBx34a6DdtqqgHQEYd6nfEVnHEhUF/yAmElV1/rtjubOtKVxwVdFUrwLDA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash.chunk': 4.2.7
|
||||||
|
'@types/lodash.frompairs': 4.0.7
|
||||||
|
'@types/lodash.range': 3.2.7
|
||||||
|
'@types/qs': 6.9.7
|
||||||
|
got: 11.8.2
|
||||||
|
lodash.chunk: 4.2.0
|
||||||
|
lodash.frompairs: 4.0.1
|
||||||
|
lodash.range: 3.2.0
|
||||||
|
p-queue: 6.6.2
|
||||||
|
qs: 6.11.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@_ueberdosis/prosemirror-tables/1.1.3:
|
/@_ueberdosis/prosemirror-tables/1.1.3:
|
||||||
resolution: {integrity: sha512-su3pbFi1DT89g6Cuh72TE0MWWKHmWgHcQJ3ODRkm6XfIppWaGpU49t02ur3sgJc7hUhfQXjB93aSkDgOmIii2w==}
|
resolution: {integrity: sha512-su3pbFi1DT89g6Cuh72TE0MWWKHmWgHcQJ3ODRkm6XfIppWaGpU49t02ur3sgJc7hUhfQXjB93aSkDgOmIii2w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3091,6 +3120,28 @@ packages:
|
|||||||
'@types/koa': 2.13.5
|
'@types/koa': 2.13.5
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/lodash.chunk/4.2.7:
|
||||||
|
resolution: {integrity: sha512-//tmaWHiANgToom/YYYKKqiCtlNz11fwYtMUUbaemNSbWTI+2zHtYW5nt1PHNCRWHPAJHHhn4UVFD9LKUFvatA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash': 4.14.191
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@types/lodash.frompairs/4.0.7:
|
||||||
|
resolution: {integrity: sha512-7UeH2+GF9yop4AqnPwae5/2TE+eY0WRDy0RRQtNGHjzIgdUhilRskMXvXqUcCSazvbkxasjqydXrIE1OB6bPKA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash': 4.14.191
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@types/lodash.range/3.2.7:
|
||||||
|
resolution: {integrity: sha512-sq3eRZWspMKGAE0QLH+tIE/EWeSEVKlFqaUoqmgR/c4keXiTP8ob24TUzclYKihw/ojftz6vBb632NRmxR4qrQ==}
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash': 4.14.191
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@types/lodash/4.14.191:
|
||||||
|
resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/mime-types/2.1.1:
|
/@types/mime-types/2.1.1:
|
||||||
resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==}
|
resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -6354,6 +6405,23 @@ packages:
|
|||||||
get-intrinsic: 1.1.3
|
get-intrinsic: 1.1.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/got/11.8.2:
|
||||||
|
resolution: {integrity: sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==}
|
||||||
|
engines: {node: '>=10.19.0'}
|
||||||
|
dependencies:
|
||||||
|
'@sindresorhus/is': 4.6.0
|
||||||
|
'@szmarczak/http-timer': 4.0.6
|
||||||
|
'@types/cacheable-request': 6.0.3
|
||||||
|
'@types/responselike': 1.0.0
|
||||||
|
cacheable-lookup: 5.0.4
|
||||||
|
cacheable-request: 7.0.2
|
||||||
|
decompress-response: 6.0.0
|
||||||
|
http2-wrapper: 1.0.3
|
||||||
|
lowercase-keys: 2.0.0
|
||||||
|
p-cancelable: 2.1.1
|
||||||
|
responselike: 2.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/got/11.8.5:
|
/got/11.8.5:
|
||||||
resolution: {integrity: sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==}
|
resolution: {integrity: sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==}
|
||||||
engines: {node: '>=10.19.0'}
|
engines: {node: '>=10.19.0'}
|
||||||
@ -7994,10 +8062,18 @@ packages:
|
|||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/lodash.chunk/4.2.0:
|
||||||
|
resolution: {integrity: sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lodash.debounce/4.0.8:
|
/lodash.debounce/4.0.8:
|
||||||
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/lodash.frompairs/4.0.1:
|
||||||
|
resolution: {integrity: sha512-dvqe2I+cO5MzXCMhUnfYFa9MD+/760yx2aTAN1lqEcEkf896TxgrX373igVdqSJj6tQd0jnSLE1UMuKufqqxFw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lodash.get/4.4.2:
|
/lodash.get/4.4.2:
|
||||||
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -8010,6 +8086,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/lodash.range/3.2.0:
|
||||||
|
resolution: {integrity: sha512-Fgkb7SinmuzqgIhNhAElo0BL/R1rHCnhwSZf78omqSwvWqD0kD2ssOAutQonDKH/ldS8BxA72ORYI09qAY9CYg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lodash/4.17.21:
|
/lodash/4.17.21:
|
||||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -8595,6 +8675,11 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/p-finally/1.0.0:
|
||||||
|
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/p-limit/2.3.0:
|
/p-limit/2.3.0:
|
||||||
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@ -8623,6 +8708,22 @@ packages:
|
|||||||
p-limit: 3.1.0
|
p-limit: 3.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/p-queue/6.6.2:
|
||||||
|
resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
eventemitter3: 4.0.7
|
||||||
|
p-timeout: 3.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/p-queue/7.3.0:
|
||||||
|
resolution: {integrity: sha512-5fP+yVQ0qp0rEfZoDTlP2c3RYBgxvRsw30qO+VtPPc95lyvSG+x6USSh1TuLB4n96IO6I8/oXQGsTgtna4q2nQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
eventemitter3: 4.0.7
|
||||||
|
p-timeout: 5.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/p-retry/4.6.2:
|
/p-retry/4.6.2:
|
||||||
resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==}
|
resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -8631,6 +8732,18 @@ packages:
|
|||||||
retry: 0.13.1
|
retry: 0.13.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/p-timeout/3.2.0:
|
||||||
|
resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
p-finally: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/p-timeout/5.1.0:
|
||||||
|
resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/p-try/2.2.0:
|
/p-try/2.2.0:
|
||||||
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@ -11139,7 +11252,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/account.tgz:
|
file:projects/account.tgz:
|
||||||
resolution: {integrity: sha512-QSlfOq1M5zdpWg0tNGWBu3VegEZkkPLQG2gim+GXAC7vEQd7vRsWXTMbKntwOdkovV29NDNsIcUWp6G5DUfFRg==, tarball: file:projects/account.tgz}
|
resolution: {integrity: sha512-Cu4HQ37cZjY4LU7x7/btaVEfxIjb+rKA9hll64ZloH41F54jembIyQHHufIMatcMOnPFivsRZ4P9oj7WMyvOsA==, tarball: file:projects/account.tgz}
|
||||||
name: '@rush-temp/account'
|
name: '@rush-temp/account'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -11422,6 +11535,89 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
file:projects/bitrix-assets.tgz_typescript@4.8.4:
|
||||||
|
resolution: {integrity: sha512-Hod/zdxq3gCpr1vfwff7eAusB6KTImLvny1hk3x5Rcn9Rri0SMTJKm3NY0Ds+XTCnnOFZQscD81EfIpaeQPITw==, tarball: file:projects/bitrix-assets.tgz}
|
||||||
|
id: file:projects/bitrix-assets.tgz
|
||||||
|
name: '@rush-temp/bitrix-assets'
|
||||||
|
version: 0.0.0
|
||||||
|
dependencies:
|
||||||
|
'@rushstack/heft': 0.47.11
|
||||||
|
'@types/heft-jest': 1.0.3
|
||||||
|
'@types/node': 16.11.68
|
||||||
|
'@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464
|
||||||
|
'@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4
|
||||||
|
eslint: 8.27.0
|
||||||
|
eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566
|
||||||
|
eslint-plugin-import: 2.26.0_eslint@8.27.0
|
||||||
|
eslint-plugin-n: 15.5.1_eslint@8.27.0
|
||||||
|
eslint-plugin-promise: 6.1.1_eslint@8.27.0
|
||||||
|
prettier: 2.7.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
- typescript
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
file:projects/bitrix-resources.tgz_49b4785992daa3b61a639b2b31601e76:
|
||||||
|
resolution: {integrity: sha512-QQs9ffRu6ZbMcDsIHbbqwEnqlVIFPT5SOemp5PMLCfLcAhJn4arE89dCCuVLZ6p/JMYF3g7SCy/dgUllg4nNxA==, tarball: file:projects/bitrix-resources.tgz}
|
||||||
|
id: file:projects/bitrix-resources.tgz
|
||||||
|
name: '@rush-temp/bitrix-resources'
|
||||||
|
version: 0.0.0
|
||||||
|
dependencies:
|
||||||
|
'@2bad/bitrix': 2.5.0
|
||||||
|
'@types/qs': 6.9.7
|
||||||
|
'@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464
|
||||||
|
'@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4
|
||||||
|
eslint: 8.27.0
|
||||||
|
eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566
|
||||||
|
eslint-plugin-import: 2.26.0_eslint@8.27.0
|
||||||
|
eslint-plugin-n: 15.5.1_eslint@8.27.0
|
||||||
|
eslint-plugin-promise: 6.1.1_eslint@8.27.0
|
||||||
|
eslint-plugin-svelte3: 4.0.0_eslint@8.27.0+svelte@3.53.1
|
||||||
|
fast-equals: 2.0.4
|
||||||
|
p-queue: 7.3.0
|
||||||
|
prettier: 2.7.1
|
||||||
|
prettier-plugin-svelte: 2.8.0_prettier@2.7.1+svelte@3.53.1
|
||||||
|
qs: 6.11.0
|
||||||
|
sass: 1.56.1
|
||||||
|
svelte: 3.53.1
|
||||||
|
svelte-check: 2.9.2_911be9570a99ae2f9e069a41b9f14a00
|
||||||
|
svelte-loader: 3.1.4_svelte@3.53.1
|
||||||
|
svelte-preprocess: 4.10.7_1cd24d71cb02643c0a6ca17ff2edd158
|
||||||
|
typescript: 4.8.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@babel/core'
|
||||||
|
- coffeescript
|
||||||
|
- less
|
||||||
|
- node-sass
|
||||||
|
- postcss
|
||||||
|
- postcss-load-config
|
||||||
|
- pug
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
file:projects/bitrix.tgz:
|
||||||
|
resolution: {integrity: sha512-fLYL3iRRLeMJbRIjfkylukQ/uDZ/OfDuzAvrVHW/dCu2RIEFSpbYDvTlH4Cu0CR6adruVNSP4E3F+yAZSIHmQg==, tarball: file:projects/bitrix.tgz}
|
||||||
|
name: '@rush-temp/bitrix'
|
||||||
|
version: 0.0.0
|
||||||
|
dependencies:
|
||||||
|
'@2bad/bitrix': 2.5.0
|
||||||
|
'@rushstack/heft': 0.47.11
|
||||||
|
'@types/heft-jest': 1.0.3
|
||||||
|
'@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464
|
||||||
|
'@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4
|
||||||
|
eslint: 8.27.0
|
||||||
|
eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566
|
||||||
|
eslint-plugin-import: 2.26.0_eslint@8.27.0
|
||||||
|
eslint-plugin-n: 15.5.1_eslint@8.27.0
|
||||||
|
eslint-plugin-promise: 6.1.1_eslint@8.27.0
|
||||||
|
prettier: 2.7.1
|
||||||
|
typescript: 4.8.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
file:projects/board-assets.tgz_typescript@4.8.4:
|
file:projects/board-assets.tgz_typescript@4.8.4:
|
||||||
resolution: {integrity: sha512-rjdJ/U7sW/VdIPr31Kf1TCA9NxfStFMcypFbhhP13V00V4KM15ZzjdS9dcVhR0S1mqrXSomEZqzUeHpjkyfGgA==, tarball: file:projects/board-assets.tgz}
|
resolution: {integrity: sha512-rjdJ/U7sW/VdIPr31Kf1TCA9NxfStFMcypFbhhP13V00V4KM15ZzjdS9dcVhR0S1mqrXSomEZqzUeHpjkyfGgA==, tarball: file:projects/board-assets.tgz}
|
||||||
id: file:projects/board-assets.tgz
|
id: file:projects/board-assets.tgz
|
||||||
@ -11841,7 +12037,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/dev-client-resources.tgz:
|
file:projects/dev-client-resources.tgz:
|
||||||
resolution: {integrity: sha512-Bf1K9DyTyiKCNSXwfDfnMEQvyTpwTKLxtermBHCAtH/X07jkRTQR78TityPmMY13qEP29GzVbiLdcYwMStLuoA==, tarball: file:projects/dev-client-resources.tgz}
|
resolution: {integrity: sha512-aBdWjVRiHsvyjb4ZVwV3CcoYF+LnWbnBtZjvQh0JbVTBZOZfOcXUiDjw73dugHlSu0EqemahiIpKl0B8H40f2g==, tarball: file:projects/dev-client-resources.tgz}
|
||||||
name: '@rush-temp/dev-client-resources'
|
name: '@rush-temp/dev-client-resources'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -11907,7 +12103,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/devmodel-resources.tgz_5536a2d3219f8677582bfc0330dae14a:
|
file:projects/devmodel-resources.tgz_5536a2d3219f8677582bfc0330dae14a:
|
||||||
resolution: {integrity: sha512-ipNVydhF5HfLSsx/TEb3G3Zo1Cag6f/yyDcWDXZfI73extFwzmdObyiPFPvXeSm5yjdu0EsEJH/TSSyxk3QJtA==, tarball: file:projects/devmodel-resources.tgz}
|
resolution: {integrity: sha512-4ZrlsE0DJ62seKCcvfHeMqCc/JI7gix7sw7G31SBZ5oJbm/4Uz62BplxJ+Gt57wbAzC6X1wTozIPHQI+ksaaZg==, tarball: file:projects/devmodel-resources.tgz}
|
||||||
id: file:projects/devmodel-resources.tgz
|
id: file:projects/devmodel-resources.tgz
|
||||||
name: '@rush-temp/devmodel-resources'
|
name: '@rush-temp/devmodel-resources'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -11942,7 +12138,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/devmodel.tgz_typescript@4.8.4:
|
file:projects/devmodel.tgz_typescript@4.8.4:
|
||||||
resolution: {integrity: sha512-PgNRY82kL7qujBpgY/LHg52igSugdIOfpunDxUE7zq7R9TGU5rAyCdnZvTvfnQ4ZztcC0ICzJ9z0YoqpFjctRQ==, tarball: file:projects/devmodel.tgz}
|
resolution: {integrity: sha512-eQP4GWVA+cCkwjFfcFy0r+/m/u8acg6ts/YBxVDX36+FmiOuHVvHE7aAciR7uZ0Zhz/SS3rEcW19L7ol9/BVnQ==, tarball: file:projects/devmodel.tgz}
|
||||||
id: file:projects/devmodel.tgz
|
id: file:projects/devmodel.tgz
|
||||||
name: '@rush-temp/devmodel'
|
name: '@rush-temp/devmodel'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -12107,7 +12303,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/generator.tgz:
|
file:projects/generator.tgz:
|
||||||
resolution: {integrity: sha512-9WrSXJKYs18Mv/42PcUJ93qVi9iOCFRVwm6/WMtgeSiZHUwE7EExXtlnMlB7RcpH6H6F8LRLBQ28xTC59duCMg==, tarball: file:projects/generator.tgz}
|
resolution: {integrity: sha512-MBL1q/ENOolYC7S4zK/F93ToETBseT2NO4sb2uvmw+hVok93zZdGt+L3sX6WZvQSGkIv/GV/Rd5ochzNYvYPcg==, tarball: file:projects/generator.tgz}
|
||||||
name: '@rush-temp/generator'
|
name: '@rush-temp/generator'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -12683,7 +12879,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/model-all.tgz_typescript@4.8.4:
|
file:projects/model-all.tgz_typescript@4.8.4:
|
||||||
resolution: {integrity: sha512-TZENWGRR6McuiodV3kw5TsRolcDNGyj9/oe7KaS98cElBPKtK5+0xmVMsQjCkxkelSS5XU5acxy0F13IVKS+LQ==, tarball: file:projects/model-all.tgz}
|
resolution: {integrity: sha512-ypjI0QqQeCCDYXOlHjJqJ4Xpq/KaUIvpYqHoCz3kjRHqR3IuW+nOgxCNYfk5oB5kY2MDhWym0KQ5m05WjVfyGA==, tarball: file:projects/model-all.tgz}
|
||||||
id: file:projects/model-all.tgz
|
id: file:projects/model-all.tgz
|
||||||
name: '@rush-temp/model-all'
|
name: '@rush-temp/model-all'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -12749,6 +12945,27 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
file:projects/model-bitrix.tgz_typescript@4.8.4:
|
||||||
|
resolution: {integrity: sha512-8dZO37adBNzvRYUR8UsawVY1GPRN9F4g4IZj2viQyCZBx0z5vtY+Y+xfoDbHhY23D6pFSBMEz7O0vSCrH2jPSQ==, tarball: file:projects/model-bitrix.tgz}
|
||||||
|
id: file:projects/model-bitrix.tgz
|
||||||
|
name: '@rush-temp/model-bitrix'
|
||||||
|
version: 0.0.0
|
||||||
|
dependencies:
|
||||||
|
'@rushstack/heft': 0.47.11
|
||||||
|
'@types/heft-jest': 1.0.3
|
||||||
|
'@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464
|
||||||
|
'@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4
|
||||||
|
eslint: 8.27.0
|
||||||
|
eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566
|
||||||
|
eslint-plugin-import: 2.26.0_eslint@8.27.0
|
||||||
|
eslint-plugin-n: 15.5.1_eslint@8.27.0
|
||||||
|
eslint-plugin-promise: 6.1.1_eslint@8.27.0
|
||||||
|
prettier: 2.7.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
- typescript
|
||||||
|
dev: false
|
||||||
|
|
||||||
file:projects/model-board.tgz_typescript@4.8.4:
|
file:projects/model-board.tgz_typescript@4.8.4:
|
||||||
resolution: {integrity: sha512-5I0fthDx1r5yySLmftHGUUPgvFiWEtDZO0M7nLwmfT34wPqhE7EgwsGJHSatDYm6P8e/XCbhl5L55MY58tGrJQ==, tarball: file:projects/model-board.tgz}
|
resolution: {integrity: sha512-5I0fthDx1r5yySLmftHGUUPgvFiWEtDZO0M7nLwmfT34wPqhE7EgwsGJHSatDYm6P8e/XCbhl5L55MY58tGrJQ==, tarball: file:projects/model-board.tgz}
|
||||||
id: file:projects/model-board.tgz
|
id: file:projects/model-board.tgz
|
||||||
@ -13869,7 +14086,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/pod-backup.tgz:
|
file:projects/pod-backup.tgz:
|
||||||
resolution: {integrity: sha512-aYuP6b79VLRNZxzds3pAkI3pU7nnflvilszNFThWoIUxrMF6UToUJJZsYWH41PIXprpXJqRIsYGBZhMTQobg0w==, tarball: file:projects/pod-backup.tgz}
|
resolution: {integrity: sha512-HE0Z8soW6YkoYXsUW6c/sx2ZE9I7T7KHfXtxzNuAbyokYYfaH0vGRUE4y8NZt+bDDAfzzzDOivhu2TPeJUQCYw==, tarball: file:projects/pod-backup.tgz}
|
||||||
name: '@rush-temp/pod-backup'
|
name: '@rush-temp/pod-backup'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -14050,7 +14267,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/presentation.tgz_49b4785992daa3b61a639b2b31601e76:
|
file:projects/presentation.tgz_49b4785992daa3b61a639b2b31601e76:
|
||||||
resolution: {integrity: sha512-Nry3q2egydTo0b3dfhd/ejmLBKBROZ6rpyn7943+PJe5vcriJMOazi/OQur0J3LmFl8ozg2IVqeW+A2STDOt+Q==, tarball: file:projects/presentation.tgz}
|
resolution: {integrity: sha512-s/CjB7oBP/pwiNFtVjC7R1Rs0Ejth0XA+1QgbgYZ3qDqnybD0jbwbXFwnZK8xyCWAK+HSvnzrxI9+SbqGE0p3g==, tarball: file:projects/presentation.tgz}
|
||||||
id: file:projects/presentation.tgz
|
id: file:projects/presentation.tgz
|
||||||
name: '@rush-temp/presentation'
|
name: '@rush-temp/presentation'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -14086,7 +14303,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/prod.tgz_b3a81ceaadec606c0eb174aef12a6049:
|
file:projects/prod.tgz_b3a81ceaadec606c0eb174aef12a6049:
|
||||||
resolution: {integrity: sha512-xW0K2fEEkHycO18ZB6wSk6u5Qm5X8VLVss47tsS2KHIEwWgLHAIasxhL/SA1DJClWW+kS0o42U2fj76DjLhWJw==, tarball: file:projects/prod.tgz}
|
resolution: {integrity: sha512-HF6aMWOyjHsQ6STxaZz0/dFY3zb6o4J+tz/NZYcSTdspxgHb+yuyqvfm+/RPItDiTTDO6GHVogJ8i/WYIkSyhQ==, tarball: file:projects/prod.tgz}
|
||||||
id: file:projects/prod.tgz
|
id: file:projects/prod.tgz
|
||||||
name: '@rush-temp/prod'
|
name: '@rush-temp/prod'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -14297,7 +14514,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/server-backup.tgz:
|
file:projects/server-backup.tgz:
|
||||||
resolution: {integrity: sha512-+Sxk2DeLDmhbuG/OMQMwfEJRl/atZE9ZXzdiZotTpd+Et1q9w4WH3h34RWdiCOTiInZyJZXjXF5xDF375VAzpQ==, tarball: file:projects/server-backup.tgz}
|
resolution: {integrity: sha512-mOaBUJaifcxXZ6AqSpdIiI19QTCADyT/565qkkfja5HI02gLLNdrmyRKoc65YN3UAZbM2C1l+JXMBU8r58St1A==, tarball: file:projects/server-backup.tgz}
|
||||||
name: '@rush-temp/server-backup'
|
name: '@rush-temp/server-backup'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -14923,7 +15140,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/server-tool.tgz:
|
file:projects/server-tool.tgz:
|
||||||
resolution: {integrity: sha512-sdJKsx2tdPWOmolw3blM7QpXlkN+WUikx/uRmofKYuS/Y1HZkY8UHCYLYzWoDmf2/VplseccWe6HatjkLG6c8Q==, tarball: file:projects/server-tool.tgz}
|
resolution: {integrity: sha512-5WEO5W7xOR+BMCgmj9G6+cfEtU+FSGcz7Mfb+apZ24IXhwCrn2eVh/YgDGwSRzQV4W3yeIcO0sUbvowKwLhIDQ==, tarball: file:projects/server-tool.tgz}
|
||||||
name: '@rush-temp/server-tool'
|
name: '@rush-temp/server-tool'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15556,7 +15773,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/tool.tgz:
|
file:projects/tool.tgz:
|
||||||
resolution: {integrity: sha512-otLw170db0pwKQJWhEYqLlU29LqXHyjuS6V/oYcOPxYCvWXe7Nu1NT82TtVD4DgVE4ecgsS1iR1tVfyPwWjeYQ==, tarball: file:projects/tool.tgz}
|
resolution: {integrity: sha512-eV5BJ5aZRslw3lgvX7imSH6m5EBU8wCOPmZUl4tuKxZAnpjSJA9pHyUivn6EFaJ3r+pdu+ZVSjZ3D/DEbwzAKw==, tarball: file:projects/tool.tgz}
|
||||||
name: '@rush-temp/tool'
|
name: '@rush-temp/tool'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15624,7 +15841,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/tracker-resources.tgz_49b4785992daa3b61a639b2b31601e76:
|
file:projects/tracker-resources.tgz_49b4785992daa3b61a639b2b31601e76:
|
||||||
resolution: {integrity: sha512-IcoVWcO8DdlZyXbAml4GE9UlmPBzihwEzKWq9+Xs8H1U7cDcHhNwGgF+qv8N+gH5mgBHSziEBQrKfnvF+iz5vw==, tarball: file:projects/tracker-resources.tgz}
|
resolution: {integrity: sha512-z547GvQvHWfDkFJs1VvW4xJKZEzr4O7cICzIfB/lbxZt8uVrbXMVWHxWqfLHrlHxGEalqDeSRdkZ59EQ64cveA==, tarball: file:projects/tracker-resources.tgz}
|
||||||
id: file:projects/tracker-resources.tgz
|
id: file:projects/tracker-resources.tgz
|
||||||
name: '@rush-temp/tracker-resources'
|
name: '@rush-temp/tracker-resources'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -15817,7 +16034,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/workbench-resources.tgz_49b4785992daa3b61a639b2b31601e76:
|
file:projects/workbench-resources.tgz_49b4785992daa3b61a639b2b31601e76:
|
||||||
resolution: {integrity: sha512-aZ25sMEnPTO2r84j+DL2Ej0KWk71wnvPdmJSntkdrWZH3HhLYVEd+xY0kYoDB+zm4yxLPvr94hrkoo+IG+LCFA==, tarball: file:projects/workbench-resources.tgz}
|
resolution: {integrity: sha512-hyxyGITfGafwcakPTdjJ7iQkwz25QiBIWF/QK0/sdPYTqSlxDHxkzvwBh2bKNXGuAPuqKS7aykvhrhHXqZsFXQ==, tarball: file:projects/workbench-resources.tgz}
|
||||||
id: file:projects/workbench-resources.tgz
|
id: file:projects/workbench-resources.tgz
|
||||||
name: '@rush-temp/workbench-resources'
|
name: '@rush-temp/workbench-resources'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
|
@ -40,6 +40,7 @@ const fs = __importStar(require("fs"));
|
|||||||
const install_run_1 = require("./install-run");
|
const install_run_1 = require("./install-run");
|
||||||
const PACKAGE_NAME = '@microsoft/rush';
|
const PACKAGE_NAME = '@microsoft/rush';
|
||||||
const RUSH_PREVIEW_VERSION = 'RUSH_PREVIEW_VERSION';
|
const RUSH_PREVIEW_VERSION = 'RUSH_PREVIEW_VERSION';
|
||||||
|
const INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_RUSH_LOCKFILE_PATH';
|
||||||
function _getRushVersion(logger) {
|
function _getRushVersion(logger) {
|
||||||
const rushPreviewVersion = process.env[RUSH_PREVIEW_VERSION];
|
const rushPreviewVersion = process.env[RUSH_PREVIEW_VERSION];
|
||||||
if (rushPreviewVersion !== undefined) {
|
if (rushPreviewVersion !== undefined) {
|
||||||
@ -104,7 +105,11 @@ function _run() {
|
|||||||
(0, install_run_1.runWithErrorAndStatusCode)(logger, () => {
|
(0, install_run_1.runWithErrorAndStatusCode)(logger, () => {
|
||||||
const version = _getRushVersion(logger);
|
const version = _getRushVersion(logger);
|
||||||
logger.info(`The rush.json configuration requests Rush version ${version}`);
|
logger.info(`The rush.json configuration requests Rush version ${version}`);
|
||||||
return (0, install_run_1.installAndRun)(logger, PACKAGE_NAME, version, bin, packageBinArgs);
|
const lockFilePath = process.env[INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE];
|
||||||
|
if (lockFilePath) {
|
||||||
|
logger.info(`Found ${INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE}="${lockFilePath}", installing with lockfile.`);
|
||||||
|
}
|
||||||
|
return (0, install_run_1.installAndRun)(logger, PACKAGE_NAME, version, bin, packageBinArgs, lockFilePath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_run();
|
_run();
|
||||||
|
@ -42,6 +42,7 @@ const os = __importStar(require("os"));
|
|||||||
const path = __importStar(require("path"));
|
const path = __importStar(require("path"));
|
||||||
exports.RUSH_JSON_FILENAME = 'rush.json';
|
exports.RUSH_JSON_FILENAME = 'rush.json';
|
||||||
const RUSH_TEMP_FOLDER_ENV_VARIABLE_NAME = 'RUSH_TEMP_FOLDER';
|
const RUSH_TEMP_FOLDER_ENV_VARIABLE_NAME = 'RUSH_TEMP_FOLDER';
|
||||||
|
const INSTALL_RUN_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_LOCKFILE_PATH';
|
||||||
const INSTALLED_FLAG_FILENAME = 'installed.flag';
|
const INSTALLED_FLAG_FILENAME = 'installed.flag';
|
||||||
const NODE_MODULES_FOLDER_NAME = 'node_modules';
|
const NODE_MODULES_FOLDER_NAME = 'node_modules';
|
||||||
const PACKAGE_JSON_FILENAME = 'package.json';
|
const PACKAGE_JSON_FILENAME = 'package.json';
|
||||||
@ -82,6 +83,9 @@ function _parsePackageSpecifier(rawPackageSpecifier) {
|
|||||||
* home directory.
|
* home directory.
|
||||||
*
|
*
|
||||||
* IMPORTANT: THIS CODE SHOULD BE KEPT UP TO DATE WITH Utilities.copyAndTrimNpmrcFile()
|
* IMPORTANT: THIS CODE SHOULD BE KEPT UP TO DATE WITH Utilities.copyAndTrimNpmrcFile()
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
* The text of the the .npmrc.
|
||||||
*/
|
*/
|
||||||
function _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath) {
|
function _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath) {
|
||||||
logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose
|
logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose
|
||||||
@ -121,20 +125,25 @@ function _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath) {
|
|||||||
resultLines.push(line);
|
resultLines.push(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fs.writeFileSync(targetNpmrcPath, resultLines.join(os.EOL));
|
const combinedNpmrc = resultLines.join(os.EOL);
|
||||||
|
fs.writeFileSync(targetNpmrcPath, combinedNpmrc);
|
||||||
|
return combinedNpmrc;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* syncNpmrc() copies the .npmrc file to the target folder, and also trims unusable lines from the .npmrc file.
|
* syncNpmrc() copies the .npmrc file to the target folder, and also trims unusable lines from the .npmrc file.
|
||||||
* If the source .npmrc file not exist, then syncNpmrc() will delete an .npmrc that is found in the target folder.
|
* If the source .npmrc file not exist, then syncNpmrc() will delete an .npmrc that is found in the target folder.
|
||||||
*
|
*
|
||||||
* IMPORTANT: THIS CODE SHOULD BE KEPT UP TO DATE WITH Utilities._syncNpmrc()
|
* IMPORTANT: THIS CODE SHOULD BE KEPT UP TO DATE WITH Utilities._syncNpmrc()
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
* The text of the the synced .npmrc, if one exists. If one does not exist, then undefined is returned.
|
||||||
*/
|
*/
|
||||||
function _syncNpmrc(logger, sourceNpmrcFolder, targetNpmrcFolder, useNpmrcPublish) {
|
function _syncNpmrc(logger, sourceNpmrcFolder, targetNpmrcFolder, useNpmrcPublish) {
|
||||||
const sourceNpmrcPath = path.join(sourceNpmrcFolder, !useNpmrcPublish ? '.npmrc' : '.npmrc-publish');
|
const sourceNpmrcPath = path.join(sourceNpmrcFolder, !useNpmrcPublish ? '.npmrc' : '.npmrc-publish');
|
||||||
const targetNpmrcPath = path.join(targetNpmrcFolder, '.npmrc');
|
const targetNpmrcPath = path.join(targetNpmrcFolder, '.npmrc');
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(sourceNpmrcPath)) {
|
if (fs.existsSync(sourceNpmrcPath)) {
|
||||||
_copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath);
|
return _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath);
|
||||||
}
|
}
|
||||||
else if (fs.existsSync(targetNpmrcPath)) {
|
else if (fs.existsSync(targetNpmrcPath)) {
|
||||||
// If the source .npmrc doesn't exist and there is one in the target, delete the one in the target
|
// If the source .npmrc doesn't exist and there is one in the target, delete the one in the target
|
||||||
@ -306,28 +315,43 @@ function _isPackageAlreadyInstalled(packageInstallFolder) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Delete a file. Fail silently if it does not exist.
|
||||||
|
*/
|
||||||
|
function _deleteFile(file) {
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(file);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Removes the following files and directories under the specified folder path:
|
* Removes the following files and directories under the specified folder path:
|
||||||
* - installed.flag
|
* - installed.flag
|
||||||
* -
|
* -
|
||||||
* - node_modules
|
* - node_modules
|
||||||
*/
|
*/
|
||||||
function _cleanInstallFolder(rushTempFolder, packageInstallFolder) {
|
function _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath) {
|
||||||
try {
|
try {
|
||||||
const flagFile = path.resolve(packageInstallFolder, INSTALLED_FLAG_FILENAME);
|
const flagFile = path.resolve(packageInstallFolder, INSTALLED_FLAG_FILENAME);
|
||||||
if (fs.existsSync(flagFile)) {
|
_deleteFile(flagFile);
|
||||||
fs.unlinkSync(flagFile);
|
|
||||||
}
|
|
||||||
const packageLockFile = path.resolve(packageInstallFolder, 'package-lock.json');
|
const packageLockFile = path.resolve(packageInstallFolder, 'package-lock.json');
|
||||||
if (fs.existsSync(packageLockFile)) {
|
if (lockFilePath) {
|
||||||
fs.unlinkSync(packageLockFile);
|
fs.copyFileSync(lockFilePath, packageLockFile);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Not running `npm ci`, so need to cleanup
|
||||||
|
_deleteFile(packageLockFile);
|
||||||
const nodeModulesFolder = path.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME);
|
const nodeModulesFolder = path.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME);
|
||||||
if (fs.existsSync(nodeModulesFolder)) {
|
if (fs.existsSync(nodeModulesFolder)) {
|
||||||
const rushRecyclerFolder = _ensureAndJoinPath(rushTempFolder, 'rush-recycler');
|
const rushRecyclerFolder = _ensureAndJoinPath(rushTempFolder, 'rush-recycler');
|
||||||
fs.renameSync(nodeModulesFolder, path.join(rushRecyclerFolder, `install-run-${Date.now().toString()}`));
|
fs.renameSync(nodeModulesFolder, path.join(rushRecyclerFolder, `install-run-${Date.now().toString()}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
throw new Error(`Error cleaning the package install folder (${packageInstallFolder}): ${e}`);
|
throw new Error(`Error cleaning the package install folder (${packageInstallFolder}): ${e}`);
|
||||||
}
|
}
|
||||||
@ -354,17 +378,17 @@ function _createPackageJson(packageInstallFolder, name, version) {
|
|||||||
/**
|
/**
|
||||||
* Run "npm install" in the package install folder.
|
* Run "npm install" in the package install folder.
|
||||||
*/
|
*/
|
||||||
function _installPackage(logger, packageInstallFolder, name, version) {
|
function _installPackage(logger, packageInstallFolder, name, version, command) {
|
||||||
try {
|
try {
|
||||||
logger.info(`Installing ${name}...`);
|
logger.info(`Installing ${name}...`);
|
||||||
const npmPath = getNpmPath();
|
const npmPath = getNpmPath();
|
||||||
const result = childProcess.spawnSync(npmPath, ['install'], {
|
const result = childProcess.spawnSync(npmPath, [command], {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
cwd: packageInstallFolder,
|
cwd: packageInstallFolder,
|
||||||
env: process.env
|
env: process.env
|
||||||
});
|
});
|
||||||
if (result.status !== 0) {
|
if (result.status !== 0) {
|
||||||
throw new Error('"npm install" encountered an error');
|
throw new Error(`"npm ${command}" encountered an error`);
|
||||||
}
|
}
|
||||||
logger.info(`Successfully installed ${name}@${version}`);
|
logger.info(`Successfully installed ${name}@${version}`);
|
||||||
}
|
}
|
||||||
@ -392,18 +416,19 @@ function _writeFlagFile(packageInstallFolder) {
|
|||||||
throw new Error(`Unable to create installed.flag file in ${packageInstallFolder}`);
|
throw new Error(`Unable to create installed.flag file in ${packageInstallFolder}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function installAndRun(logger, packageName, packageVersion, packageBinName, packageBinArgs) {
|
function installAndRun(logger, packageName, packageVersion, packageBinName, packageBinArgs, lockFilePath = process.env[INSTALL_RUN_LOCKFILE_PATH_VARIABLE]) {
|
||||||
const rushJsonFolder = findRushJsonFolder();
|
const rushJsonFolder = findRushJsonFolder();
|
||||||
const rushCommonFolder = path.join(rushJsonFolder, 'common');
|
const rushCommonFolder = path.join(rushJsonFolder, 'common');
|
||||||
const rushTempFolder = _getRushTempFolder(rushCommonFolder);
|
const rushTempFolder = _getRushTempFolder(rushCommonFolder);
|
||||||
const packageInstallFolder = _ensureAndJoinPath(rushTempFolder, 'install-run', `${packageName}@${packageVersion}`);
|
const packageInstallFolder = _ensureAndJoinPath(rushTempFolder, 'install-run', `${packageName}@${packageVersion}`);
|
||||||
if (!_isPackageAlreadyInstalled(packageInstallFolder)) {
|
if (!_isPackageAlreadyInstalled(packageInstallFolder)) {
|
||||||
// The package isn't already installed
|
// The package isn't already installed
|
||||||
_cleanInstallFolder(rushTempFolder, packageInstallFolder);
|
_cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath);
|
||||||
const sourceNpmrcFolder = path.join(rushCommonFolder, 'config', 'rush');
|
const sourceNpmrcFolder = path.join(rushCommonFolder, 'config', 'rush');
|
||||||
_syncNpmrc(logger, sourceNpmrcFolder, packageInstallFolder);
|
_syncNpmrc(logger, sourceNpmrcFolder, packageInstallFolder);
|
||||||
_createPackageJson(packageInstallFolder, packageName, packageVersion);
|
_createPackageJson(packageInstallFolder, packageName, packageVersion);
|
||||||
_installPackage(logger, packageInstallFolder, packageName, packageVersion);
|
const command = lockFilePath ? 'ci' : 'install';
|
||||||
|
_installPackage(logger, packageInstallFolder, packageName, packageVersion, command);
|
||||||
_writeFlagFile(packageInstallFolder);
|
_writeFlagFile(packageInstallFolder);
|
||||||
}
|
}
|
||||||
const statusMessage = `Invoking "${packageBinName} ${packageBinArgs.join(' ')}"`;
|
const statusMessage = `Invoking "${packageBinName} ${packageBinArgs.join(' ')}"`;
|
||||||
|
@ -154,6 +154,10 @@
|
|||||||
"@hcengineering/server-hr-resources": "~0.6.0",
|
"@hcengineering/server-hr-resources": "~0.6.0",
|
||||||
"@hcengineering/document": "^0.6.0",
|
"@hcengineering/document": "^0.6.0",
|
||||||
"@hcengineering/document-assets": "^0.6.0",
|
"@hcengineering/document-assets": "^0.6.0",
|
||||||
"@hcengineering/document-resources": "^0.6.0"
|
"@hcengineering/document-resources": "^0.6.0",
|
||||||
|
|
||||||
|
"@hcengineering/bitrix": "^0.6.0",
|
||||||
|
"@hcengineering/bitrix-assets": "^0.6.0",
|
||||||
|
"@hcengineering/bitrix-resources": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ import { hrId } from '@hcengineering/hr'
|
|||||||
import rekoni from '@hcengineering/rekoni'
|
import rekoni from '@hcengineering/rekoni'
|
||||||
import document, { documentId } from '@hcengineering/document'
|
import document, { documentId } from '@hcengineering/document'
|
||||||
|
|
||||||
|
import bitrix, { bitrixId } from '@hcengineering/bitrix'
|
||||||
|
|
||||||
import '@hcengineering/login-assets'
|
import '@hcengineering/login-assets'
|
||||||
import '@hcengineering/task-assets'
|
import '@hcengineering/task-assets'
|
||||||
import '@hcengineering/view-assets'
|
import '@hcengineering/view-assets'
|
||||||
@ -68,6 +70,7 @@ import '@hcengineering/board-assets'
|
|||||||
import '@hcengineering/preference-assets'
|
import '@hcengineering/preference-assets'
|
||||||
import '@hcengineering/hr-assets'
|
import '@hcengineering/hr-assets'
|
||||||
import '@hcengineering/document-assets'
|
import '@hcengineering/document-assets'
|
||||||
|
import '@hcengineering/bitrix-assets'
|
||||||
|
|
||||||
import presentation, { presentationId } from '@hcengineering/presentation'
|
import presentation, { presentationId } from '@hcengineering/presentation'
|
||||||
import { coreId } from '@hcengineering/core'
|
import { coreId } from '@hcengineering/core'
|
||||||
@ -134,6 +137,7 @@ export async function configurePlatform() {
|
|||||||
addLocation(automationId, () => import(/* webpackChunkName: "automation" */ '@hcengineering/automation-resources'))
|
addLocation(automationId, () => import(/* webpackChunkName: "automation" */ '@hcengineering/automation-resources'))
|
||||||
addLocation(hrId, () => import(/* webpackChunkName: "hr" */ '@hcengineering/hr-resources'))
|
addLocation(hrId, () => import(/* webpackChunkName: "hr" */ '@hcengineering/hr-resources'))
|
||||||
addLocation(documentId, () => import(/* webpackChunkName: "hr" */ '@hcengineering/document-resources'))
|
addLocation(documentId, () => import(/* webpackChunkName: "hr" */ '@hcengineering/document-resources'))
|
||||||
|
addLocation(bitrixId, () => import(/* webpackChunkName: "bitrix" */ '@hcengineering/bitrix-resources'))
|
||||||
|
|
||||||
setMetadata(workbench.metadata.PlatformTitle, 'Platform')
|
setMetadata(workbench.metadata.PlatformTitle, 'Platform')
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@
|
|||||||
"@hcengineering/model-preference": "~0.6.0",
|
"@hcengineering/model-preference": "~0.6.0",
|
||||||
"@hcengineering/model-hr": "~0.6.0",
|
"@hcengineering/model-hr": "~0.6.0",
|
||||||
"@hcengineering/model-server-hr": "~0.6.0",
|
"@hcengineering/model-server-hr": "~0.6.0",
|
||||||
"@hcengineering/model-document": "~0.6.0"
|
"@hcengineering/model-document": "~0.6.0",
|
||||||
|
"@hcengineering/model-bitrix": "~0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ import { createModel as preferenceModel } from '@hcengineering/model-preference'
|
|||||||
import { createModel as hrModel } from '@hcengineering/model-hr'
|
import { createModel as hrModel } from '@hcengineering/model-hr'
|
||||||
import { createModel as serverHrModel } from '@hcengineering/model-server-hr'
|
import { createModel as serverHrModel } from '@hcengineering/model-server-hr'
|
||||||
import { createModel as documentModel } from '@hcengineering/model-document'
|
import { createModel as documentModel } from '@hcengineering/model-document'
|
||||||
|
import { createModel as bitrixModel } from '@hcengineering/model-bitrix'
|
||||||
|
|
||||||
export const version: Data<Version> = jsonVersion as Data<Version>
|
export const version: Data<Version> = jsonVersion as Data<Version>
|
||||||
|
|
||||||
@ -90,6 +91,7 @@ const builders: [(b: Builder) => void, string][] = [
|
|||||||
[trackerModel, 'tracker'],
|
[trackerModel, 'tracker'],
|
||||||
[boardModel, 'board'],
|
[boardModel, 'board'],
|
||||||
[calendarModel, 'calendar'],
|
[calendarModel, 'calendar'],
|
||||||
|
[bitrixModel, 'bitrix'],
|
||||||
|
|
||||||
[serverCoreModel, 'server-core'],
|
[serverCoreModel, 'server-core'],
|
||||||
[serverAttachmentModel, 'server-attachment'],
|
[serverAttachmentModel, 'server-attachment'],
|
||||||
|
@ -35,6 +35,7 @@ import { boardOperation } from '@hcengineering/model-board'
|
|||||||
import { demoOperation } from '@hcengineering/model-demo'
|
import { demoOperation } from '@hcengineering/model-demo'
|
||||||
import { hrOperation } from '@hcengineering/model-hr'
|
import { hrOperation } from '@hcengineering/model-hr'
|
||||||
import { documentOperation } from '@hcengineering/model-document'
|
import { documentOperation } from '@hcengineering/model-document'
|
||||||
|
import { bitrixOperation } from '@hcengineering/model-bitrix'
|
||||||
|
|
||||||
export const migrateOperations: MigrateOperation[] = [
|
export const migrateOperations: MigrateOperation[] = [
|
||||||
coreOperation,
|
coreOperation,
|
||||||
@ -56,5 +57,6 @@ export const migrateOperations: MigrateOperation[] = [
|
|||||||
trackerOperation,
|
trackerOperation,
|
||||||
boardOperation,
|
boardOperation,
|
||||||
hrOperation,
|
hrOperation,
|
||||||
documentOperation
|
documentOperation,
|
||||||
|
bitrixOperation
|
||||||
]
|
]
|
||||||
|
7
models/bitrix/.eslintrc.js
Normal file
7
models/bitrix/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['./node_modules/@hcengineering/model-rig/profiles/default/config/eslint.config.json'],
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: './tsconfig.json'
|
||||||
|
}
|
||||||
|
}
|
4
models/bitrix/.npmignore
Normal file
4
models/bitrix/.npmignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!/lib/**
|
||||||
|
!CHANGELOG.md
|
||||||
|
/lib/**/__tests__/
|
18
models/bitrix/config/rig.json
Normal file
18
models/bitrix/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": "@hcengineering/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"
|
||||||
|
}
|
45
models/bitrix/package.json
Normal file
45
models/bitrix/package.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "@hcengineering/model-bitrix",
|
||||||
|
"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": {
|
||||||
|
"@hcengineering/model-rig": "~0.6.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
|
"eslint-plugin-n": "^15.4.0",
|
||||||
|
"eslint": "^8.26.0",
|
||||||
|
"@types/heft-jest": "^1.0.3",
|
||||||
|
"@typescript-eslint/parser": "^5.41.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^23.0.0",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"@rushstack/heft": "^0.47.9"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hcengineering/activity": "~0.6.0",
|
||||||
|
"@hcengineering/model": "~0.6.0",
|
||||||
|
"@hcengineering/core": "^0.6.19",
|
||||||
|
"@hcengineering/platform": "^0.6.8",
|
||||||
|
"@hcengineering/model-core": "~0.6.0",
|
||||||
|
"@hcengineering/model-attachment": "~0.6.0",
|
||||||
|
"@hcengineering/model-contact": "~0.6.1",
|
||||||
|
"@hcengineering/view": "^0.6.1",
|
||||||
|
"@hcengineering/model-view": "~0.6.0",
|
||||||
|
"@hcengineering/contact": "~0.6.8",
|
||||||
|
"@hcengineering/bitrix": "^0.6.0",
|
||||||
|
"@hcengineering/bitrix-resources": "^0.6.0",
|
||||||
|
"@hcengineering/preference": "^0.6.1",
|
||||||
|
"@hcengineering/model-preference": "~0.6.0",
|
||||||
|
"@hcengineering/setting": "~0.6.1",
|
||||||
|
"@hcengineering/ui": "^0.6.2"
|
||||||
|
}
|
||||||
|
}
|
99
models/bitrix/src/index.ts
Normal file
99
models/bitrix/src/index.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021 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, Collection, Mixin, Model, Prop, TypeRef, TypeString } from '@hcengineering/model'
|
||||||
|
import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core'
|
||||||
|
import bitrix from './plugin'
|
||||||
|
|
||||||
|
import { BitrixEntityMapping, BitrixFieldMapping, BitrixSyncDoc, Fields } from '@hcengineering/bitrix'
|
||||||
|
import { AnyAttribute, Class, Doc, Domain, Ref } from '@hcengineering/core'
|
||||||
|
|
||||||
|
import view, { createAction } from '@hcengineering/model-view'
|
||||||
|
|
||||||
|
import setting from '@hcengineering/setting'
|
||||||
|
|
||||||
|
const DOMAIN_BITRIX = 'bitrix' as Domain
|
||||||
|
|
||||||
|
@Mixin(bitrix.mixin.BitrixSyncDoc, core.class.Doc)
|
||||||
|
export class TBitrixSyncDoc extends TDoc implements BitrixSyncDoc {
|
||||||
|
type!: string
|
||||||
|
bitrixId!: string
|
||||||
|
}
|
||||||
|
|
||||||
|
@Model(bitrix.class.EntityMapping, core.class.Doc, DOMAIN_BITRIX)
|
||||||
|
export class TBitrixEntityMapping extends TDoc implements BitrixEntityMapping {
|
||||||
|
@Prop(TypeRef(core.class.Attribute), core.string.ClassPropertyLabel)
|
||||||
|
ofClass!: Ref<Class<Doc>>
|
||||||
|
|
||||||
|
type!: string
|
||||||
|
|
||||||
|
@Prop(Collection(bitrix.class.FieldMapping), bitrix.string.FieldMapping)
|
||||||
|
fields!: number
|
||||||
|
|
||||||
|
bitrixFields!: Fields
|
||||||
|
comments!: boolean
|
||||||
|
activity!: boolean
|
||||||
|
attachments!: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
@Model(bitrix.class.FieldMapping, core.class.Doc, DOMAIN_BITRIX)
|
||||||
|
export class TBitrixFieldMapping extends TAttachedDoc implements BitrixFieldMapping {
|
||||||
|
@Prop(TypeRef(core.class.Attribute), core.string.ClassPropertyLabel)
|
||||||
|
ofClass!: Ref<Class<Doc>>
|
||||||
|
|
||||||
|
@Prop(TypeString(), core.string.ClassPropertyLabel)
|
||||||
|
attributeName!: Ref<AnyAttribute>
|
||||||
|
|
||||||
|
bitrixTitle!: string
|
||||||
|
bitrixType!: string
|
||||||
|
operation!: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createModel (builder: Builder): void {
|
||||||
|
builder.createModel(TBitrixEntityMapping, TBitrixFieldMapping, TBitrixSyncDoc)
|
||||||
|
|
||||||
|
builder.createDoc(
|
||||||
|
setting.class.IntegrationType,
|
||||||
|
core.space.Model,
|
||||||
|
{
|
||||||
|
label: bitrix.string.Bitrix,
|
||||||
|
description: bitrix.string.BitrixDesc,
|
||||||
|
icon: bitrix.component.BitrixIcon,
|
||||||
|
createComponent: bitrix.component.BitrixConnect,
|
||||||
|
configureComponent: bitrix.component.BitrixConfigure
|
||||||
|
},
|
||||||
|
bitrix.integrationType.Bitrix
|
||||||
|
)
|
||||||
|
|
||||||
|
createAction(
|
||||||
|
builder,
|
||||||
|
{
|
||||||
|
action: view.actionImpl.ShowPopup,
|
||||||
|
actionProps: {
|
||||||
|
component: bitrix.component.BitrixImport
|
||||||
|
},
|
||||||
|
label: bitrix.string.BitrixImport,
|
||||||
|
icon: bitrix.icon.Bitrix,
|
||||||
|
input: 'any',
|
||||||
|
category: view.category.General,
|
||||||
|
target: core.class.Doc,
|
||||||
|
context: { mode: ['workbench', 'browser', 'editor', 'panel', 'popup'], group: 'create' }
|
||||||
|
},
|
||||||
|
bitrix.action.BitrixImport
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { bitrixOperation } from './migration'
|
21
models/bitrix/src/migration.ts
Normal file
21
models/bitrix/src/migration.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// 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 { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
|
||||||
|
|
||||||
|
export const bitrixOperation: MigrateOperation = {
|
||||||
|
async migrate (client: MigrationClient): Promise<void> {},
|
||||||
|
async upgrade (client: MigrationUpgradeClient): Promise<void> {}
|
||||||
|
}
|
37
models/bitrix/src/plugin.ts
Normal file
37
models/bitrix/src/plugin.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021 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 { bitrixId } from '@hcengineering/bitrix'
|
||||||
|
import bitrix from '@hcengineering/bitrix-resources/src/plugin'
|
||||||
|
import { IntlString, mergeIds } from '@hcengineering/platform'
|
||||||
|
import { Action } from '@hcengineering/view'
|
||||||
|
|
||||||
|
import { Ref } from '@hcengineering/core'
|
||||||
|
import type { AnyComponent } from '@hcengineering/ui'
|
||||||
|
|
||||||
|
export default mergeIds(bitrixId, bitrix, {
|
||||||
|
component: {
|
||||||
|
BitrixConnect: '' as AnyComponent,
|
||||||
|
BitrixConfigure: '' as AnyComponent,
|
||||||
|
BitrixImport: '' as AnyComponent
|
||||||
|
},
|
||||||
|
string: {
|
||||||
|
BitrixImport: '' as IntlString
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
BitrixImport: '' as Ref<Action>
|
||||||
|
}
|
||||||
|
})
|
8
models/bitrix/tsconfig.json
Normal file
8
models/bitrix/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "./node_modules/@hcengineering/model-rig/profiles/default/tsconfig.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib",
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { DocumentUpdate, Hierarchy, MixinData, MixinUpdate, ModelDb } from '.'
|
import { DocumentUpdate, Hierarchy, MixinData, MixinUpdate, ModelDb } from '.'
|
||||||
import type { Account, AttachedData, AttachedDoc, Class, Data, Doc, Mixin, Ref, Space } from './classes'
|
import type { Account, AttachedData, AttachedDoc, Class, Data, Doc, Mixin, Ref, Space, Timestamp } from './classes'
|
||||||
import { Client } from './client'
|
import { Client } from './client'
|
||||||
import core from './component'
|
import core from './component'
|
||||||
import type { DocumentQuery, FindOptions, FindResult, TxResult, WithLookup } from './storage'
|
import type { DocumentQuery, FindOptions, FindResult, TxResult, WithLookup } from './storage'
|
||||||
@ -55,9 +55,11 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
_class: Ref<Class<T>>,
|
_class: Ref<Class<T>>,
|
||||||
space: Ref<Space>,
|
space: Ref<Space>,
|
||||||
attributes: Data<T>,
|
attributes: Data<T>,
|
||||||
id?: Ref<T>
|
id?: Ref<T>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<Ref<T>> {
|
): Promise<Ref<T>> {
|
||||||
const tx = this.txFactory.createTxCreateDoc(_class, space, attributes, id)
|
const tx = this.txFactory.createTxCreateDoc(_class, space, attributes, id, modifiedOn, modifiedBy)
|
||||||
await this.client.tx(tx)
|
await this.client.tx(tx)
|
||||||
return tx.objectId
|
return tx.objectId
|
||||||
}
|
}
|
||||||
@ -69,14 +71,16 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
attachedToClass: Ref<Class<T>>,
|
attachedToClass: Ref<Class<T>>,
|
||||||
collection: string,
|
collection: string,
|
||||||
attributes: AttachedData<P>,
|
attributes: AttachedData<P>,
|
||||||
id?: Ref<P>
|
id?: Ref<P>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<Ref<P>> {
|
): Promise<Ref<P>> {
|
||||||
const tx = this.txFactory.createTxCollectionCUD<T, P>(
|
const tx = this.txFactory.createTxCollectionCUD<T, P>(
|
||||||
attachedToClass,
|
attachedToClass,
|
||||||
attachedTo,
|
attachedTo,
|
||||||
space,
|
space,
|
||||||
collection,
|
collection,
|
||||||
this.txFactory.createTxCreateDoc<P>(_class, space, attributes as unknown as Data<P>, id)
|
this.txFactory.createTxCreateDoc<P>(_class, space, attributes as unknown as Data<P>, id, modifiedOn, modifiedBy)
|
||||||
)
|
)
|
||||||
await this.client.tx(tx)
|
await this.client.tx(tx)
|
||||||
return tx.tx.objectId as unknown as Ref<P>
|
return tx.tx.objectId as unknown as Ref<P>
|
||||||
@ -90,14 +94,16 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
attachedToClass: Ref<Class<T>>,
|
attachedToClass: Ref<Class<T>>,
|
||||||
collection: string,
|
collection: string,
|
||||||
operations: DocumentUpdate<P>,
|
operations: DocumentUpdate<P>,
|
||||||
retrieve?: boolean
|
retrieve?: boolean,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<Ref<T>> {
|
): Promise<Ref<T>> {
|
||||||
const tx = this.txFactory.createTxCollectionCUD(
|
const tx = this.txFactory.createTxCollectionCUD(
|
||||||
attachedToClass,
|
attachedToClass,
|
||||||
attachedTo,
|
attachedTo,
|
||||||
space,
|
space,
|
||||||
collection,
|
collection,
|
||||||
this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve)
|
this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve, modifiedOn, modifiedBy)
|
||||||
)
|
)
|
||||||
await this.client.tx(tx)
|
await this.client.tx(tx)
|
||||||
return tx.objectId
|
return tx.objectId
|
||||||
@ -109,14 +115,16 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
objectId: Ref<P>,
|
objectId: Ref<P>,
|
||||||
attachedTo: Ref<T>,
|
attachedTo: Ref<T>,
|
||||||
attachedToClass: Ref<Class<T>>,
|
attachedToClass: Ref<Class<T>>,
|
||||||
collection: string
|
collection: string,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<Ref<T>> {
|
): Promise<Ref<T>> {
|
||||||
const tx = this.txFactory.createTxCollectionCUD(
|
const tx = this.txFactory.createTxCollectionCUD(
|
||||||
attachedToClass,
|
attachedToClass,
|
||||||
attachedTo,
|
attachedTo,
|
||||||
space,
|
space,
|
||||||
collection,
|
collection,
|
||||||
this.txFactory.createTxRemoveDoc(_class, space, objectId)
|
this.txFactory.createTxRemoveDoc(_class, space, objectId, modifiedOn, modifiedBy)
|
||||||
)
|
)
|
||||||
await this.client.tx(tx)
|
await this.client.tx(tx)
|
||||||
return tx.objectId
|
return tx.objectId
|
||||||
@ -127,14 +135,22 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
space: Ref<Space>,
|
space: Ref<Space>,
|
||||||
objectId: Ref<T>,
|
objectId: Ref<T>,
|
||||||
operations: DocumentUpdate<T>,
|
operations: DocumentUpdate<T>,
|
||||||
retrieve?: boolean
|
retrieve?: boolean,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<TxResult> {
|
): Promise<TxResult> {
|
||||||
const tx = this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve)
|
const tx = this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve, modifiedOn, modifiedBy)
|
||||||
return this.client.tx(tx)
|
return this.client.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDoc<T extends Doc>(_class: Ref<Class<T>>, space: Ref<Space>, objectId: Ref<T>): Promise<TxResult> {
|
removeDoc<T extends Doc>(
|
||||||
const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId)
|
_class: Ref<Class<T>>,
|
||||||
|
space: Ref<Space>,
|
||||||
|
objectId: Ref<T>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
|
): Promise<TxResult> {
|
||||||
|
const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId, modifiedOn, modifiedBy)
|
||||||
return this.client.tx(tx)
|
return this.client.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +159,19 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
objectClass: Ref<Class<D>>,
|
objectClass: Ref<Class<D>>,
|
||||||
objectSpace: Ref<Space>,
|
objectSpace: Ref<Space>,
|
||||||
mixin: Ref<Mixin<M>>,
|
mixin: Ref<Mixin<M>>,
|
||||||
attributes: MixinData<D, M>
|
attributes: MixinData<D, M>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<TxResult> {
|
): Promise<TxResult> {
|
||||||
const tx = this.txFactory.createTxMixin(objectId, objectClass, objectSpace, mixin, attributes)
|
const tx = this.txFactory.createTxMixin(
|
||||||
|
objectId,
|
||||||
|
objectClass,
|
||||||
|
objectSpace,
|
||||||
|
mixin,
|
||||||
|
attributes,
|
||||||
|
modifiedOn,
|
||||||
|
modifiedBy
|
||||||
|
)
|
||||||
return this.client.tx(tx)
|
return this.client.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,17 +180,33 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
objectClass: Ref<Class<D>>,
|
objectClass: Ref<Class<D>>,
|
||||||
objectSpace: Ref<Space>,
|
objectSpace: Ref<Space>,
|
||||||
mixin: Ref<Mixin<M>>,
|
mixin: Ref<Mixin<M>>,
|
||||||
attributes: MixinUpdate<D, M>
|
attributes: MixinUpdate<D, M>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<TxResult> {
|
): Promise<TxResult> {
|
||||||
const tx = this.txFactory.createTxMixin(objectId, objectClass, objectSpace, mixin, attributes)
|
const tx = this.txFactory.createTxMixin(
|
||||||
|
objectId,
|
||||||
|
objectClass,
|
||||||
|
objectSpace,
|
||||||
|
mixin,
|
||||||
|
attributes,
|
||||||
|
modifiedOn,
|
||||||
|
modifiedBy
|
||||||
|
)
|
||||||
return this.client.tx(tx)
|
return this.client.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
update<T extends Doc>(doc: T, update: DocumentUpdate<T>, retrieve?: boolean): Promise<TxResult> {
|
update<T extends Doc>(
|
||||||
|
doc: T,
|
||||||
|
update: DocumentUpdate<T>,
|
||||||
|
retrieve?: boolean,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
|
): Promise<TxResult> {
|
||||||
const hierarchy = this.client.getHierarchy()
|
const hierarchy = this.client.getHierarchy()
|
||||||
if (hierarchy.isMixin(doc._class)) {
|
if (hierarchy.isMixin(doc._class)) {
|
||||||
const baseClass = hierarchy.getBaseClass(doc._class)
|
const baseClass = hierarchy.getBaseClass(doc._class)
|
||||||
return this.updateMixin(doc._id, baseClass, doc.space, doc._class, update)
|
return this.updateMixin(doc._id, baseClass, doc.space, doc._class, update, modifiedOn, modifiedBy)
|
||||||
}
|
}
|
||||||
if (hierarchy.isDerived(doc._class, core.class.AttachedDoc)) {
|
if (hierarchy.isDerived(doc._class, core.class.AttachedDoc)) {
|
||||||
const adoc = doc as unknown as AttachedDoc
|
const adoc = doc as unknown as AttachedDoc
|
||||||
@ -176,13 +218,15 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
adoc.attachedToClass,
|
adoc.attachedToClass,
|
||||||
adoc.collection,
|
adoc.collection,
|
||||||
update,
|
update,
|
||||||
retrieve
|
retrieve,
|
||||||
|
modifiedOn,
|
||||||
|
modifiedBy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return this.updateDoc(doc._class, doc.space, doc._id, update, retrieve)
|
return this.updateDoc(doc._class, doc.space, doc._id, update, retrieve, modifiedOn, modifiedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
remove<T extends Doc>(doc: T): Promise<TxResult> {
|
remove<T extends Doc>(doc: T, modifiedOn?: Timestamp, modifiedBy?: Ref<Account>): Promise<TxResult> {
|
||||||
if (this.client.getHierarchy().isDerived(doc._class, core.class.AttachedDoc)) {
|
if (this.client.getHierarchy().isDerived(doc._class, core.class.AttachedDoc)) {
|
||||||
const adoc = doc as unknown as AttachedDoc
|
const adoc = doc as unknown as AttachedDoc
|
||||||
return this.removeCollection(
|
return this.removeCollection(
|
||||||
@ -191,7 +235,9 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
adoc._id,
|
adoc._id,
|
||||||
adoc.attachedTo,
|
adoc.attachedTo,
|
||||||
adoc.attachedToClass,
|
adoc.attachedToClass,
|
||||||
adoc.collection
|
adoc.collection,
|
||||||
|
modifiedOn,
|
||||||
|
modifiedBy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return this.removeDoc(doc._class, doc.space, doc._id)
|
return this.removeDoc(doc._class, doc.space, doc._id)
|
||||||
|
@ -14,7 +14,20 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { KeysByType } from 'simplytyped'
|
import type { KeysByType } from 'simplytyped'
|
||||||
import type { Account, Arr, AttachedDoc, Class, Data, Doc, Domain, Mixin, PropertyType, Ref, Space } from './classes'
|
import type {
|
||||||
|
Account,
|
||||||
|
Arr,
|
||||||
|
AttachedDoc,
|
||||||
|
Class,
|
||||||
|
Data,
|
||||||
|
Doc,
|
||||||
|
Domain,
|
||||||
|
Mixin,
|
||||||
|
PropertyType,
|
||||||
|
Ref,
|
||||||
|
Space,
|
||||||
|
Timestamp
|
||||||
|
} from './classes'
|
||||||
import core from './component'
|
import core from './component'
|
||||||
import { setObjectValue } from './objvalue'
|
import { setObjectValue } from './objvalue'
|
||||||
import { _getOperator } from './operator'
|
import { _getOperator } from './operator'
|
||||||
@ -398,7 +411,9 @@ export class TxFactory {
|
|||||||
_class: Ref<Class<T>>,
|
_class: Ref<Class<T>>,
|
||||||
space: Ref<Space>,
|
space: Ref<Space>,
|
||||||
attributes: Data<T>,
|
attributes: Data<T>,
|
||||||
objectId?: Ref<T>
|
objectId?: Ref<T>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): TxCreateDoc<T> {
|
): TxCreateDoc<T> {
|
||||||
return {
|
return {
|
||||||
_id: generateId(),
|
_id: generateId(),
|
||||||
@ -407,8 +422,8 @@ export class TxFactory {
|
|||||||
objectId: objectId ?? generateId(),
|
objectId: objectId ?? generateId(),
|
||||||
objectClass: _class,
|
objectClass: _class,
|
||||||
objectSpace: space,
|
objectSpace: space,
|
||||||
modifiedOn: Date.now(),
|
modifiedOn: modifiedOn ?? Date.now(),
|
||||||
modifiedBy: this.account,
|
modifiedBy: modifiedBy ?? this.account,
|
||||||
attributes
|
attributes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,7 +433,9 @@ export class TxFactory {
|
|||||||
objectId: Ref<T>,
|
objectId: Ref<T>,
|
||||||
space: Ref<Space>,
|
space: Ref<Space>,
|
||||||
collection: string,
|
collection: string,
|
||||||
tx: TxCUD<P>
|
tx: TxCUD<P>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): TxCollectionCUD<T, P> {
|
): TxCollectionCUD<T, P> {
|
||||||
return {
|
return {
|
||||||
_id: generateId(),
|
_id: generateId(),
|
||||||
@ -427,8 +444,8 @@ export class TxFactory {
|
|||||||
objectId,
|
objectId,
|
||||||
objectClass: _class,
|
objectClass: _class,
|
||||||
objectSpace: space,
|
objectSpace: space,
|
||||||
modifiedOn: Date.now(),
|
modifiedOn: modifiedOn ?? Date.now(),
|
||||||
modifiedBy: this.account,
|
modifiedBy: modifiedBy ?? this.account,
|
||||||
collection,
|
collection,
|
||||||
tx
|
tx
|
||||||
}
|
}
|
||||||
@ -439,14 +456,16 @@ export class TxFactory {
|
|||||||
space: Ref<Space>,
|
space: Ref<Space>,
|
||||||
objectId: Ref<T>,
|
objectId: Ref<T>,
|
||||||
operations: DocumentUpdate<T>,
|
operations: DocumentUpdate<T>,
|
||||||
retrieve?: boolean
|
retrieve?: boolean,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): TxUpdateDoc<T> {
|
): TxUpdateDoc<T> {
|
||||||
return {
|
return {
|
||||||
_id: generateId(),
|
_id: generateId(),
|
||||||
_class: core.class.TxUpdateDoc,
|
_class: core.class.TxUpdateDoc,
|
||||||
space: core.space.Tx,
|
space: core.space.Tx,
|
||||||
modifiedBy: this.account,
|
modifiedBy: modifiedBy ?? this.account,
|
||||||
modifiedOn: Date.now(),
|
modifiedOn: modifiedOn ?? Date.now(),
|
||||||
objectId,
|
objectId,
|
||||||
objectClass: _class,
|
objectClass: _class,
|
||||||
objectSpace: space,
|
objectSpace: space,
|
||||||
@ -455,13 +474,19 @@ export class TxFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createTxRemoveDoc<T extends Doc>(_class: Ref<Class<T>>, space: Ref<Space>, objectId: Ref<T>): TxRemoveDoc<T> {
|
createTxRemoveDoc<T extends Doc>(
|
||||||
|
_class: Ref<Class<T>>,
|
||||||
|
space: Ref<Space>,
|
||||||
|
objectId: Ref<T>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
|
): TxRemoveDoc<T> {
|
||||||
return {
|
return {
|
||||||
_id: generateId(),
|
_id: generateId(),
|
||||||
_class: core.class.TxRemoveDoc,
|
_class: core.class.TxRemoveDoc,
|
||||||
space: core.space.Tx,
|
space: core.space.Tx,
|
||||||
modifiedBy: this.account,
|
modifiedBy: modifiedBy ?? this.account,
|
||||||
modifiedOn: Date.now(),
|
modifiedOn: modifiedOn ?? Date.now(),
|
||||||
objectId,
|
objectId,
|
||||||
objectClass: _class,
|
objectClass: _class,
|
||||||
objectSpace: space
|
objectSpace: space
|
||||||
@ -473,14 +498,16 @@ export class TxFactory {
|
|||||||
objectClass: Ref<Class<D>>,
|
objectClass: Ref<Class<D>>,
|
||||||
objectSpace: Ref<Space>,
|
objectSpace: Ref<Space>,
|
||||||
mixin: Ref<Mixin<M>>,
|
mixin: Ref<Mixin<M>>,
|
||||||
attributes: MixinUpdate<D, M>
|
attributes: MixinUpdate<D, M>,
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
): TxMixin<D, M> {
|
): TxMixin<D, M> {
|
||||||
return {
|
return {
|
||||||
_id: generateId(),
|
_id: generateId(),
|
||||||
_class: core.class.TxMixin,
|
_class: core.class.TxMixin,
|
||||||
space: core.space.Tx,
|
space: core.space.Tx,
|
||||||
modifiedBy: this.account,
|
modifiedBy: modifiedBy ?? this.account,
|
||||||
modifiedOn: Date.now(),
|
modifiedOn: modifiedOn ?? Date.now(),
|
||||||
objectId,
|
objectId,
|
||||||
objectClass,
|
objectClass,
|
||||||
objectSpace,
|
objectSpace,
|
||||||
@ -489,13 +516,20 @@ export class TxFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createTxApplyIf (space: Ref<Space>, scope: string, match: DocumentClassQuery<Doc>[], txes: TxCUD<Doc>[]): TxApplyIf {
|
createTxApplyIf (
|
||||||
|
space: Ref<Space>,
|
||||||
|
scope: string,
|
||||||
|
match: DocumentClassQuery<Doc>[],
|
||||||
|
txes: TxCUD<Doc>[],
|
||||||
|
modifiedOn?: Timestamp,
|
||||||
|
modifiedBy?: Ref<Account>
|
||||||
|
): TxApplyIf {
|
||||||
return {
|
return {
|
||||||
_id: generateId(),
|
_id: generateId(),
|
||||||
_class: core.class.TxApplyIf,
|
_class: core.class.TxApplyIf,
|
||||||
space: core.space.Tx,
|
space: core.space.Tx,
|
||||||
modifiedBy: this.account,
|
modifiedBy: modifiedBy ?? this.account,
|
||||||
modifiedOn: Date.now(),
|
modifiedOn: modifiedOn ?? Date.now(),
|
||||||
objectSpace: space,
|
objectSpace: space,
|
||||||
scope,
|
scope,
|
||||||
match,
|
match,
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
export let createMore: boolean | undefined = undefined
|
export let createMore: boolean | undefined = undefined
|
||||||
export let okLabel: IntlString = presentation.string.Create
|
export let okLabel: IntlString = presentation.string.Create
|
||||||
export let onCancel: Function | undefined = undefined
|
export let onCancel: Function | undefined = undefined
|
||||||
|
export let fullSize = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@ -36,6 +37,7 @@
|
|||||||
<form
|
<form
|
||||||
id={label}
|
id={label}
|
||||||
class="antiCard {$deviceInfo.isMobile ? 'mobile' : 'dialog'}"
|
class="antiCard {$deviceInfo.isMobile ? 'mobile' : 'dialog'}"
|
||||||
|
class:full={fullSize}
|
||||||
on:submit|preventDefault={() => {}}
|
on:submit|preventDefault={() => {}}
|
||||||
use:resizeObserver={() => {
|
use:resizeObserver={() => {
|
||||||
dispatch('changeContent')
|
dispatch('changeContent')
|
||||||
|
@ -64,12 +64,14 @@
|
|||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
|
|
||||||
|
$: _idExtra = typeof docQuery?._id === 'object' ? docQuery?._id : {}
|
||||||
$: query.query<Doc>(
|
$: query.query<Doc>(
|
||||||
_class,
|
_class,
|
||||||
{
|
{
|
||||||
...(docQuery ?? {}),
|
...(docQuery ?? {}),
|
||||||
[searchField]: { $like: '%' + search + '%' },
|
[searchField]: { $like: '%' + search + '%' },
|
||||||
_id: { $nin: ignoreObjects }
|
_id: { $nin: ignoreObjects, ..._idExtra }
|
||||||
},
|
},
|
||||||
(result) => {
|
(result) => {
|
||||||
result.sort((a, b) => {
|
result.sort((a, b) => {
|
||||||
|
@ -214,13 +214,19 @@
|
|||||||
margin: 2.5rem;
|
margin: 2.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.vScroll {
|
||||||
|
overflow-y: auto;
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
margin: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ac-column {
|
.ac-column {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 1.75rem 2.5rem;
|
padding: 0.75rem 0.5rem;
|
||||||
min-width: 25rem;
|
min-width: 15rem;
|
||||||
max-width: 25rem;
|
max-width: 25rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-right: 1px solid var(--theme-menu-divider);
|
border-right: 1px solid var(--theme-menu-divider);
|
||||||
|
@ -139,6 +139,10 @@
|
|||||||
height: auto;
|
height: auto;
|
||||||
max-width: 60rem;
|
max-width: 60rem;
|
||||||
max-height: inherit;
|
max-height: inherit;
|
||||||
|
&.full {
|
||||||
|
width: max-content;
|
||||||
|
// max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.mobile {
|
&.mobile {
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
export let focusIndex = -1
|
export let focusIndex = -1
|
||||||
export let autoSelect: boolean = true
|
export let autoSelect: boolean = true
|
||||||
export let useFlexGrow = false
|
export let useFlexGrow = false
|
||||||
|
export let minW0 = true
|
||||||
|
|
||||||
let container: HTMLElement
|
let container: HTMLElement
|
||||||
let opened: boolean = false
|
let opened: boolean = false
|
||||||
@ -53,7 +54,7 @@
|
|||||||
const mgr = getFocusManager()
|
const mgr = getFocusManager()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={container} class="min-w-0" class:flex-grow={useFlexGrow}>
|
<div bind:this={container} class:min-w-0={minW0} class:flex-grow={useFlexGrow}>
|
||||||
<Button
|
<Button
|
||||||
{focusIndex}
|
{focusIndex}
|
||||||
{icon}
|
{icon}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
export let width: string | undefined = undefined
|
export let width: string | undefined = undefined
|
||||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||||
export let shouldUpdateUndefined: boolean = true
|
export let shouldUpdateUndefined: boolean = true
|
||||||
|
export let minW0 = true
|
||||||
|
|
||||||
let container: HTMLElement
|
let container: HTMLElement
|
||||||
let opened: boolean = false
|
let opened: boolean = false
|
||||||
@ -72,7 +73,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={container} class="min-w-0">
|
<div bind:this={container} class:min-w-0={minW0}>
|
||||||
<Button
|
<Button
|
||||||
{icon}
|
{icon}
|
||||||
width={width ?? 'min-content'}
|
width={width ?? 'min-content'}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
export let focus: boolean = false
|
export let focus: boolean = false
|
||||||
export let focusable: boolean = false
|
export let focusable: boolean = false
|
||||||
export let disabled: boolean = false
|
export let disabled: boolean = false
|
||||||
|
export let fullSize = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@ -74,7 +75,7 @@
|
|||||||
} else if (kind === 'underline') {
|
} else if (kind === 'underline') {
|
||||||
target.style.width = `calc(${text.clientWidth}px + 1.125rem)`
|
target.style.width = `calc(${text.clientWidth}px + 1.125rem)`
|
||||||
} else {
|
} else {
|
||||||
target.style.width = text.clientWidth + 'px'
|
target.style.width = Math.max(text.clientWidth, 50) + 'px'
|
||||||
}
|
}
|
||||||
dispatch('input')
|
dispatch('input')
|
||||||
}
|
}
|
||||||
@ -118,7 +119,8 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="editbox-container"
|
class="editbox-container"
|
||||||
class:w-full={focusable}
|
class:flex-grow={fullSize}
|
||||||
|
class:w-full={focusable || fullSize}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
input.focus()
|
input.focus()
|
||||||
}}
|
}}
|
||||||
|
66
packages/ui/src/components/Expandable.svelte
Normal file
66
packages/ui/src/components/Expandable.svelte
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<!--
|
||||||
|
// 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 type { Asset, IntlString } from '@hcengineering/platform'
|
||||||
|
import ExpandCollapse from './ExpandCollapse.svelte'
|
||||||
|
import Icon from './Icon.svelte'
|
||||||
|
import Label from './Label.svelte'
|
||||||
|
|
||||||
|
export let icon: Asset | undefined = undefined
|
||||||
|
export let label: IntlString | undefined = undefined
|
||||||
|
export let expanded: boolean = false
|
||||||
|
export let expandable = true
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<div class="flex-grow flex" class:expanded class:expandable>
|
||||||
|
<div
|
||||||
|
class="fs-title flex-row-center mr-4"
|
||||||
|
on:click|stopPropagation={() => {
|
||||||
|
expanded = !expanded
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="chevron" class:expanded>▶</div>
|
||||||
|
<div class="an-element__icon">
|
||||||
|
{#if icon}
|
||||||
|
<Icon {icon} size={'small'} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<span class="an-element__label title">
|
||||||
|
{#if label}<Label {label} />{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<slot name="tools" />
|
||||||
|
</div>
|
||||||
|
<ExpandCollapse isExpanded={expanded}>
|
||||||
|
<div class="antiComponent p-2">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</ExpandCollapse>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.expandable {
|
||||||
|
.chevron {
|
||||||
|
content: '▶';
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--dark-color);
|
||||||
|
&.expanded {
|
||||||
|
transform: rotateZ(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -21,6 +21,7 @@
|
|||||||
<slot name="popup-header" />
|
<slot name="popup-header" />
|
||||||
{/if}
|
{/if}
|
||||||
{#each $modal as popup, i}
|
{#each $modal as popup, i}
|
||||||
|
{#key popup.id}
|
||||||
<PopupInstance
|
<PopupInstance
|
||||||
is={popup.is}
|
is={popup.is}
|
||||||
props={popup.props}
|
props={popup.props}
|
||||||
@ -32,4 +33,5 @@
|
|||||||
close={popup.close}
|
close={popup.close}
|
||||||
overlay={popup.options.overlay}
|
overlay={popup.options.overlay}
|
||||||
/>
|
/>
|
||||||
|
{/key}
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -150,6 +150,7 @@ export { default as FocusHandler } from './components/FocusHandler.svelte'
|
|||||||
export { default as ListView } from './components/ListView.svelte'
|
export { default as ListView } from './components/ListView.svelte'
|
||||||
export { default as ToggleButton } from './components/ToggleButton.svelte'
|
export { default as ToggleButton } from './components/ToggleButton.svelte'
|
||||||
export { default as ExpandCollapse } from './components/ExpandCollapse.svelte'
|
export { default as ExpandCollapse } from './components/ExpandCollapse.svelte'
|
||||||
|
export { default as Expandable } from './components/Expandable.svelte'
|
||||||
export { default as BarDashboard } from './components/BarDashboard.svelte'
|
export { default as BarDashboard } from './components/BarDashboard.svelte'
|
||||||
export { default as Notifications } from './components/notifications/Notifications.svelte'
|
export { default as Notifications } from './components/notifications/Notifications.svelte'
|
||||||
export { default as notificationsStore } from './components/notifications/store'
|
export { default as notificationsStore } from './components/notifications/store'
|
||||||
|
@ -229,6 +229,15 @@ async function getAllRealValues (client: TxOperations, values: any[], _class: Re
|
|||||||
if (values.some((value) => typeof value !== 'string')) {
|
if (values.some((value) => typeof value !== 'string')) {
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
_class === core.class.TypeString ||
|
||||||
|
_class === core.class.EnumOf ||
|
||||||
|
_class === core.class.TypeNumber ||
|
||||||
|
_class === core.class.TypeDate
|
||||||
|
) {
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
const realValues = await client.findAll(_class, { _id: { $in: values } })
|
const realValues = await client.findAll(_class, { _id: { $in: values } })
|
||||||
const realValuesIds = realValues.map(({ _id }) => _id)
|
const realValuesIds = realValues.map(({ _id }) => _id)
|
||||||
return [
|
return [
|
||||||
|
7
plugins/bitrix-assets/.eslintrc.js
Normal file
7
plugins/bitrix-assets/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['./node_modules/@hcengineering/platform-rig/profiles/assets/config/eslint.config.json'],
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: './tsconfig.json'
|
||||||
|
}
|
||||||
|
}
|
16
plugins/bitrix-assets/assets/icons.svg
Normal file
16
plugins/bitrix-assets/assets/icons.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!-- ALL HASHTAGs -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||||
|
<symbol id="bitrix" viewBox="0 0 200.9 201.2">
|
||||||
|
<style>
|
||||||
|
.st0 {
|
||||||
|
fill: #c60c30;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<g id="layer1" transform="translate(-76.221 -152.77)">
|
||||||
|
<path
|
||||||
|
id="path821"
|
||||||
|
class="st0"
|
||||||
|
d="M157.3 352.8c-17.6-3.6-34-11.8-46.8-23.2-33-29.3-43.3-75.4-25.8-116.5C89 203 96 192.8 104.6 183.9l7.6-7.8.5 45.8c.5 43.9.6 46.1 2.7 51.6 4.2 11.1 8.6 18.1 16.3 25.7 19.8 19.7 48.8 24.6 73.5 12.5 23.2-11.4 37.8-36.3 36.1-61.5-1.1-16.9-6.8-29.3-19.1-41.6-9.3-9.3-20.2-15.4-32-17.9-12.1-2.5-11.5-2.9-11.5 7.3v8.8l6.6 1.2c25.6 4.7 42.6 29.9 37.3 55.3-1.7 8.1-5.5 15.3-11.6 21.9-9.3 10-22.4 14.8-33.8 14.8-18.9 0-34.6-10.8-42.3-26.6 0 0-2-4.5-2.6-7.3-3.8-16.5-1.4-51.1-1.4-51.1l-.3-50.7 3-1.5c1.6-.9 3.2-1.5 3.5-1.5s.6 23.3.8 51.8c0 0-3.6 37.3.9 51.6.6 2.1 1.9 5.6 1.9 5.6 3.9 8 10.2 15.1 18.4 19 6.2 2.9 9.4 4.3 18.6 4.3 6 0 11.3-1.9 17.5-4.7 7.2-3.3 15.5-12.1 18.6-20.2 8.4-22.2-3.3-46-26-52.7-9.3-2.7-9.1-2.9-9.1 7.2v8.8l4.4 1.2c16 4.3 20.9 25.8 8.3 36.8-4.4 3.9-8.4 5.6-15.4 5.6-7.5 0-14.7-4.5-18.4-11.5-2.2-4.2-2.4-8.1-2.4-9.9 0-16.7-.4-46.9-.4-46.9l-.3-51.7 2.3-.6c1.3-.3 6.4-1.1 11.4-1.7 16.9-2.2 41.9 3.4 58.7 13 22.9 13.1 40.2 35.6 47.4 61.6 2.5 8.9 2.8 11.6 2.8 25.9 0 14.3-.3 17-2.7 25.8-9.8 35.4-34.8 60.6-70.5 71.4-7.5 2.3-11 2.7-24.3 3-10.7.1-17.6-.2-22.3-1.2z"
|
||||||
|
/></g>
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
5
plugins/bitrix-assets/config/rig.json
Normal file
5
plugins/bitrix-assets/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||||
|
"rigPackageName": "@hcengineering/platform-rig",
|
||||||
|
"rigProfile": "assets"
|
||||||
|
}
|
15
plugins/bitrix-assets/lang/en.json
Normal file
15
plugins/bitrix-assets/lang/en.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"string": {
|
||||||
|
"BitrixTokenUrl": "Bitrix24 Token Url",
|
||||||
|
"Bitrix": "",
|
||||||
|
"BitrixDesc": "Bitrix 24 integration",
|
||||||
|
"Settings": "Configure",
|
||||||
|
"NotAllowed": "Not allowed without administrative rights",
|
||||||
|
"BitrixEntityType": "Entity mappping",
|
||||||
|
"FieldMapping": "Field mapping",
|
||||||
|
"AddField": "Add mapping",
|
||||||
|
"Attribute": "Attribute",
|
||||||
|
"MapField": "Map...",
|
||||||
|
"BitrixImport": "Synchronize with Bitrix"
|
||||||
|
}
|
||||||
|
}
|
15
plugins/bitrix-assets/lang/ru.json
Normal file
15
plugins/bitrix-assets/lang/ru.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"string": {
|
||||||
|
"BitrixTokenUrl": "Bitrix24 Token Url",
|
||||||
|
"Bitrix": "",
|
||||||
|
"BitrixDesc": "Bitrix 24 integration",
|
||||||
|
"Settings": "Configure",
|
||||||
|
"NotAllowed": "Not allowed without administrative rights",
|
||||||
|
"BitrixEntityType": "Entity mappping",
|
||||||
|
"FieldMapping": "Field mapping",
|
||||||
|
"AddField": "Add mapping",
|
||||||
|
"Attribute": "Attribute",
|
||||||
|
"MapField": "Map...",
|
||||||
|
"BitrixImport": "Synchronize with Bitrix"
|
||||||
|
}
|
||||||
|
}
|
35
plugins/bitrix-assets/package.json
Normal file
35
plugins/bitrix-assets/package.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "@hcengineering/bitrix-assets",
|
||||||
|
"version": "0.6.0",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"author": "Anticrm Platform Contributors",
|
||||||
|
"template": "@hcengineering/assets-package",
|
||||||
|
"license": "EPL-2.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "heft build",
|
||||||
|
"test": "heft test",
|
||||||
|
"build:docs": "",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"lint:fix": "eslint --fix src",
|
||||||
|
"format": "prettier --write src && eslint --fix src",
|
||||||
|
"build:watch": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@hcengineering/platform-rig": "~0.6.0",
|
||||||
|
"@types/heft-jest": "^1.0.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||||
|
"@typescript-eslint/parser": "^5.41.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^23.0.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"eslint-plugin-n": "^15.4.0",
|
||||||
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
|
"eslint": "^8.26.0",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"@rushstack/heft": "^0.47.9",
|
||||||
|
"@types/node": "~16.11.12"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hcengineering/platform": "^0.6.8",
|
||||||
|
"@hcengineering/bitrix": "^0.6.0"
|
||||||
|
}
|
||||||
|
}
|
6
plugins/bitrix-assets/src/__tests__/lang.test.ts
Normal file
6
plugins/bitrix-assets/src/__tests__/lang.test.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { makeLocalesTest } from '@hcengineering/platform'
|
||||||
|
|
||||||
|
it(
|
||||||
|
'Locales are equale',
|
||||||
|
makeLocalesTest((lang) => import(`../../lang/${lang}.json`))
|
||||||
|
)
|
24
plugins/bitrix-assets/src/index.ts
Normal file
24
plugins/bitrix-assets/src/index.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// 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 '@hcengineering/platform'
|
||||||
|
import bitrix, { bitrixId } from '@hcengineering/bitrix'
|
||||||
|
|
||||||
|
const icons = require('../assets/icons.svg') as string // eslint-disable-line
|
||||||
|
loadMetadata(bitrix.icon, {
|
||||||
|
Bitrix: `${icons}#bitrix`
|
||||||
|
})
|
||||||
|
|
||||||
|
addStringsLoader(bitrixId, async (lang: string) => await import(`../lang/${lang}.json`))
|
18
plugins/bitrix-assets/tsconfig.json
Normal file
18
plugins/bitrix-assets/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "commonjs",
|
||||||
|
"sourceMap": true,
|
||||||
|
"types": ["node", "heft-jest"],
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "./lib",
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
7
plugins/bitrix-resources/.eslintrc.js
Normal file
7
plugins/bitrix-resources/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/config/eslint.config.json'],
|
||||||
|
parserOptions: { tsconfigRootDir: __dirname },
|
||||||
|
settings: {
|
||||||
|
'svelte3/ignore-styles': () => true
|
||||||
|
}
|
||||||
|
}
|
5
plugins/bitrix-resources/config/rig.json
Normal file
5
plugins/bitrix-resources/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||||
|
"rigPackageName": "@hcengineering/platform-rig",
|
||||||
|
"rigProfile": "ui"
|
||||||
|
}
|
56
plugins/bitrix-resources/package.json
Normal file
56
plugins/bitrix-resources/package.json
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"name": "@hcengineering/bitrix-resources",
|
||||||
|
"version": "0.6.0",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"author": "Anticrm Platform Contributors",
|
||||||
|
"license": "EPL-2.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --incremental --noEmit --outDir ./dist_cache && echo build",
|
||||||
|
"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": {
|
||||||
|
"@hcengineering/platform-rig": "~0.6.0",
|
||||||
|
"svelte-loader": "^3.1.3",
|
||||||
|
"sass": "^1.53.0",
|
||||||
|
"svelte-preprocess": "^4.10.7",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
|
"eslint-plugin-n": "^15.4.0",
|
||||||
|
"eslint": "^8.26.0",
|
||||||
|
"@typescript-eslint/parser": "^5.41.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^23.0.0",
|
||||||
|
"eslint-plugin-svelte3": "^4.0.0",
|
||||||
|
"prettier-plugin-svelte": "^2.8.0",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"svelte-check": "^2.8.0",
|
||||||
|
"typescript": "^4.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hcengineering/platform": "^0.6.8",
|
||||||
|
"svelte": "^3.47",
|
||||||
|
"@hcengineering/bitrix": "^0.6.0",
|
||||||
|
"@hcengineering/ui": "^0.6.2",
|
||||||
|
"@hcengineering/presentation": "~0.6.2",
|
||||||
|
"@hcengineering/text-editor": "~0.6.0",
|
||||||
|
"@hcengineering/contact": "~0.6.8",
|
||||||
|
"@hcengineering/lead": "~0.6.0",
|
||||||
|
"@hcengineering/setting": "~0.6.1",
|
||||||
|
"@hcengineering/core": "^0.6.19",
|
||||||
|
"@hcengineering/attachment": "~0.6.1",
|
||||||
|
"@hcengineering/attachment-resources": "~0.6.0",
|
||||||
|
"@hcengineering/view": "^0.6.1",
|
||||||
|
"@hcengineering/view-resources": "~0.6.0",
|
||||||
|
"@hcengineering/setting-resources": "~0.6.0",
|
||||||
|
"@hcengineering/chunter": "~0.6.1",
|
||||||
|
"p-queue": "~7.3.0",
|
||||||
|
"qs": "~6.11.0",
|
||||||
|
"@types/qs": "~6.9.7",
|
||||||
|
"@hcengineering/tags": "~0.6.2",
|
||||||
|
"@hcengineering/tags-resources": "~0.6.0",
|
||||||
|
"fast-equals": "^2.0.3"
|
||||||
|
}
|
||||||
|
}
|
5
plugins/bitrix-resources/postcss.config.js
Normal file
5
plugins/bitrix-resources/postcss.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('autoprefixer')
|
||||||
|
]
|
||||||
|
}
|
28
plugins/bitrix-resources/src/client.ts
Normal file
28
plugins/bitrix-resources/src/client.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import PQueue from 'p-queue'
|
||||||
|
import { stringify as toQuery } from 'qs'
|
||||||
|
import { BitrixResult } from './types'
|
||||||
|
|
||||||
|
const queue = new PQueue({
|
||||||
|
intervalCap: 1,
|
||||||
|
interval: 1000
|
||||||
|
})
|
||||||
|
export class BitrixClient {
|
||||||
|
constructor (readonly url: string) {}
|
||||||
|
|
||||||
|
async call (method: string, params: any): Promise<BitrixResult> {
|
||||||
|
return await queue.add(async () => {
|
||||||
|
let query: string = toQuery(params)
|
||||||
|
if (query.length > 0) {
|
||||||
|
query = `?${query}`
|
||||||
|
}
|
||||||
|
return await (
|
||||||
|
await fetch(`${this.url}/${method}${query}`, {
|
||||||
|
method: 'get',
|
||||||
|
headers: {
|
||||||
|
'user-agent': 'anticrm'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
).json()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
102
plugins/bitrix-resources/src/components/AttributeMapper.svelte
Normal file
102
plugins/bitrix-resources/src/components/AttributeMapper.svelte
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, BitrixFieldMapping, MappingOperation, Fields } from '@hcengineering/bitrix'
|
||||||
|
import core, { AnyAttribute } from '@hcengineering/core'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
import { getClient } from '@hcengineering/presentation'
|
||||||
|
import { Button, eventToHTMLElement, IconDelete, Menu, showPopup, Action, IconEdit } from '@hcengineering/ui'
|
||||||
|
import bitrix from '../plugin'
|
||||||
|
import CreateMappingAttribute from './CreateMappingAttribute.svelte'
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let fieldMapping: BitrixFieldMapping[]
|
||||||
|
export let fields: Fields
|
||||||
|
export let attribute: AnyAttribute
|
||||||
|
|
||||||
|
$: mappedField = fieldMapping.find((it) => it.attributeName === attribute.name)
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
const allowed = new Set([
|
||||||
|
core.class.TypeString,
|
||||||
|
core.class.TypeMarkup,
|
||||||
|
core.class.TypeBoolean,
|
||||||
|
core.class.TypeDate,
|
||||||
|
core.class.TypeNumber,
|
||||||
|
core.class.EnumOf,
|
||||||
|
core.class.Collection
|
||||||
|
])
|
||||||
|
|
||||||
|
function addMapping (evt: MouseEvent, kind: MappingOperation): void {
|
||||||
|
showPopup(
|
||||||
|
CreateMappingAttribute,
|
||||||
|
{
|
||||||
|
mapping,
|
||||||
|
attribute,
|
||||||
|
fields,
|
||||||
|
kind
|
||||||
|
},
|
||||||
|
'middle'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: actions = [
|
||||||
|
{
|
||||||
|
label: getEmbeddedLabel('Add Copy mapping'),
|
||||||
|
action: (_: any, evt: MouseEvent) => {
|
||||||
|
addMapping(evt, MappingOperation.CopyValue)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: getEmbeddedLabel('Add Tag mapping'),
|
||||||
|
action: (_: any, evt: MouseEvent) => {
|
||||||
|
addMapping(evt, MappingOperation.CreateTag)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: getEmbeddedLabel('Add Channel mapping'),
|
||||||
|
action: (_: any, evt: MouseEvent) => {
|
||||||
|
addMapping(evt, MappingOperation.CreateChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] as Action[]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-row-center">
|
||||||
|
{#if mappedField !== undefined}
|
||||||
|
<Button
|
||||||
|
icon={IconEdit}
|
||||||
|
on:click={(evt) => {
|
||||||
|
showPopup(
|
||||||
|
CreateMappingAttribute,
|
||||||
|
{
|
||||||
|
mapping,
|
||||||
|
attribute,
|
||||||
|
fields,
|
||||||
|
field: mappedField
|
||||||
|
},
|
||||||
|
'middle'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={IconDelete}
|
||||||
|
on:click={() => {
|
||||||
|
if (mappedField) {
|
||||||
|
client.remove(mappedField)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else if allowed.has(attribute.type._class)}
|
||||||
|
<Button
|
||||||
|
label={bitrix.string.MapField}
|
||||||
|
on:click={(evt) => {
|
||||||
|
showPopup(
|
||||||
|
Menu,
|
||||||
|
{
|
||||||
|
actions
|
||||||
|
},
|
||||||
|
eventToHTMLElement(evt)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
@ -0,0 +1,96 @@
|
|||||||
|
<!--
|
||||||
|
//
|
||||||
|
// 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 presentation, { Card, createQuery } from '@hcengineering/presentation'
|
||||||
|
import { Integration } from '@hcengineering/setting'
|
||||||
|
import { createEventDispatcher, onMount } from 'svelte'
|
||||||
|
import bitrix from '../plugin'
|
||||||
|
|
||||||
|
import { BitrixEntityMapping } from '@hcengineering/bitrix'
|
||||||
|
import { Button, eventToHTMLElement, IconAdd, Label, showPopup } from '@hcengineering/ui'
|
||||||
|
import { BitrixClient } from '../client'
|
||||||
|
import { BitrixProfile, StatusValue } from '../types'
|
||||||
|
import CreateMapping from './CreateMapping.svelte'
|
||||||
|
import EntiryMapping from './EntityMapping.svelte'
|
||||||
|
|
||||||
|
export let integration: Integration
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
const bitrixClient = new BitrixClient(integration.value)
|
||||||
|
|
||||||
|
let profile: BitrixProfile | undefined
|
||||||
|
|
||||||
|
let statusList: StatusValue[] = []
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
bitrixClient.call('profile', {}).then((res: any) => {
|
||||||
|
profile = res.result
|
||||||
|
})
|
||||||
|
|
||||||
|
bitrixClient.call('crm.status.list', {}).then((res) => {
|
||||||
|
statusList = res.result
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const mQuery = createQuery()
|
||||||
|
|
||||||
|
let mappings: BitrixEntityMapping[] = []
|
||||||
|
$: mQuery.query(bitrix.class.EntityMapping, {}, (res) => {
|
||||||
|
mappings = res
|
||||||
|
})
|
||||||
|
|
||||||
|
function addMapping (evt: MouseEvent): void {
|
||||||
|
showPopup(CreateMapping, { integration, mappings, bitrixClient }, eventToHTMLElement(evt))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
label={bitrix.string.BitrixDesc}
|
||||||
|
okAction={() => {
|
||||||
|
dispatch('close')
|
||||||
|
}}
|
||||||
|
canSave={true}
|
||||||
|
okLabel={presentation.string.Ok}
|
||||||
|
on:close={() => dispatch('close')}
|
||||||
|
fullSize={true}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="header">
|
||||||
|
{#if profile}
|
||||||
|
<div class="lines-limit-2">
|
||||||
|
{profile.LAST_NAME}
|
||||||
|
{profile.NAME}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
{#if profile}
|
||||||
|
{#if !profile.ADMIN}
|
||||||
|
<Label label={bitrix.string.NotAllowed} />
|
||||||
|
{:else}
|
||||||
|
<div class="flex flex-reverse flex-grab">
|
||||||
|
<Button icon={IconAdd} label={presentation.string.Add} on:click={addMapping} />
|
||||||
|
</div>
|
||||||
|
<div class="flex-row">
|
||||||
|
{#each mappings as mapping}
|
||||||
|
<EntiryMapping {mapping} {bitrixClient} {statusList} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
<!-- <EditBox label={bitrix.string.BitrixTokenUrl} bind:value={url} /> -->
|
||||||
|
<svelte:fragment slot="pool" />
|
||||||
|
</Card>
|
44
plugins/bitrix-resources/src/components/BitrixConnect.svelte
Normal file
44
plugins/bitrix-resources/src/components/BitrixConnect.svelte
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!--
|
||||||
|
//
|
||||||
|
// 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 presentation, { Card } from '@hcengineering/presentation'
|
||||||
|
import { Integration } from '@hcengineering/setting'
|
||||||
|
import { EditBox } from '@hcengineering/ui'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import bitrix from '../plugin'
|
||||||
|
|
||||||
|
export let integration: Integration
|
||||||
|
|
||||||
|
let url: string = integration.value
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
function save (): void {
|
||||||
|
dispatch('close', { value: url })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
label={bitrix.string.BitrixDesc}
|
||||||
|
okAction={save}
|
||||||
|
canSave={true}
|
||||||
|
okLabel={presentation.string.Ok}
|
||||||
|
on:close={() => dispatch('close')}
|
||||||
|
>
|
||||||
|
<EditBox label={bitrix.string.BitrixTokenUrl} bind:value={url} />
|
||||||
|
<svelte:fragment slot="pool" />
|
||||||
|
</Card>
|
142
plugins/bitrix-resources/src/components/BitrixFieldLookup.svelte
Normal file
142
plugins/bitrix-resources/src/components/BitrixFieldLookup.svelte
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, Fields, FieldValue } from '@hcengineering/bitrix'
|
||||||
|
import core, { Enum, Ref } from '@hcengineering/core'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
|
import setting from '@hcengineering/setting-resources/src/plugin'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DatePresenter,
|
||||||
|
EditBox,
|
||||||
|
eventToHTMLElement,
|
||||||
|
getEventPositionElement,
|
||||||
|
IconAdd,
|
||||||
|
IconEdit,
|
||||||
|
Menu,
|
||||||
|
showPopup
|
||||||
|
} from '@hcengineering/ui'
|
||||||
|
import Grid from '@hcengineering/ui/src/components/Grid.svelte'
|
||||||
|
import { BitrixClient } from '../client'
|
||||||
|
|
||||||
|
import EnumPopup from './EnumPopup.svelte'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let bitrixClient: BitrixClient
|
||||||
|
export let fields: Fields = {}
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
let items: any[] = []
|
||||||
|
function loadItems (order: boolean): void {
|
||||||
|
bitrixClient
|
||||||
|
.call(mapping.type + '.list', { select: ['*', 'UF_*'], order: { ID: order ? 'ASC' : 'DSC' } })
|
||||||
|
.then((res) => {
|
||||||
|
items = res.result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const enumQuery = createQuery()
|
||||||
|
|
||||||
|
let enums: Enum[] = []
|
||||||
|
enumQuery.query(core.class.Enum, {}, (res) => {
|
||||||
|
enums = res
|
||||||
|
})
|
||||||
|
|
||||||
|
async function updateEnum (evt: MouseEvent, fieldId: string, field: FieldValue): Promise<void> {
|
||||||
|
const enumId = ('bitrix_' + fieldId) as Ref<Enum>
|
||||||
|
const existingEnum = await client.findOne(core.class.Enum, { _id: enumId })
|
||||||
|
if (existingEnum !== undefined) {
|
||||||
|
existingEnum.enumValues = [...existingEnum.enumValues, ...(field.items?.map((it) => it.VALUE) ?? [])]
|
||||||
|
existingEnum.enumValues = existingEnum.enumValues.filter((it, idx) => existingEnum.enumValues.indexOf(it) === idx)
|
||||||
|
showPopup(setting.component.EditEnum, { value: existingEnum }, 'top')
|
||||||
|
} else {
|
||||||
|
showPopup(
|
||||||
|
Menu,
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: getEmbeddedLabel('New'),
|
||||||
|
action: (_: any, evt: MouseEvent) => {
|
||||||
|
showPopup(
|
||||||
|
setting.component.EditEnum,
|
||||||
|
{
|
||||||
|
name: 'Bitrix:' + field.formLabel ?? field.title,
|
||||||
|
values: field.items?.map((it) => it.VALUE) ?? [],
|
||||||
|
value: existingEnum
|
||||||
|
},
|
||||||
|
getEventPositionElement(evt)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Modify existing',
|
||||||
|
component: EnumPopup,
|
||||||
|
props: {
|
||||||
|
_class: core.class.Enum,
|
||||||
|
action: async (existingEnum: Enum) => {
|
||||||
|
if (existingEnum !== undefined) {
|
||||||
|
existingEnum.enumValues = [
|
||||||
|
...existingEnum.enumValues,
|
||||||
|
...(field.items?.map((it) => it.VALUE) ?? [])
|
||||||
|
]
|
||||||
|
existingEnum.enumValues = existingEnum.enumValues.filter(
|
||||||
|
(it, idx) => existingEnum.enumValues.indexOf(it) === idx
|
||||||
|
)
|
||||||
|
showPopup(setting.component.EditEnum, { value: existingEnum }, 'top')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
eventToHTMLElement(evt)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let pattern = ''
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="top-divider">
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<Button label={getEmbeddedLabel('Load First N values')} on:click={() => loadItems(true)} />
|
||||||
|
<Button label={getEmbeddedLabel('Load Last N values')} on:click={() => loadItems(false)} />
|
||||||
|
</div>
|
||||||
|
<EditBox kind={'search-style'} bind:value={pattern} />
|
||||||
|
</div>
|
||||||
|
<div class="p-2 top-divider" style:overflow={'auto'} style:max-height={'40rem'}>
|
||||||
|
<Grid column={2} rowGap={1}>
|
||||||
|
{#each Object.entries(fields) as kv}
|
||||||
|
{@const field = kv[1]}
|
||||||
|
{@const title = field.formLabel ?? field.title}
|
||||||
|
{#if title.includes(pattern) || field.type.includes(pattern) || pattern.length === 0}
|
||||||
|
<span class="fs-title select-text flex-row-center">
|
||||||
|
{title} : {field.type}
|
||||||
|
<span class="select-text ml-2 mr-2">
|
||||||
|
"{kv[0]}"
|
||||||
|
</span>
|
||||||
|
{#if field.type === 'enumeration' || field.type === 'crm_status'}
|
||||||
|
<Button
|
||||||
|
icon={enums.find((it) => it._id === 'bitrix_' + kv[0]) ? IconEdit : IconAdd}
|
||||||
|
on:click={(evt) => updateEnum(evt, kv[0], field)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
<div class="whitespace-nowrap select-text flex-row-center gap-2">
|
||||||
|
{#each items.map((it) => it[kv[0]]).filter((it) => it != null && it !== '') as value}
|
||||||
|
<div class="ml-2">
|
||||||
|
{#if value !== null && value !== undefined && value !== ''}
|
||||||
|
{#if (field.type === 'datetime' || field.type === 'date') && value != null && value !== ''}
|
||||||
|
<DatePresenter value={new Date(value).getTime()} withTime={field.type === 'datetime'} />
|
||||||
|
{:else if field.type === 'enumeration'}
|
||||||
|
{field.items?.find((it) => it.ID === value)?.VALUE}
|
||||||
|
{:else}
|
||||||
|
{JSON.stringify(value)}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
61
plugins/bitrix-resources/src/components/BitrixImport.svelte
Normal file
61
plugins/bitrix-resources/src/components/BitrixImport.svelte
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping } from '@hcengineering/bitrix'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
import { Card, createQuery } from '@hcengineering/presentation'
|
||||||
|
import setting, { Integration } from '@hcengineering/setting'
|
||||||
|
import { Label } from '@hcengineering/ui'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import { BitrixClient } from '../client'
|
||||||
|
import bitrix from '../plugin'
|
||||||
|
import FieldMappingSynchronizer from './FieldMappingSynchronizer.svelte'
|
||||||
|
|
||||||
|
const mappingQuery = createQuery()
|
||||||
|
|
||||||
|
let mappings: BitrixEntityMapping[] = []
|
||||||
|
|
||||||
|
mappingQuery.query(
|
||||||
|
bitrix.class.EntityMapping,
|
||||||
|
{},
|
||||||
|
(res) => {
|
||||||
|
mappings = res
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
_id: {
|
||||||
|
fields: bitrix.class.FieldMapping
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
const q = createQuery()
|
||||||
|
|
||||||
|
let integration: Integration | undefined
|
||||||
|
|
||||||
|
q.query(setting.class.Integration, { type: bitrix.integrationType.Bitrix }, (res) => {
|
||||||
|
integration = res.shift()
|
||||||
|
})
|
||||||
|
|
||||||
|
$: bitrixClient = integration !== undefined ? new BitrixClient(integration.value) : undefined
|
||||||
|
let loading = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
label={getEmbeddedLabel('Bitrix Synchronization...')}
|
||||||
|
canSave={!loading}
|
||||||
|
fullSize={false}
|
||||||
|
okAction={() => {
|
||||||
|
dispatch('close')
|
||||||
|
}}
|
||||||
|
okLabel={getEmbeddedLabel('Close')}
|
||||||
|
on:close
|
||||||
|
>
|
||||||
|
{#if integration && bitrixClient}
|
||||||
|
{#each mappings as mapping}
|
||||||
|
<FieldMappingSynchronizer {mapping} {bitrixClient} bind:loading />
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
<Label label={getEmbeddedLabel('No integration configured')} />
|
||||||
|
{/if}
|
||||||
|
</Card>
|
67
plugins/bitrix-resources/src/components/CreateMapping.svelte
Normal file
67
plugins/bitrix-resources/src/components/CreateMapping.svelte
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, BitrixEntityType, mappingTypes } from '@hcengineering/bitrix'
|
||||||
|
import core, { Class, ClassifierKind, Doc, Obj, Ref } from '@hcengineering/core'
|
||||||
|
import { Card, createQuery, getClient } from '@hcengineering/presentation'
|
||||||
|
import setting, { Integration } from '@hcengineering/setting'
|
||||||
|
import DropdownLabels from '@hcengineering/ui/src/components/DropdownLabels.svelte'
|
||||||
|
import { ObjectBox } from '@hcengineering/view-resources'
|
||||||
|
import bitrix from '../plugin'
|
||||||
|
|
||||||
|
export let integration: Integration
|
||||||
|
export let mappings: BitrixEntityMapping[]
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
async function save (): Promise<void> {
|
||||||
|
client.createDoc(bitrix.class.EntityMapping, bitrix.space.Mappings, {
|
||||||
|
ofClass,
|
||||||
|
type
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$: existingTypes = new Set(mappings.map((it) => it.type))
|
||||||
|
$: items = existingTypes !== undefined ? mappingTypes.filter((it) => !existingTypes.has(it.id)) : []
|
||||||
|
|
||||||
|
let type: string = BitrixEntityType.Lead
|
||||||
|
let ofClass: Ref<Class<Doc>>
|
||||||
|
|
||||||
|
$: if (items.find((it) => it.id === type) === undefined) {
|
||||||
|
type = items[0]?.id ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const classQuery = createQuery()
|
||||||
|
|
||||||
|
let _classes: Ref<Class<Doc>>[] = []
|
||||||
|
|
||||||
|
$: classQuery.query(
|
||||||
|
core.class.Class,
|
||||||
|
{
|
||||||
|
label: { $exists: true },
|
||||||
|
kind: ClassifierKind.CLASS,
|
||||||
|
[setting.mixin.Editable + '.value']: true
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
const withDerived: Ref<Class<Obj>>[] = []
|
||||||
|
for (const r of res) {
|
||||||
|
withDerived.push(...client.getHierarchy().getDescendants(r._id))
|
||||||
|
}
|
||||||
|
_classes = withDerived
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card label={bitrix.string.AddMapping} canSave={type !== ''} okAction={save} on:close>
|
||||||
|
<div class="flex">
|
||||||
|
<DropdownLabels label={bitrix.string.BitrixEntityType} {items} bind:selected={type} />
|
||||||
|
<ObjectBox
|
||||||
|
_class={core.class.Class}
|
||||||
|
label={core.string.Class}
|
||||||
|
showNavigate={false}
|
||||||
|
bind:value={ofClass}
|
||||||
|
docQuery={{
|
||||||
|
_id: { $in: _classes },
|
||||||
|
kind: ClassifierKind.CLASS
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
@ -0,0 +1,43 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, BitrixFieldMapping, Fields, MappingOperation } from '@hcengineering/bitrix'
|
||||||
|
import { AnyAttribute } from '@hcengineering/core'
|
||||||
|
import presentation, { Card } from '@hcengineering/presentation'
|
||||||
|
import { Label } from '@hcengineering/ui'
|
||||||
|
import bitrix from '../plugin'
|
||||||
|
import CopyMapping from './mappings/CopyMapping.svelte'
|
||||||
|
import CreateChannelMapping from './mappings/CreateChannelMapping.svelte'
|
||||||
|
import CreateTagMapping from './mappings/CreateTagMapping.svelte'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let fields: Fields = {}
|
||||||
|
export let attribute: AnyAttribute
|
||||||
|
export let kind: MappingOperation | undefined
|
||||||
|
export let field: BitrixFieldMapping | undefined
|
||||||
|
|
||||||
|
$: _kind = kind ?? field?.operation.kind
|
||||||
|
|
||||||
|
let op: CopyMapping | CreateTagMapping | CreateChannelMapping
|
||||||
|
|
||||||
|
async function save (): Promise<void> {
|
||||||
|
op.save()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
label={bitrix.string.AddField}
|
||||||
|
canSave={attribute !== undefined}
|
||||||
|
okAction={save}
|
||||||
|
okLabel={presentation.string.Save}
|
||||||
|
on:close
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="header">
|
||||||
|
<Label label={attribute.label} />
|
||||||
|
</svelte:fragment>
|
||||||
|
{#if _kind === MappingOperation.CopyValue}
|
||||||
|
<CopyMapping {mapping} {fields} {attribute} {field} bind:this={op} />
|
||||||
|
{:else if _kind === MappingOperation.CreateTag}
|
||||||
|
<CreateTagMapping {mapping} {fields} {attribute} {field} bind:this={op} />
|
||||||
|
{:else if _kind === MappingOperation.CreateChannel}
|
||||||
|
<CreateChannelMapping {mapping} {fields} {attribute} {field} bind:this={op} />
|
||||||
|
{/if}
|
||||||
|
</Card>
|
167
plugins/bitrix-resources/src/components/EntityMapping.svelte
Normal file
167
plugins/bitrix-resources/src/components/EntityMapping.svelte
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, BitrixFieldMapping, Fields, mappingTypes } from '@hcengineering/bitrix'
|
||||||
|
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
|
import { ClassSetting } from '@hcengineering/setting-resources'
|
||||||
|
import { Button, Expandable, Icon, IconDelete, IconEdit, Label, showPopup } from '@hcengineering/ui'
|
||||||
|
import { BitrixClient } from '../client'
|
||||||
|
import bitrix from '../plugin'
|
||||||
|
|
||||||
|
import AttributeMapper from './AttributeMapper.svelte'
|
||||||
|
import FieldMappingPresenter from './FieldMappingPresenter.svelte'
|
||||||
|
|
||||||
|
import CheckBox from '@hcengineering/ui/src/components/CheckBox.svelte'
|
||||||
|
import { deepEqual } from 'fast-equals'
|
||||||
|
import { StatusValue } from '../types'
|
||||||
|
import { toClassRef } from '../utils'
|
||||||
|
import BitrixFieldLookup from './BitrixFieldLookup.svelte'
|
||||||
|
import CreateMappingAttribute from './CreateMappingAttribute.svelte'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let bitrixClient: BitrixClient
|
||||||
|
export let statusList: StatusValue[] = []
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
$: ofClass = client.getHierarchy().getClass(mapping.ofClass)
|
||||||
|
|
||||||
|
$: typeTitle = mappingTypes.find((it) => it.id === mapping.type)
|
||||||
|
|
||||||
|
let fields: Fields = {}
|
||||||
|
bitrixClient.call(mapping.type + '.fields', {}).then((res) => {
|
||||||
|
fields = res.result
|
||||||
|
})
|
||||||
|
|
||||||
|
async function updateMappingFields (
|
||||||
|
mapping: BitrixEntityMapping,
|
||||||
|
fields: Fields,
|
||||||
|
statusList: StatusValue[]
|
||||||
|
): Promise<void> {
|
||||||
|
if (deepEqual(fields, {})) {
|
||||||
|
return // no value resieved yet.
|
||||||
|
}
|
||||||
|
// Update fields with status valies if missing.
|
||||||
|
for (const f of Object.values(fields)) {
|
||||||
|
if (f.type === 'crm_status') {
|
||||||
|
f.items = statusList
|
||||||
|
.filter((it) => it.ENTITY_ID === f.statusType)
|
||||||
|
.map((it) => ({ ID: `${it.STATUS_ID}`, VALUE: it.NAME }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store if changed.
|
||||||
|
// TODO: We need to inform about mapping changes and remove old ones.
|
||||||
|
if (!deepEqual(mapping.bitrixFields, fields)) {
|
||||||
|
await client.update(mapping, { bitrixFields: fields })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: updateMappingFields(mapping, fields, statusList)
|
||||||
|
|
||||||
|
const attrs = createQuery()
|
||||||
|
let fieldMapping: BitrixFieldMapping[] = []
|
||||||
|
|
||||||
|
$: attrs.query(bitrix.class.FieldMapping, { attachedTo: mapping._id }, (res) => {
|
||||||
|
fieldMapping = res
|
||||||
|
})
|
||||||
|
|
||||||
|
$: fieldsByClass = fieldMapping.reduce((p, c) => {
|
||||||
|
p[c.ofClass] = [...(p[c.ofClass] ?? []), c]
|
||||||
|
return p
|
||||||
|
}, {} as Record<Ref<Class<Doc>>, BitrixFieldMapping[]>)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<div class="antiComponent max-w-240 flex-grow p-1">
|
||||||
|
<Expandable icon={ofClass?.icon} label={getEmbeddedLabel(typeTitle?.label ?? '')}>
|
||||||
|
<svelte:fragment slot="tools">
|
||||||
|
<div class="flex flex-reverse flex-grow">
|
||||||
|
<Button icon={IconDelete} on:click={() => client.remove(mapping)} size={'small'} />
|
||||||
|
</div>
|
||||||
|
</svelte:fragment>
|
||||||
|
<Expandable label={getEmbeddedLabel('Options')}>
|
||||||
|
<div class="flex-col">
|
||||||
|
<div class="flex-row-center">
|
||||||
|
<CheckBox
|
||||||
|
bind:checked={mapping.comments}
|
||||||
|
on:value={(evt) => client.update(mapping, { comments: evt.detail })}
|
||||||
|
/>
|
||||||
|
<div class="ml-2">
|
||||||
|
<Label label={getEmbeddedLabel('Comments')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row-center">
|
||||||
|
<CheckBox
|
||||||
|
bind:checked={mapping.attachments}
|
||||||
|
on:value={(evt) => client.update(mapping, { attachments: evt.detail })}
|
||||||
|
/>
|
||||||
|
<div class="ml-2">
|
||||||
|
<Label label={getEmbeddedLabel('Attachments')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row-center">
|
||||||
|
<CheckBox
|
||||||
|
bind:checked={mapping.activity}
|
||||||
|
readonly
|
||||||
|
on:value={(evt) => client.update(mapping, { activity: evt.detail })}
|
||||||
|
/>
|
||||||
|
<div class="ml-2">
|
||||||
|
<Label label={getEmbeddedLabel('Activity')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Expandable>
|
||||||
|
<Expandable label={getEmbeddedLabel('Mappings')} expanded>
|
||||||
|
<div class="flex-row flex-grow bottom-divider p-2">
|
||||||
|
{#each Object.entries(fieldsByClass) as field, i}
|
||||||
|
{@const cl = client.getHierarchy().getClass(toClassRef(field[0]))}
|
||||||
|
<div class="fs-title flex-row-center">
|
||||||
|
{#if cl.icon}
|
||||||
|
<div class="mr-1">
|
||||||
|
<Icon icon={cl.icon} size={'large'} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<Label label={cl.label} />
|
||||||
|
</div>
|
||||||
|
<div class="flex-row">
|
||||||
|
{#each field[1] as cfield, i}
|
||||||
|
<div class="fs-title flex-row-center ml-4">
|
||||||
|
{i + 1}.
|
||||||
|
<FieldMappingPresenter {mapping} value={cfield} />
|
||||||
|
<Button
|
||||||
|
icon={IconEdit}
|
||||||
|
on:click={(evt) => {
|
||||||
|
showPopup(
|
||||||
|
CreateMappingAttribute,
|
||||||
|
{
|
||||||
|
mapping,
|
||||||
|
attribute: client.getHierarchy().getAttribute(cfield.ofClass, cfield.attributeName),
|
||||||
|
fields,
|
||||||
|
field: cfield
|
||||||
|
},
|
||||||
|
'middle'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Expandable>
|
||||||
|
<Expandable label={getEmbeddedLabel('Class settings')}>
|
||||||
|
<ClassSetting
|
||||||
|
ofClass={mapping.ofClass}
|
||||||
|
withoutHeader
|
||||||
|
attributeMapper={{
|
||||||
|
component: AttributeMapper,
|
||||||
|
props: { mapping, fields, fieldMapping },
|
||||||
|
label: bitrix.string.FieldMapping
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Expandable>
|
||||||
|
<Expandable label={getEmbeddedLabel('Bitrix field lookup')}>
|
||||||
|
<BitrixFieldLookup {mapping} {bitrixClient} {fields} />
|
||||||
|
</Expandable>
|
||||||
|
</Expandable>
|
||||||
|
</div>
|
46
plugins/bitrix-resources/src/components/EnumPopup.svelte
Normal file
46
plugins/bitrix-resources/src/components/EnumPopup.svelte
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<!--
|
||||||
|
// 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 type { Class, DocumentQuery, Enum, Ref } from '@hcengineering/core'
|
||||||
|
import { ObjectPopup } from '@hcengineering/presentation'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
|
export let _class: Ref<Class<Enum>>
|
||||||
|
export let selected: Ref<Enum> | undefined
|
||||||
|
export let query: DocumentQuery<Enum> | undefined
|
||||||
|
export let action: (evt: Enum) => void | Promise<void>
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ObjectPopup
|
||||||
|
{_class}
|
||||||
|
{selected}
|
||||||
|
bind:docQuery={query}
|
||||||
|
multiSelect={false}
|
||||||
|
allowDeselect={false}
|
||||||
|
shadows={true}
|
||||||
|
on:update
|
||||||
|
on:close={(evt) => {
|
||||||
|
if (evt.detail != null) {
|
||||||
|
action(evt.detail)
|
||||||
|
}
|
||||||
|
dispatch('close')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="item" let:item>
|
||||||
|
{item.name}
|
||||||
|
</svelte:fragment>
|
||||||
|
</ObjectPopup>
|
@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, BitrixFieldMapping, MappingOperation } from '@hcengineering/bitrix'
|
||||||
|
import { AnyAttribute } from '@hcengineering/core'
|
||||||
|
import { getClient } from '@hcengineering/presentation'
|
||||||
|
import { Icon, IconArrowLeft, Label } from '@hcengineering/ui'
|
||||||
|
import CopyMappingPresenter from './mappings/CopyMappingPresenter.svelte'
|
||||||
|
import CreateChannelMappingPresenter from './mappings/CreateChannelMappingPresenter.svelte'
|
||||||
|
import CreateTagMappingPresenter from './mappings/CreateTagMappingPresenter.svelte'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let value: BitrixFieldMapping
|
||||||
|
$: kind = value.operation.kind
|
||||||
|
|
||||||
|
const attr: AnyAttribute | undefined = getClient().getHierarchy().getAttribute(value.ofClass, value.attributeName)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-row-center top-divider">
|
||||||
|
{#if attr}
|
||||||
|
<div class="ml-4 fs-title mr-2 flex-row-center">
|
||||||
|
<Label label={attr.label} />
|
||||||
|
<Icon icon={IconArrowLeft} size={'small'} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if mapping && mapping.bitrixFields}
|
||||||
|
{#if kind === MappingOperation.CopyValue}
|
||||||
|
<CopyMappingPresenter {mapping} {value} />
|
||||||
|
{:else if kind === MappingOperation.CreateTag}
|
||||||
|
<CreateTagMappingPresenter {mapping} {value} />
|
||||||
|
{:else if kind === MappingOperation.CreateChannel}
|
||||||
|
<CreateChannelMappingPresenter {mapping} {value} />
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div>
|
@ -0,0 +1,389 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import bitrix, {
|
||||||
|
BitrixEntityMapping,
|
||||||
|
BitrixEntityType,
|
||||||
|
BitrixFieldMapping,
|
||||||
|
BitrixSyncDoc
|
||||||
|
} from '@hcengineering/bitrix'
|
||||||
|
import chunter, { Comment } from '@hcengineering/chunter'
|
||||||
|
import contact, { combineName, EmployeeAccount } from '@hcengineering/contact'
|
||||||
|
import core, {
|
||||||
|
AccountRole,
|
||||||
|
ApplyOperations,
|
||||||
|
AttachedDoc,
|
||||||
|
Class,
|
||||||
|
Data,
|
||||||
|
Doc,
|
||||||
|
DocumentUpdate,
|
||||||
|
generateId,
|
||||||
|
Mixin,
|
||||||
|
Ref,
|
||||||
|
Space,
|
||||||
|
WithLookup
|
||||||
|
} from '@hcengineering/core'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
import { getClient, SpaceSelect } from '@hcengineering/presentation'
|
||||||
|
import { TagElement } from '@hcengineering/tags'
|
||||||
|
import { Button, Expandable, Icon, Label } from '@hcengineering/ui'
|
||||||
|
import DropdownLabels from '@hcengineering/ui/src/components/DropdownLabels.svelte'
|
||||||
|
import { NumberEditor } from '@hcengineering/view-resources'
|
||||||
|
import { deepEqual } from 'fast-equals'
|
||||||
|
import { BitrixClient } from '../client'
|
||||||
|
import { convert, ConvertResult, toClassRef } from '../utils'
|
||||||
|
import FieldMappingPresenter from './FieldMappingPresenter.svelte'
|
||||||
|
|
||||||
|
export let mapping: WithLookup<BitrixEntityMapping>
|
||||||
|
export let bitrixClient: BitrixClient
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
$: fieldMapping = (mapping.$lookup?.fields as BitrixFieldMapping[]) ?? []
|
||||||
|
$: fieldsByClass = fieldMapping.reduce((p, c) => {
|
||||||
|
p[c.ofClass] = [...(p[c.ofClass] ?? []), c]
|
||||||
|
return p
|
||||||
|
}, {} as Record<Ref<Class<Doc>>, BitrixFieldMapping[]>)
|
||||||
|
|
||||||
|
let direction: 'ASC' | 'DSC' = 'DSC'
|
||||||
|
let limit = 200
|
||||||
|
let space: Ref<Space> | undefined
|
||||||
|
|
||||||
|
export let loading = false
|
||||||
|
let state = ''
|
||||||
|
|
||||||
|
async function updateDoc (client: ApplyOperations, doc: Doc, raw: Doc | Data<Doc>): Promise<void> {
|
||||||
|
// We need to update fields if they are different.
|
||||||
|
const documentUpdate: DocumentUpdate<Doc> = {}
|
||||||
|
for (const [k, v] of Object.entries(raw)) {
|
||||||
|
if (['_class', '_id', 'modifiedBy', 'modifiedOn', 'space'].includes(k)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!deepEqual((doc as any)[k], v)) {
|
||||||
|
;(documentUpdate as any)[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(documentUpdate).length > 0) {
|
||||||
|
await client.update(doc, documentUpdate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let docsProcessed = 0
|
||||||
|
let total = 0
|
||||||
|
|
||||||
|
async function syncPlatform (documents: ConvertResult[]): Promise<void> {
|
||||||
|
const existingDocuments = await client.findAll(mapping.ofClass, {
|
||||||
|
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: { $in: documents.map((it) => it.document.bitrixId) }
|
||||||
|
})
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
for (const d of documents) {
|
||||||
|
const existing = existingDocuments.find(
|
||||||
|
(it) => hierarchy.as(it, bitrix.mixin.BitrixSyncDoc).bitrixId === d.document.bitrixId
|
||||||
|
)
|
||||||
|
const applyOp = client.apply('bitrix')
|
||||||
|
if (existing !== undefined) {
|
||||||
|
// We need to update fields if they are different.
|
||||||
|
await updateDoc(applyOp, existing, d.document)
|
||||||
|
|
||||||
|
// Check and update mixins
|
||||||
|
for (const [m, mv] of Object.entries(d.mixins)) {
|
||||||
|
const mRef = m as Ref<Mixin<Doc>>
|
||||||
|
if (hierarchy.hasMixin(existing, mRef)) {
|
||||||
|
await applyOp.createMixin(
|
||||||
|
d.document._id,
|
||||||
|
d.document._class,
|
||||||
|
d.document.space,
|
||||||
|
m as Ref<Mixin<Doc>>,
|
||||||
|
mv,
|
||||||
|
d.document.modifiedOn,
|
||||||
|
d.document.modifiedBy
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const existingM = hierarchy.as(existing, mRef)
|
||||||
|
await updateDoc(applyOp, existingM, mv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await applyOp.createDoc(
|
||||||
|
d.document._class,
|
||||||
|
d.document.space,
|
||||||
|
d.document,
|
||||||
|
d.document._id,
|
||||||
|
d.document.modifiedOn,
|
||||||
|
d.document.modifiedBy
|
||||||
|
)
|
||||||
|
|
||||||
|
await applyOp.createMixin<Doc, BitrixSyncDoc>(
|
||||||
|
d.document._id,
|
||||||
|
d.document._class,
|
||||||
|
d.document.space,
|
||||||
|
bitrix.mixin.BitrixSyncDoc,
|
||||||
|
{
|
||||||
|
type: d.document.type,
|
||||||
|
bitrixId: d.document.bitrixId
|
||||||
|
},
|
||||||
|
d.document.modifiedOn,
|
||||||
|
d.document.modifiedBy
|
||||||
|
)
|
||||||
|
for (const [m, mv] of Object.entries(d.mixins)) {
|
||||||
|
await applyOp.createMixin(
|
||||||
|
d.document._id,
|
||||||
|
d.document._class,
|
||||||
|
d.document.space,
|
||||||
|
m as Ref<Mixin<Doc>>,
|
||||||
|
mv,
|
||||||
|
d.document.modifiedOn,
|
||||||
|
d.document.modifiedBy
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (const ed of d.extraDocs) {
|
||||||
|
if (applyOp.getHierarchy().isDerived(ed._class, core.class.AttachedDoc)) {
|
||||||
|
const adoc = ed as AttachedDoc
|
||||||
|
await applyOp.addCollection(
|
||||||
|
adoc._class,
|
||||||
|
adoc.space,
|
||||||
|
adoc.attachedTo,
|
||||||
|
adoc.attachedToClass,
|
||||||
|
adoc.collection,
|
||||||
|
adoc,
|
||||||
|
adoc._id,
|
||||||
|
d.document.modifiedOn,
|
||||||
|
d.document.modifiedBy
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await applyOp.createDoc(ed._class, ed.space, ed, ed._id, d.document.modifiedOn, d.document.modifiedBy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.comments !== undefined) {
|
||||||
|
const comments = await d.comments
|
||||||
|
if (comments !== undefined && comments.length > 0) {
|
||||||
|
const existingComments = await client.findAll(chunter.class.Comment, {
|
||||||
|
attachedTo: d.document._id,
|
||||||
|
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: { $in: comments.map((it) => it.bitrixId) }
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const comment of comments) {
|
||||||
|
const existing = existingComments.find(
|
||||||
|
(it) => hierarchy.as<Doc, BitrixSyncDoc>(it, bitrix.mixin.BitrixSyncDoc).bitrixId === comment.bitrixId
|
||||||
|
)
|
||||||
|
if (existing !== undefined) {
|
||||||
|
// We need to update fields if they are different.
|
||||||
|
await updateDoc(applyOp, existing, comment)
|
||||||
|
} else {
|
||||||
|
await applyOp.addCollection(
|
||||||
|
comment._class,
|
||||||
|
comment.space,
|
||||||
|
comment.attachedTo,
|
||||||
|
comment.attachedToClass,
|
||||||
|
comment.collection,
|
||||||
|
comment,
|
||||||
|
comment._id,
|
||||||
|
comment.modifiedOn,
|
||||||
|
comment.modifiedBy
|
||||||
|
)
|
||||||
|
|
||||||
|
await applyOp.createMixin<Doc, BitrixSyncDoc>(
|
||||||
|
comment._id,
|
||||||
|
comment._class,
|
||||||
|
comment.space,
|
||||||
|
bitrix.mixin.BitrixSyncDoc,
|
||||||
|
{
|
||||||
|
type: d.document.type,
|
||||||
|
bitrixId: d.document.bitrixId
|
||||||
|
},
|
||||||
|
comment.modifiedOn,
|
||||||
|
comment.modifiedBy
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await applyOp.commit()
|
||||||
|
docsProcessed++
|
||||||
|
state = `processed: ${docsProcessed}/${total}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doSync (): Promise<void> {
|
||||||
|
loading = true
|
||||||
|
|
||||||
|
const commentFields = await bitrixClient.call(BitrixEntityType.Comment + '.fields', {})
|
||||||
|
|
||||||
|
const commentFieldKeys = Object.keys(commentFields.result)
|
||||||
|
|
||||||
|
const allEmployee = await client.findAll(contact.class.EmployeeAccount, {})
|
||||||
|
|
||||||
|
const userList = new Map<string, Ref<EmployeeAccount>>()
|
||||||
|
|
||||||
|
// Fill all users and create new ones, if required.
|
||||||
|
let totalUsers = 1
|
||||||
|
let next = 0
|
||||||
|
while (userList.size < totalUsers) {
|
||||||
|
const users = await bitrixClient.call('user.search', { start: next })
|
||||||
|
next = users.next
|
||||||
|
totalUsers = users.total
|
||||||
|
for (const u of users.result) {
|
||||||
|
let accountId = allEmployee.find((it) => it.email === u.EMAIL)?._id
|
||||||
|
if (accountId === undefined) {
|
||||||
|
const employeeId = await client.createDoc(contact.class.Employee, contact.space.Contacts, {
|
||||||
|
name: combineName(u.NAME, u.LAST_NAME),
|
||||||
|
avatar: u.PERSONAL_PHOTO,
|
||||||
|
active: u.ACTIVE,
|
||||||
|
city: u.PERSONAL_CITY
|
||||||
|
})
|
||||||
|
accountId = await client.createDoc(contact.class.EmployeeAccount, core.space.Model, {
|
||||||
|
email: u.EMAIL,
|
||||||
|
name: combineName(u.NAME, u.LAST_NAME),
|
||||||
|
employee: employeeId,
|
||||||
|
role: AccountRole.User
|
||||||
|
})
|
||||||
|
}
|
||||||
|
userList.set(u.ID, accountId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (space === undefined || mapping.$lookup?.fields === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let processed = 0
|
||||||
|
const tagElements: Map<Ref<Class<Doc>>, TagElement[]> = new Map()
|
||||||
|
|
||||||
|
let added = 0
|
||||||
|
|
||||||
|
while (added <= limit) {
|
||||||
|
const result = await bitrixClient.call(mapping.type + '.list', {
|
||||||
|
select: ['*', 'UF_*'],
|
||||||
|
order: { ID: direction },
|
||||||
|
start: processed
|
||||||
|
})
|
||||||
|
|
||||||
|
const extraDocs: Doc[] = []
|
||||||
|
|
||||||
|
const convertResults: ConvertResult[] = []
|
||||||
|
for (const r of result.result) {
|
||||||
|
// Convert documents.
|
||||||
|
const res = await convert(
|
||||||
|
client,
|
||||||
|
mapping,
|
||||||
|
space,
|
||||||
|
mapping.$lookup?.fields as BitrixFieldMapping[],
|
||||||
|
r,
|
||||||
|
extraDocs,
|
||||||
|
tagElements,
|
||||||
|
userList
|
||||||
|
)
|
||||||
|
if (mapping.comments) {
|
||||||
|
res.comments = bitrixClient
|
||||||
|
.call(BitrixEntityType.Comment + '.list', {
|
||||||
|
filter: {
|
||||||
|
ENTITY_ID: res.document.bitrixId,
|
||||||
|
ENTITY_TYPE: mapping.type.replace('crm.', '')
|
||||||
|
},
|
||||||
|
select: commentFieldKeys,
|
||||||
|
order: { ID: direction }
|
||||||
|
})
|
||||||
|
.then((comments) => {
|
||||||
|
return comments.result.map(
|
||||||
|
(it: any) =>
|
||||||
|
({
|
||||||
|
_id: generateId(),
|
||||||
|
_class: chunter.class.Comment,
|
||||||
|
message: it.COMMENT,
|
||||||
|
bitrixId: it.ID,
|
||||||
|
type: it.ENTITY_TYPE,
|
||||||
|
attachedTo: res.document._id,
|
||||||
|
attachedToClass: res.document._class,
|
||||||
|
collection: 'comments',
|
||||||
|
space: res.document.space,
|
||||||
|
modifiedBy: userList.get(it.AUTHOR_ID) ?? core.account.System,
|
||||||
|
modifiedOn: new Date(userList.get(it.CREATED) ?? new Date().toString()).getTime()
|
||||||
|
} as Comment)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
convertResults.push(res)
|
||||||
|
extraDocs.push(...res.extraDocs)
|
||||||
|
added++
|
||||||
|
if (added > limit) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total = result.total
|
||||||
|
await syncPlatform(convertResults)
|
||||||
|
|
||||||
|
processed = result.next
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
state = err.message
|
||||||
|
console.error(err)
|
||||||
|
} finally {
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Expandable label={getEmbeddedLabel(mapping.type)}>
|
||||||
|
<svelte:fragment slot="tools">
|
||||||
|
<div class="flex-row-center">
|
||||||
|
<SpaceSelect
|
||||||
|
_class={core.class.Space}
|
||||||
|
label={core.string.Space}
|
||||||
|
bind:value={space}
|
||||||
|
on:change={(evt) => {
|
||||||
|
space = evt.detail
|
||||||
|
}}
|
||||||
|
autoSelect
|
||||||
|
spaceQuery={{ _id: { $in: [contact.space.Contacts] } }}
|
||||||
|
/>
|
||||||
|
<DropdownLabels
|
||||||
|
label={getEmbeddedLabel('Direction')}
|
||||||
|
items={[
|
||||||
|
{ id: 'ASC', label: 'Ascending' },
|
||||||
|
{ id: 'DSC', label: 'Descending' }
|
||||||
|
]}
|
||||||
|
bind:selected={direction}
|
||||||
|
/>
|
||||||
|
<div class="fs-title flex-row-center">
|
||||||
|
<NumberEditor
|
||||||
|
kind={'button'}
|
||||||
|
value={limit}
|
||||||
|
focus={false}
|
||||||
|
placeholder={getEmbeddedLabel('Limit')}
|
||||||
|
onChange={(val) => {
|
||||||
|
if (val) {
|
||||||
|
limit = val
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="ml-2 flex-row-center">
|
||||||
|
<div class="p-1">
|
||||||
|
{state}
|
||||||
|
</div>
|
||||||
|
<Button size={'large'} label={getEmbeddedLabel('Synchronize')} {loading} on:click={doSync} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</svelte:fragment>
|
||||||
|
<div class="flex-row flex-grow bottom-divider p-2">
|
||||||
|
{#each Object.entries(fieldsByClass) as field, i}
|
||||||
|
{@const cl = client.getHierarchy().getClass(toClassRef(field[0]))}
|
||||||
|
<div class="fs-title flex-row-center">
|
||||||
|
{#if cl.icon}
|
||||||
|
<div class="mr-1">
|
||||||
|
<Icon icon={cl.icon} size={'large'} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<Label label={cl.label} />
|
||||||
|
</div>
|
||||||
|
<div class="flex-row">
|
||||||
|
{#each field[1] as cfield, i}
|
||||||
|
<div class="fs-title flex-row-center ml-4">
|
||||||
|
{i + 1}.
|
||||||
|
<FieldMappingPresenter {mapping} value={cfield} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Expandable>
|
38
plugins/bitrix-resources/src/components/icons/Bitrix.svelte
Normal file
38
plugins/bitrix-resources/src/components/icons/Bitrix.svelte
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021 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">
|
||||||
|
export let size: 'small' | 'medium' | 'large'
|
||||||
|
const fill: string = 'currentColor'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-grow">
|
||||||
|
<svg class="svg-{size}" {fill} viewBox="0 0 174 33" width="174" height="33" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none">
|
||||||
|
<path
|
||||||
|
d="M106.8 32.4h18.7v-4.1h-12.4c1.7-6.8 12.1-8.3 12.1-15.9 0-4.1-2.8-7.1-8.6-7.1-3.7 0-6.8 1.1-9 2.2l1.3 3.8c2-.9 4.2-1.8 6.9-1.8 2.2 0 4.2.9 4.2 3.4.1 5.6-12.1 6-13.2 19.5zm56.1-5.7c-5.7 0-10.4-4.7-10.4-10.4s4.7-10.4 10.4-10.4 10.4 4.7 10.4 10.4-4.7 10.4-10.4 10.4zm0-18.9c-4.7 0-8.5 3.8-8.5 8.5s3.8 8.5 8.5 8.5 8.5-3.8 8.5-8.5-3.8-8.5-8.5-8.5z"
|
||||||
|
fill="#00629A"
|
||||||
|
/>
|
||||||
|
<path d="M168.8 16.3h-5.2v-5.2h-1.4v6.6h6.6v-1.4z" fill="#00629A" /><path
|
||||||
|
d="M0 1.6h9c6.6 0 9.6 3.8 9.6 7.8 0 2.7-1.3 5.1-3.7 6.4v.1c3.6.9 5.8 3.8 5.8 7.4 0 4.8-3.6 9.1-10.8 9.1H0V1.6zm8.3 12.9c3.1 0 4.8-1.7 4.8-4.1 0-2.3-1.5-4.1-4.8-4.1H5.7v8.2h2.6zm.9 13.3c3.7 0 5.8-1.4 5.8-4.5 0-2.6-2-4.2-5.1-4.2H5.7v8.7h3.5zM24.9 3.4c0-1.9 1.5-3.4 3.4-3.4s3.5 1.4 3.5 3.4c0 1.8-1.5 3.3-3.5 3.3s-3.4-1.4-3.4-3.3zm.6 6.9h5.6v22.1h-5.6V10.3zM38.6 27V14.7h-4v-4.4h4V5.2l5.6-1.6v6.7h6.7l-1.4 4.4h-5.3v10.9c0 2.1.7 2.8 2.2 2.8 1.3 0 2.5-.5 3.4-1.1l1.7 3.8c-1.6 1.1-4.3 1.7-6.5 1.7-4 .1-6.4-2.1-6.4-5.8zm15.9-16.7h4.7l.6 2.5c2-2 3.8-3 6.1-3 1 0 2.2.3 3.1.9l-2 4.7c-1-.6-1.9-.7-2.5-.7-1.5 0-2.7.6-4.5 2.2v15.6h-5.6V10.3h.1zm16.7-6.9c0-1.9 1.5-3.4 3.4-3.4S78 1.5 78 3.4c0 1.8-1.5 3.3-3.5 3.3s-3.3-1.4-3.3-3.3zm.6 6.9h5.6v22.1h-5.6V10.3zm16.9 11-8-11h5.8l5.2 7.2 5.3-7.2h5.8l-8.1 11 8.2 11.1h-5.8L91.8 25l-5.4 7.4h-5.8l8.1-11.1z"
|
||||||
|
fill="#3FC0F0"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M142.9 21.9V5.3h-3.5l-13.3 17.3v3.3h12v6.6h4.8v-6.6h4v-4h-4zm-4.8-3.9v3.8h-3.2c-1 0-2.8.1-3.4.1l6.8-9.2c0 .8-.2 3.2-.2 5.3z"
|
||||||
|
fill="#00629A"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
</div>
|
@ -0,0 +1,134 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
BitrixEntityMapping,
|
||||||
|
BitrixFieldMapping,
|
||||||
|
CopyPattern,
|
||||||
|
CopyValueOperation,
|
||||||
|
Fields,
|
||||||
|
MappingOperation
|
||||||
|
} from '@hcengineering/bitrix'
|
||||||
|
import core, { AnyAttribute } from '@hcengineering/core'
|
||||||
|
import { getClient } from '@hcengineering/presentation'
|
||||||
|
import { Button, IconActivity, IconAdd, IconClose, IconDelete } from '@hcengineering/ui'
|
||||||
|
import DropdownLabels from '@hcengineering/ui/src/components/DropdownLabels.svelte'
|
||||||
|
import EditBox from '@hcengineering/ui/src/components/EditBox.svelte'
|
||||||
|
import bitrix from '../../plugin'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let fields: Fields = {}
|
||||||
|
export let attribute: AnyAttribute
|
||||||
|
export let field: BitrixFieldMapping | undefined
|
||||||
|
|
||||||
|
let patterns: CopyPattern[] = [
|
||||||
|
...((field?.operation as CopyValueOperation)?.patterns ?? [
|
||||||
|
{
|
||||||
|
text: ''
|
||||||
|
}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
export async function save (): Promise<void> {
|
||||||
|
if (field) {
|
||||||
|
await client.update(field, {
|
||||||
|
operation: {
|
||||||
|
kind: MappingOperation.CopyValue,
|
||||||
|
patterns
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await client.addCollection(bitrix.class.FieldMapping, mapping.space, mapping._id, mapping._class, 'fields', {
|
||||||
|
ofClass: attribute.attributeOf,
|
||||||
|
attributeName: attribute.name,
|
||||||
|
operation: {
|
||||||
|
kind: MappingOperation.CopyValue,
|
||||||
|
patterns
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: items = Object.entries(fields)
|
||||||
|
.filter((it) => {
|
||||||
|
if (attribute.type._class === core.class.EnumOf) {
|
||||||
|
return it[1].type === 'enumeration' || it[1].type === 'crm_status'
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.map((it) => ({
|
||||||
|
id: it[0],
|
||||||
|
label: `${it[1].formLabel ?? it[1].title}${it[0].startsWith('UF_') ? ' *' : ''} - ${it[0]}`
|
||||||
|
}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-row-center flex-wrap">
|
||||||
|
{#each patterns as p, i}
|
||||||
|
<div class="pattern flex-row-center">
|
||||||
|
{#if attribute.type._class !== core.class.EnumOf}
|
||||||
|
<EditBox kind={'editbox'} bind:value={p.text} maxWidth={'5rem'} fullSize />
|
||||||
|
{/if}
|
||||||
|
<DropdownLabels minW0={false} label={bitrix.string.FieldMapping} {items} bind:selected={p.field} />
|
||||||
|
{#if p.alternatives}
|
||||||
|
{#each p.alternatives as alt, i}
|
||||||
|
<DropdownLabels minW0={false} label={bitrix.string.FieldMapping} {items} bind:selected={p.alternatives[i]} />
|
||||||
|
<Button
|
||||||
|
icon={IconClose}
|
||||||
|
size={'small'}
|
||||||
|
on:click={() => {
|
||||||
|
p.alternatives?.splice(i, 1)
|
||||||
|
patterns = patterns
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
<div class="ml-1">
|
||||||
|
<Button
|
||||||
|
icon={IconActivity}
|
||||||
|
size={'small'}
|
||||||
|
on:click={() => {
|
||||||
|
p.alternatives = [...(p.alternatives ?? []), items[0].id]
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={IconDelete}
|
||||||
|
size={'small'}
|
||||||
|
on:click={() => {
|
||||||
|
patterns.splice(i, 1)
|
||||||
|
patterns = patterns
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{#if attribute.type._class !== core.class.EnumOf || patterns.length === 0}
|
||||||
|
<div class="ml-2">
|
||||||
|
<Button
|
||||||
|
icon={IconAdd}
|
||||||
|
size={'small'}
|
||||||
|
on:click={() => {
|
||||||
|
patterns = [...patterns, { text: '', field: undefined }]
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.pattern {
|
||||||
|
margin: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px dashed var(--accent-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
|
||||||
|
// text-transform: uppercase;
|
||||||
|
color: var(--accent-color);
|
||||||
|
&:hover {
|
||||||
|
color: var(--caption-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,48 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, BitrixFieldMapping, CopyValueOperation } from '@hcengineering/bitrix'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let value: BitrixFieldMapping
|
||||||
|
|
||||||
|
$: op = value.operation as CopyValueOperation
|
||||||
|
|
||||||
|
const fieldOf = (field?: string) =>
|
||||||
|
field ? mapping.bitrixFields[field]?.formLabel ?? mapping.bitrixFields[field]?.title : field ?? ''
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-row-center flex-wrap">
|
||||||
|
{#each op.patterns as p, i}
|
||||||
|
<div class="pattern flex-row-center">
|
||||||
|
<span>
|
||||||
|
{p.text}
|
||||||
|
</span>
|
||||||
|
{#if mapping.bitrixFields}
|
||||||
|
=> {fieldOf(p.field)}
|
||||||
|
{/if}
|
||||||
|
{#if p.alternatives}
|
||||||
|
{#each p.alternatives as alt, i}
|
||||||
|
|{fieldOf(alt)}
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.pattern {
|
||||||
|
margin: 0.1rem;
|
||||||
|
padding: 0.3rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px dashed var(--accent-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
|
||||||
|
// text-transform: uppercase;
|
||||||
|
color: var(--accent-color);
|
||||||
|
&:hover {
|
||||||
|
color: var(--caption-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,116 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
BitrixEntityMapping,
|
||||||
|
BitrixFieldMapping,
|
||||||
|
ChannelFieldMapping,
|
||||||
|
Fields,
|
||||||
|
MappingOperation,
|
||||||
|
CreateChannelOperation
|
||||||
|
} from '@hcengineering/bitrix'
|
||||||
|
import { AnyAttribute } from '@hcengineering/core'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
import { getClient } from '@hcengineering/presentation'
|
||||||
|
|
||||||
|
import contact, { ChannelProvider } from '@hcengineering/contact'
|
||||||
|
import { Button, DropdownLabelsIntl, IconAdd, IconDelete } from '@hcengineering/ui'
|
||||||
|
import DropdownLabels from '@hcengineering/ui/src/components/DropdownLabels.svelte'
|
||||||
|
import bitrix from '../../plugin'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let fields: Fields = {}
|
||||||
|
export let attribute: AnyAttribute
|
||||||
|
export let field: BitrixFieldMapping | undefined
|
||||||
|
|
||||||
|
let channelFields: ChannelFieldMapping[] = [...((field?.operation as CreateChannelOperation)?.fields ?? [])]
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
export async function save (): Promise<void> {
|
||||||
|
if (field !== undefined) {
|
||||||
|
await client.update(field, {
|
||||||
|
operation: {
|
||||||
|
kind: MappingOperation.CreateChannel,
|
||||||
|
fields: channelFields
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await client.addCollection(bitrix.class.FieldMapping, mapping.space, mapping._id, mapping._class, 'fields', {
|
||||||
|
ofClass: attribute.attributeOf,
|
||||||
|
attributeName: attribute.name,
|
||||||
|
operation: {
|
||||||
|
kind: MappingOperation.CreateChannel,
|
||||||
|
fields: channelFields
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: items = Object.entries(fields)
|
||||||
|
.filter((it) => {
|
||||||
|
return it[1].type !== 'enumeration'
|
||||||
|
})
|
||||||
|
.map((it) => ({
|
||||||
|
id: it[0],
|
||||||
|
label: `${it[1].formLabel ?? it[1].title}${it[0].startsWith('UF_') ? ' *' : ''} - ${it[0]}`
|
||||||
|
}))
|
||||||
|
|
||||||
|
let providers: ChannelProvider[] = []
|
||||||
|
client.findAll(contact.class.ChannelProvider, {}).then((res) => {
|
||||||
|
providers = res
|
||||||
|
})
|
||||||
|
|
||||||
|
$: providerItems = providers.map((it) => ({ id: it._id, label: it.label }))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-col flex-wrap">
|
||||||
|
{#each channelFields as p, i}
|
||||||
|
<div class="pattern flex-row-center gap-2">
|
||||||
|
<DropdownLabelsIntl
|
||||||
|
minW0={false}
|
||||||
|
label={getEmbeddedLabel('Channel')}
|
||||||
|
items={providerItems}
|
||||||
|
bind:selected={p.provider}
|
||||||
|
/>
|
||||||
|
<DropdownLabels minW0={false} label={bitrix.string.FieldMapping} {items} bind:selected={p.field} />
|
||||||
|
|
||||||
|
<div class="ml-1">
|
||||||
|
<Button
|
||||||
|
icon={IconDelete}
|
||||||
|
size={'small'}
|
||||||
|
on:click={() => {
|
||||||
|
channelFields.splice(i, 1)
|
||||||
|
channelFields = channelFields
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
<div class="ml-2">
|
||||||
|
<Button
|
||||||
|
icon={IconAdd}
|
||||||
|
size={'small'}
|
||||||
|
on:click={() => {
|
||||||
|
channelFields = [...channelFields, { field: items[0].id, provider: providers[0]._id }]
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.pattern {
|
||||||
|
margin: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px dashed var(--accent-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
|
||||||
|
// text-transform: uppercase;
|
||||||
|
color: var(--accent-color);
|
||||||
|
&:hover {
|
||||||
|
color: var(--caption-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,46 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, BitrixFieldMapping, CreateChannelOperation } from '@hcengineering/bitrix'
|
||||||
|
|
||||||
|
import contact from '@hcengineering/contact'
|
||||||
|
import { Component } from '@hcengineering/ui'
|
||||||
|
import view from '@hcengineering/view'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let value: BitrixFieldMapping
|
||||||
|
|
||||||
|
$: op = value.operation as CreateChannelOperation
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
{#each op.fields as p, i}
|
||||||
|
<div class="pattern flex-row-center gap-2">
|
||||||
|
<Component
|
||||||
|
is={view.component.ObjectPresenter}
|
||||||
|
props={{ _class: contact.class.ChannelProvider, objectId: p.provider }}
|
||||||
|
/>
|
||||||
|
->
|
||||||
|
{#if mapping.bitrixFields}
|
||||||
|
{p.field ? mapping.bitrixFields[p.field]?.formLabel ?? mapping.bitrixFields[p.field]?.title : p.field ?? ''}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.pattern {
|
||||||
|
margin: 0.1rem;
|
||||||
|
padding: 0.3rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px dashed var(--accent-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
|
||||||
|
// text-transform: uppercase;
|
||||||
|
color: var(--accent-color);
|
||||||
|
&:hover {
|
||||||
|
color: var(--caption-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,137 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
BitrixEntityMapping,
|
||||||
|
BitrixFieldMapping,
|
||||||
|
CreateTagOperation,
|
||||||
|
Fields,
|
||||||
|
MappingOperation,
|
||||||
|
TagField
|
||||||
|
} from '@hcengineering/bitrix'
|
||||||
|
import { AnyAttribute } from '@hcengineering/core'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
import { getClient } from '@hcengineering/presentation'
|
||||||
|
import tags from '@hcengineering/tags'
|
||||||
|
import { WeightPopup } from '@hcengineering/tags-resources'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DropdownTextItem,
|
||||||
|
getEventPopupPositionElement,
|
||||||
|
IconAdd,
|
||||||
|
IconDelete,
|
||||||
|
showPopup
|
||||||
|
} from '@hcengineering/ui'
|
||||||
|
import DropdownLabels from '@hcengineering/ui/src/components/DropdownLabels.svelte'
|
||||||
|
import EditBox from '@hcengineering/ui/src/components/EditBox.svelte'
|
||||||
|
import bitrix from '../../plugin'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let fields: Fields = {}
|
||||||
|
export let attribute: AnyAttribute
|
||||||
|
export let field: BitrixFieldMapping | undefined
|
||||||
|
|
||||||
|
let tagFields: TagField[] = [...((field?.operation as CreateTagOperation)?.fields ?? [])]
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
export async function save (): Promise<void> {
|
||||||
|
if (field !== undefined) {
|
||||||
|
await client.update(field, {
|
||||||
|
operation: {
|
||||||
|
kind: MappingOperation.CreateTag,
|
||||||
|
fields: tagFields
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await client.addCollection(bitrix.class.FieldMapping, mapping.space, mapping._id, mapping._class, 'fields', {
|
||||||
|
ofClass: attribute.attributeOf,
|
||||||
|
attributeName: attribute.name,
|
||||||
|
operation: {
|
||||||
|
kind: MappingOperation.CreateTag,
|
||||||
|
fields: tagFields
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItems (fields: Fields): DropdownTextItem[] {
|
||||||
|
return Object.entries(fields).map((it) => ({
|
||||||
|
id: it[0],
|
||||||
|
label: `${it[1].formLabel ?? it[1].title}${it[0].startsWith('UF_') ? ' *' : ''} - ${it[0]}`
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
$: items = getItems(fields)
|
||||||
|
|
||||||
|
const tagLevel = [tags.icon.Level1, tags.icon.Level2, tags.icon.Level3]
|
||||||
|
const labels = [getEmbeddedLabel('Initial'), getEmbeddedLabel('Meaningfull'), getEmbeddedLabel('Expert')]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-col flex-wrap">
|
||||||
|
{#each tagFields as p, i}
|
||||||
|
{@const tagIcon = tagLevel[p.weight % 3]}
|
||||||
|
{@const tagLabel = labels[Math.floor(p.weight / 3)]}
|
||||||
|
<div class="pattern flex-row-center gap-2">
|
||||||
|
<DropdownLabels minW0={false} label={bitrix.string.FieldMapping} {items} bind:selected={p.field} />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
label={tagLabel}
|
||||||
|
icon={tagIcon}
|
||||||
|
on:click={(evt) => {
|
||||||
|
showPopup(WeightPopup, { value: p.weight }, getEventPopupPositionElement(evt), (res) => {
|
||||||
|
if (Number.isFinite(res) && res >= 0 && res <= 8) {
|
||||||
|
if (res != null) {
|
||||||
|
p.weight = res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<EditBox
|
||||||
|
kind={'editbox'}
|
||||||
|
bind:value={p.split}
|
||||||
|
maxWidth={'5rem'}
|
||||||
|
placeholder={getEmbeddedLabel('Separator...')}
|
||||||
|
fullSize
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="ml-1">
|
||||||
|
<Button
|
||||||
|
icon={IconDelete}
|
||||||
|
size={'small'}
|
||||||
|
on:click={() => {
|
||||||
|
tagFields.splice(i, 1)
|
||||||
|
tagFields = tagFields
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
<div class="ml-2">
|
||||||
|
<Button
|
||||||
|
icon={IconAdd}
|
||||||
|
size={'small'}
|
||||||
|
on:click={() => {
|
||||||
|
tagFields = [...tagFields, { weight: 0, field: items[0].id, split: '' }]
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.pattern {
|
||||||
|
margin: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px dashed var(--accent-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
|
||||||
|
// text-transform: uppercase;
|
||||||
|
color: var(--accent-color);
|
||||||
|
&:hover {
|
||||||
|
color: var(--caption-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,49 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { BitrixEntityMapping, BitrixFieldMapping, CreateTagOperation } from '@hcengineering/bitrix'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
import tags from '@hcengineering/tags'
|
||||||
|
import { Button } from '@hcengineering/ui'
|
||||||
|
|
||||||
|
export let mapping: BitrixEntityMapping
|
||||||
|
export let value: BitrixFieldMapping
|
||||||
|
|
||||||
|
$: op = value.operation as CreateTagOperation
|
||||||
|
|
||||||
|
const tagLevel = [tags.icon.Level1, tags.icon.Level2, tags.icon.Level3]
|
||||||
|
const labels = [getEmbeddedLabel('Initial'), getEmbeddedLabel('Meaningfull'), getEmbeddedLabel('Expert')]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
{#each op.fields as p, i}
|
||||||
|
{@const tagIcon = tagLevel[p.weight % 3]}
|
||||||
|
{@const tagLabel = labels[Math.floor(p.weight / 3)]}
|
||||||
|
|
||||||
|
<div class="pattern flex-row-center gap-2">
|
||||||
|
{#if mapping.bitrixFields}
|
||||||
|
{p.field ? mapping.bitrixFields[p.field]?.formLabel ?? mapping.bitrixFields[p.field]?.title : p.field ?? ''}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<Button label={tagLabel} icon={tagIcon} size={'small'} disabled={true} />
|
||||||
|
{p.split}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.pattern {
|
||||||
|
margin: 0.1rem;
|
||||||
|
padding: 0.3rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px dashed var(--accent-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
|
||||||
|
// text-transform: uppercase;
|
||||||
|
color: var(--accent-color);
|
||||||
|
&:hover {
|
||||||
|
color: var(--caption-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
33
plugins/bitrix-resources/src/index.ts
Normal file
33
plugins/bitrix-resources/src/index.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// 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 '@hcengineering/platform'
|
||||||
|
|
||||||
|
import BitrixConnect from './components/BitrixConnect.svelte'
|
||||||
|
import BitrixConfigure from './components/BitrixConfigure.svelte'
|
||||||
|
import BitrixIcon from './components/icons/Bitrix.svelte'
|
||||||
|
import BitrixImport from './components/BitrixImport.svelte'
|
||||||
|
|
||||||
|
export default async (): Promise<Resources> => ({
|
||||||
|
component: {
|
||||||
|
BitrixIcon,
|
||||||
|
BitrixConnect,
|
||||||
|
BitrixConfigure,
|
||||||
|
BitrixImport
|
||||||
|
},
|
||||||
|
handler: {
|
||||||
|
DisconnectHandler: async () => {}
|
||||||
|
}
|
||||||
|
})
|
47
plugins/bitrix-resources/src/plugin.ts
Normal file
47
plugins/bitrix-resources/src/plugin.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// 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 { IntlString, mergeIds } from '@hcengineering/platform'
|
||||||
|
|
||||||
|
import bitrix, { bitrixId } from '@hcengineering/bitrix'
|
||||||
|
import { Ref } from '@hcengineering/core'
|
||||||
|
import { Handler, IntegrationType } from '@hcengineering/setting'
|
||||||
|
import { AnyComponent } from '@hcengineering/ui'
|
||||||
|
|
||||||
|
export default mergeIds(bitrixId, bitrix, {
|
||||||
|
string: {
|
||||||
|
BitrixTokenUrl: '' as IntlString,
|
||||||
|
Bitrix: '' as IntlString,
|
||||||
|
BitrixDesc: '' as IntlString,
|
||||||
|
Settings: '' as IntlString,
|
||||||
|
EntityMapping: '' as IntlString,
|
||||||
|
NotAllowed: '' as IntlString,
|
||||||
|
AddMapping: '' as IntlString,
|
||||||
|
BitrixEntityType: '' as IntlString,
|
||||||
|
FieldMapping: '' as IntlString,
|
||||||
|
AddField: '' as IntlString,
|
||||||
|
Attribute: '' as IntlString,
|
||||||
|
MapField: '' as IntlString
|
||||||
|
},
|
||||||
|
component: {
|
||||||
|
BitrixIcon: '' as AnyComponent
|
||||||
|
},
|
||||||
|
handler: {
|
||||||
|
DisconnectHandler: '' as Handler
|
||||||
|
},
|
||||||
|
integrationType: {
|
||||||
|
Bitrix: '' as Ref<IntegrationType>
|
||||||
|
}
|
||||||
|
})
|
45
plugins/bitrix-resources/src/types.ts
Normal file
45
plugins/bitrix-resources/src/types.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface BitrixProfile {
|
||||||
|
ID: string
|
||||||
|
ADMIN: boolean
|
||||||
|
NAME: string
|
||||||
|
LAST_NAME: string
|
||||||
|
PERSONAL_GENDER: string
|
||||||
|
PERSONAL_PHOTO: string
|
||||||
|
TIME_ZONE: string
|
||||||
|
TIME_ZONE_OFFSET: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NumberString = string
|
||||||
|
export type ISODate = string
|
||||||
|
export type BoolString = 'Y' | 'N'
|
||||||
|
export type GenderString = 'M' | 'F' | ''
|
||||||
|
|
||||||
|
export interface MultiField {
|
||||||
|
readonly ID: NumberString
|
||||||
|
readonly VALUE_TYPE: string
|
||||||
|
readonly VALUE: string
|
||||||
|
readonly TYPE_ID: string
|
||||||
|
}
|
||||||
|
export type MultiFieldArray = ReadonlyArray<Pick<MultiField, 'VALUE' | 'VALUE_TYPE'>>
|
||||||
|
|
||||||
|
export interface StatusValue {
|
||||||
|
CATEGORY_ID: string | null
|
||||||
|
COLOR: string | null
|
||||||
|
ENTITY_ID: string | null
|
||||||
|
ID: number
|
||||||
|
NAME: string
|
||||||
|
NAME_INIT: string | null
|
||||||
|
SEMANTICS: string | null
|
||||||
|
SORT: string | null
|
||||||
|
STATUS_ID: string | null
|
||||||
|
SYSTEM: 'Y' | 'N'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BitrixResult {
|
||||||
|
result: any
|
||||||
|
next: number
|
||||||
|
total: number
|
||||||
|
}
|
277
plugins/bitrix-resources/src/utils.ts
Normal file
277
plugins/bitrix-resources/src/utils.ts
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
import {
|
||||||
|
BitrixEntityMapping,
|
||||||
|
BitrixFieldMapping,
|
||||||
|
BitrixSyncDoc,
|
||||||
|
CopyValueOperation,
|
||||||
|
CreateChannelOperation,
|
||||||
|
CreateTagOperation,
|
||||||
|
MappingOperation
|
||||||
|
} from '@hcengineering/bitrix'
|
||||||
|
import { Comment } from '@hcengineering/chunter'
|
||||||
|
import contact, { Channel, EmployeeAccount } from '@hcengineering/contact'
|
||||||
|
import core, { AnyAttribute, Class, Client, Data, Doc, generateId, Mixin, Ref, Space } from '@hcengineering/core'
|
||||||
|
import tags, { TagElement, TagReference } from '@hcengineering/tags'
|
||||||
|
import { getColorNumberByText } from '@hcengineering/ui'
|
||||||
|
|
||||||
|
export function collectFields (fieldMapping: BitrixFieldMapping[]): string[] {
|
||||||
|
const fields: string[] = ['ID']
|
||||||
|
for (const f of fieldMapping) {
|
||||||
|
switch (f.operation.kind) {
|
||||||
|
case MappingOperation.CopyValue:
|
||||||
|
fields.push(
|
||||||
|
...Array.from(f.operation.patterns.map((it) => it.field).filter((it) => it !== undefined) as string[])
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case MappingOperation.CreateChannel:
|
||||||
|
fields.push(...Array.from(f.operation.fields.map((it) => it.field).filter((it) => it !== undefined)))
|
||||||
|
break
|
||||||
|
case MappingOperation.CreateTag:
|
||||||
|
fields.push(...Array.from(f.operation.fields.map((it) => it.field).filter((it) => it !== undefined)))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConvertResult {
|
||||||
|
document: BitrixSyncDoc // Document we should achive
|
||||||
|
mixins: Record<Ref<Mixin<Doc>>, Data<Doc>> // Mixins of document we will achive
|
||||||
|
extraDocs: Doc[] // Extra documents we will achive, comments etc.
|
||||||
|
blobs: File[] //
|
||||||
|
comments?: Promise<Array<BitrixSyncDoc & Comment>>
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function convert (
|
||||||
|
client: Client,
|
||||||
|
entity: BitrixEntityMapping,
|
||||||
|
space: Ref<Space>,
|
||||||
|
fields: BitrixFieldMapping[],
|
||||||
|
rawDocument: any,
|
||||||
|
prevExtra: Doc[], // <<-- a list of previous extra documents, so for example TagElement will be reused, if present for more what one item and required to be created
|
||||||
|
tagElements: Map<Ref<Class<Doc>>, TagElement[]>, // TagElement cache.
|
||||||
|
userList: Map<string, Ref<EmployeeAccount>>
|
||||||
|
): Promise<ConvertResult> {
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
const document: BitrixSyncDoc = {
|
||||||
|
_id: generateId(),
|
||||||
|
type: entity.type,
|
||||||
|
bitrixId: `${rawDocument.ID as string}`,
|
||||||
|
_class: entity.ofClass,
|
||||||
|
space,
|
||||||
|
modifiedOn: new Date(rawDocument.DATE_CREATE).getTime(),
|
||||||
|
modifiedBy: userList.get(rawDocument.CREATED_BY_ID) ?? core.account.System
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain a proper modified by for document
|
||||||
|
|
||||||
|
const newExtraDocs: Doc[] = []
|
||||||
|
const blobs: File[] = []
|
||||||
|
const mixins: Record<Ref<Mixin<Doc>>, Data<Doc>> = {}
|
||||||
|
|
||||||
|
const extractValue = (field?: string, alternatives?: string[]): any | undefined => {
|
||||||
|
if (field !== undefined) {
|
||||||
|
let lval = rawDocument[field]
|
||||||
|
if ((lval == null || lval === '') && alternatives !== undefined) {
|
||||||
|
for (const alt of alternatives) {
|
||||||
|
lval = rawDocument[alt]
|
||||||
|
if (lval != null) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const bfield = entity.bitrixFields[field]
|
||||||
|
if (bfield === undefined) {
|
||||||
|
console.trace('Bitrix field not found', field)
|
||||||
|
} else if (bfield.type === 'integer' || bfield.type === 'double') {
|
||||||
|
if (bfield.isMultiple && Array.isArray(lval)) {
|
||||||
|
return lval[0] ?? 0
|
||||||
|
}
|
||||||
|
return lval
|
||||||
|
} else if (bfield.type === 'crm_multifield') {
|
||||||
|
if (Array.isArray(lval)) {
|
||||||
|
return lval.map((it) => it.VALUE)
|
||||||
|
}
|
||||||
|
} else if (bfield.type === 'string' || bfield.type === 'url') {
|
||||||
|
if (bfield.isMultiple && Array.isArray(lval)) {
|
||||||
|
return lval.join(', ')
|
||||||
|
}
|
||||||
|
return lval
|
||||||
|
} else if (bfield.type === 'date') {
|
||||||
|
if (lval !== '' && lval != null) {
|
||||||
|
return new Date(lval)
|
||||||
|
}
|
||||||
|
} else if (bfield.type === 'char') {
|
||||||
|
return lval === 'Y'
|
||||||
|
} else if (bfield.type === 'enumeration' || bfield.type === 'crm_status') {
|
||||||
|
if (lval != null && lval !== '') {
|
||||||
|
if (bfield.isMultiple && Array.isArray(lval)) {
|
||||||
|
lval = lval[0] ?? ''
|
||||||
|
}
|
||||||
|
const eValue = bfield.items?.find((it) => it.ID === lval)?.VALUE
|
||||||
|
if (eValue !== undefined) {
|
||||||
|
return eValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCopyValue = async (attr: AnyAttribute, operation: CopyValueOperation): Promise<any> => {
|
||||||
|
const r: Array<string | number | boolean | Date> = []
|
||||||
|
for (const o of operation.patterns) {
|
||||||
|
if (o.text.length > 0) {
|
||||||
|
r.push(o.text)
|
||||||
|
}
|
||||||
|
const lval = extractValue(o.field, o.alternatives)
|
||||||
|
if (lval != null) {
|
||||||
|
r.push(lval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r.length === 1) {
|
||||||
|
return r[0]
|
||||||
|
}
|
||||||
|
if (r.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return r.join('').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getChannelValue = async (attr: AnyAttribute, operation: CreateChannelOperation): Promise<any> => {
|
||||||
|
for (const f of operation.fields) {
|
||||||
|
const lval = extractValue(f.field)
|
||||||
|
if (lval != null && lval !== '') {
|
||||||
|
const vals = Array.isArray(lval) ? lval : [lval]
|
||||||
|
for (const llVal of vals) {
|
||||||
|
const c: Channel = {
|
||||||
|
_id: generateId(),
|
||||||
|
_class: contact.class.Channel,
|
||||||
|
attachedTo: document._id,
|
||||||
|
attachedToClass: attr.attributeOf,
|
||||||
|
collection: attr.name,
|
||||||
|
modifiedBy: document.modifiedBy,
|
||||||
|
value: llVal,
|
||||||
|
provider: f.provider,
|
||||||
|
space: document.space,
|
||||||
|
modifiedOn: document.modifiedOn
|
||||||
|
}
|
||||||
|
newExtraDocs.push(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTagValue = async (attr: AnyAttribute, operation: CreateTagOperation): Promise<any> => {
|
||||||
|
const elements =
|
||||||
|
tagElements.get(attr.attributeOf) ??
|
||||||
|
(await client.findAll(tags.class.TagElement, {
|
||||||
|
targetClass: attr.attributeOf
|
||||||
|
}))
|
||||||
|
|
||||||
|
const references = await client.findAll(tags.class.TagReference, {
|
||||||
|
attachedTo: document._id
|
||||||
|
})
|
||||||
|
// Add tags creation requests from previous conversions.
|
||||||
|
elements.push(...prevExtra.filter((it) => it._class === tags.class.TagElement).map((it) => it as TagElement))
|
||||||
|
|
||||||
|
tagElements.set(attr.attributeOf, elements)
|
||||||
|
const defaultCategory = await client.findOne(tags.class.TagCategory, {
|
||||||
|
targetClass: attr.attributeOf,
|
||||||
|
default: true
|
||||||
|
})
|
||||||
|
if (defaultCategory === undefined) {
|
||||||
|
console.error('could not proceed tags without default category')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const o of operation.fields) {
|
||||||
|
const lval = extractValue(o.field)
|
||||||
|
let vals: string[] = []
|
||||||
|
if (lval == null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (o.split !== '' && o.split != null) {
|
||||||
|
vals = `${lval as string}`.split(o.split)
|
||||||
|
} else {
|
||||||
|
vals = [lval as string]
|
||||||
|
}
|
||||||
|
for (let vv of vals) {
|
||||||
|
vv = vv.trim()
|
||||||
|
if (vv === '') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Find existing element and create reference based on it.
|
||||||
|
let tag: TagElement | undefined = elements.find((it) => it.title === vv)
|
||||||
|
if (tag === undefined) {
|
||||||
|
tag = {
|
||||||
|
_id: generateId(),
|
||||||
|
_class: tags.class.TagElement,
|
||||||
|
category: defaultCategory._id,
|
||||||
|
color: getColorNumberByText(vv),
|
||||||
|
description: '',
|
||||||
|
title: vv,
|
||||||
|
targetClass: attr.attributeOf,
|
||||||
|
space: tags.space.Tags,
|
||||||
|
modifiedBy: document.modifiedBy,
|
||||||
|
modifiedOn: document.modifiedOn
|
||||||
|
}
|
||||||
|
newExtraDocs.push(tag)
|
||||||
|
}
|
||||||
|
const ref: TagReference = {
|
||||||
|
_id: generateId(),
|
||||||
|
attachedTo: document._id,
|
||||||
|
attachedToClass: attr.attributeOf,
|
||||||
|
collection: attr.name,
|
||||||
|
_class: tags.class.TagReference,
|
||||||
|
tag: tag._id,
|
||||||
|
color: getColorNumberByText(vv),
|
||||||
|
title: vv,
|
||||||
|
weight: o.weight,
|
||||||
|
modifiedBy: document.modifiedBy,
|
||||||
|
modifiedOn: document.modifiedOn,
|
||||||
|
space: tags.space.Tags
|
||||||
|
}
|
||||||
|
if (references.find((it) => it.title === vv) === undefined) {
|
||||||
|
// Add only if not already added
|
||||||
|
newExtraDocs.push(ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const f of fields) {
|
||||||
|
const attr = hierarchy.getAttribute(f.ofClass, f.attributeName)
|
||||||
|
if (attr === undefined) {
|
||||||
|
console.trace('Attribue not found', f)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let value: any
|
||||||
|
switch (f.operation.kind) {
|
||||||
|
case MappingOperation.CopyValue:
|
||||||
|
value = await getCopyValue(attr, f.operation)
|
||||||
|
break
|
||||||
|
case MappingOperation.CreateChannel:
|
||||||
|
value = await getChannelValue(attr, f.operation)
|
||||||
|
break
|
||||||
|
case MappingOperation.CreateTag:
|
||||||
|
value = await getTagValue(attr, f.operation)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (value !== undefined) {
|
||||||
|
if (hierarchy.isMixin(attr.attributeOf)) {
|
||||||
|
mixins[attr.attributeOf] = { ...mixins[attr.attributeOf], [attr.name]: value }
|
||||||
|
} else {
|
||||||
|
;(document as any)[attr.name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { document, mixins, extraDocs: newExtraDocs, blobs }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function toClassRef (val: any): Ref<Class<Doc>> {
|
||||||
|
return val as Ref<Class<Doc>>
|
||||||
|
}
|
5
plugins/bitrix-resources/svelte.config.js
Normal file
5
plugins/bitrix-resources/svelte.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const sveltePreprocess = require('svelte-preprocess')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
preprocess: sveltePreprocess()
|
||||||
|
};
|
16
plugins/bitrix-resources/tsconfig.json
Normal file
16
plugins/bitrix-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/bitrix/.eslintrc.js
Normal file
7
plugins/bitrix/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/config/eslint.config.json'],
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: './tsconfig.json'
|
||||||
|
}
|
||||||
|
}
|
4
plugins/bitrix/.npmignore
Normal file
4
plugins/bitrix/.npmignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!/lib/**
|
||||||
|
!CHANGELOG.md
|
||||||
|
/lib/**/__tests__/
|
18
plugins/bitrix/config/rig.json
Normal file
18
plugins/bitrix/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": "@hcengineering/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"
|
||||||
|
}
|
36
plugins/bitrix/package.json
Normal file
36
plugins/bitrix/package.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "@hcengineering/bitrix",
|
||||||
|
"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": {
|
||||||
|
"@hcengineering/platform-rig": "~0.6.0",
|
||||||
|
"@types/heft-jest": "^1.0.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
|
"eslint-plugin-n": "^15.4.0",
|
||||||
|
"eslint": "^8.26.0",
|
||||||
|
"@typescript-eslint/parser": "^5.41.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^23.0.0",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"@rushstack/heft": "^0.47.9",
|
||||||
|
"typescript": "^4.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hcengineering/platform": "^0.6.8",
|
||||||
|
"@hcengineering/core": "^0.6.19",
|
||||||
|
"@hcengineering/ui": "^0.6.2",
|
||||||
|
"@hcengineering/preference": "^0.6.1",
|
||||||
|
"@hcengineering/tags": "~0.6.2",
|
||||||
|
"@hcengineering/contact": "~0.6.8"
|
||||||
|
}
|
||||||
|
}
|
184
plugins/bitrix/src/index.ts
Normal file
184
plugins/bitrix/src/index.ts
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
//
|
||||||
|
// 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 { ChannelProvider } from '@hcengineering/contact'
|
||||||
|
import type { AttachedDoc, Class, Doc, Mixin, Ref, Space } from '@hcengineering/core'
|
||||||
|
import type { Plugin } from '@hcengineering/platform'
|
||||||
|
import { Asset, plugin } from '@hcengineering/platform'
|
||||||
|
import { ExpertKnowledge, InitialKnowledge, MeaningfullKnowledge } from '@hcengineering/tags'
|
||||||
|
import { AnyComponent } from '@hcengineering/ui'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface BitrixSyncDoc extends Doc {
|
||||||
|
type: string
|
||||||
|
bitrixId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export enum BitrixEntityType {
|
||||||
|
Comment = 'crm.timeline.comment',
|
||||||
|
Binding = 'crm.timeline.bindings',
|
||||||
|
Lead = 'crm.lead',
|
||||||
|
Activity = 'crm.activity',
|
||||||
|
Company = 'crm.company'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export const mappingTypes = [
|
||||||
|
{ label: 'Leads', id: BitrixEntityType.Lead },
|
||||||
|
// { label: 'Comments', id: BitrixEntityType.Comment },
|
||||||
|
{ label: 'Company', id: BitrixEntityType.Company }
|
||||||
|
// { label: 'Activity', id: BitrixEntityType.Activity }
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface FieldValue {
|
||||||
|
type: string
|
||||||
|
statusType?: string
|
||||||
|
isRequired: boolean
|
||||||
|
isReadOnly: boolean
|
||||||
|
isImmutable: boolean
|
||||||
|
isMultiple: boolean
|
||||||
|
isDynamic: boolean
|
||||||
|
title: string
|
||||||
|
|
||||||
|
formLabel?: string
|
||||||
|
filterLabel?: string
|
||||||
|
items?: Array<{
|
||||||
|
ID: string
|
||||||
|
VALUE: string
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface Fields {
|
||||||
|
[key: string]: FieldValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface BitrixEntityMapping extends Doc {
|
||||||
|
ofClass: Ref<Class<Doc>>
|
||||||
|
type: string
|
||||||
|
bitrixFields: Fields
|
||||||
|
|
||||||
|
fields: number
|
||||||
|
|
||||||
|
comments: boolean
|
||||||
|
activity: boolean
|
||||||
|
attachments: boolean
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export enum MappingOperation {
|
||||||
|
CopyValue,
|
||||||
|
CreateTag, // Create tag
|
||||||
|
CreateChannel // Create channel
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface CopyPattern {
|
||||||
|
text: string
|
||||||
|
field?: string
|
||||||
|
alternatives?: string[]
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface CopyValueOperation {
|
||||||
|
kind: MappingOperation.CopyValue
|
||||||
|
patterns: CopyPattern[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface TagField {
|
||||||
|
weight: InitialKnowledge | MeaningfullKnowledge | ExpertKnowledge
|
||||||
|
|
||||||
|
field: string
|
||||||
|
split: string // If defined values from field will be split to check for multiple values.
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface CreateTagOperation {
|
||||||
|
kind: MappingOperation.CreateTag
|
||||||
|
|
||||||
|
fields: TagField[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface ChannelFieldMapping {
|
||||||
|
provider: Ref<ChannelProvider>
|
||||||
|
field: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface CreateChannelOperation {
|
||||||
|
kind: MappingOperation.CreateChannel
|
||||||
|
fields: ChannelFieldMapping[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface BitrixFieldMapping extends AttachedDoc {
|
||||||
|
ofClass: Ref<Class<Doc>> // Specify mixin if applicable
|
||||||
|
attributeName: string
|
||||||
|
|
||||||
|
operation: CopyValueOperation | CreateTagOperation | CreateChannelOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export const bitrixId = 'bitrix' as Plugin
|
||||||
|
|
||||||
|
export default plugin(bitrixId, {
|
||||||
|
mixin: {
|
||||||
|
BitrixSyncDoc: '' as Ref<Mixin<BitrixSyncDoc>>
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
EntityMapping: '' as Ref<Class<BitrixEntityMapping>>,
|
||||||
|
FieldMapping: '' as Ref<Class<BitrixFieldMapping>>
|
||||||
|
},
|
||||||
|
component: {
|
||||||
|
BitrixIntegration: '' as AnyComponent
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
Bitrix: '' as Asset
|
||||||
|
},
|
||||||
|
space: {
|
||||||
|
Mappings: '' as Ref<Space>
|
||||||
|
}
|
||||||
|
})
|
9
plugins/bitrix/tsconfig.json
Normal file
9
plugins/bitrix/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib",
|
||||||
|
"lib": ["esnext", "dom"]
|
||||||
|
}
|
||||||
|
}
|
@ -66,6 +66,7 @@
|
|||||||
"ShowAttribute": "Show attribute",
|
"ShowAttribute": "Show attribute",
|
||||||
"HideAttribute": "Hide attribute",
|
"HideAttribute": "Hide attribute",
|
||||||
"Visibility": "Visibility",
|
"Visibility": "Visibility",
|
||||||
"Hidden": "Hidden"
|
"Hidden": "Hidden",
|
||||||
|
"Configure": "Configure"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -67,6 +67,7 @@
|
|||||||
"ShowAttribute": "Hide",
|
"ShowAttribute": "Hide",
|
||||||
"HideAttribute": "Show",
|
"HideAttribute": "Show",
|
||||||
"Visibility": "Видимость",
|
"Visibility": "Видимость",
|
||||||
"Hidden": "Спрятанный"
|
"Hidden": "Спрятанный",
|
||||||
|
"Configure": "Настроить..."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -31,6 +31,7 @@
|
|||||||
import {
|
import {
|
||||||
Action,
|
Action,
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
|
AnySvelteComponent,
|
||||||
CircleButton,
|
CircleButton,
|
||||||
Component,
|
Component,
|
||||||
getEventPositionElement,
|
getEventPositionElement,
|
||||||
@ -51,6 +52,15 @@
|
|||||||
import EditClassLabel from './EditClassLabel.svelte'
|
import EditClassLabel from './EditClassLabel.svelte'
|
||||||
|
|
||||||
export let _class: Ref<Class<Doc>>
|
export let _class: Ref<Class<Doc>>
|
||||||
|
export let ofClass: Ref<Class<Doc>> | undefined
|
||||||
|
|
||||||
|
export let attributeMapper:
|
||||||
|
| {
|
||||||
|
component: AnySvelteComponent
|
||||||
|
label: IntlString
|
||||||
|
props: Record<string, any>
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
@ -66,7 +76,9 @@
|
|||||||
|
|
||||||
function getCustomAttributes (_class: Ref<Class<Doc>>): AnyAttribute[] {
|
function getCustomAttributes (_class: Ref<Class<Doc>>): AnyAttribute[] {
|
||||||
const cl = hierarchy.getClass(_class)
|
const cl = hierarchy.getClass(_class)
|
||||||
const attributes = Array.from(hierarchy.getAllAttributes(_class, cl.extends).values())
|
const attributes = Array.from(
|
||||||
|
hierarchy.getAllAttributes(_class, _class === ofClass ? core.class.Doc : cl.extends).values()
|
||||||
|
)
|
||||||
// const filtred = attributes.filter((p) => !p.hidden)
|
// const filtred = attributes.filter((p) => !p.hidden)
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
@ -210,6 +222,11 @@
|
|||||||
<Label label={settings.string.Custom} />
|
<Label label={settings.string.Custom} />
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
{#if attributeMapper}
|
||||||
|
<th>
|
||||||
|
<Label label={attributeMapper.label} />
|
||||||
|
</th>
|
||||||
|
{/if}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -223,7 +240,7 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<td>
|
<td>
|
||||||
<div class="antiTable-cells__firstCell">
|
<div class="antiTable-cells__firstCell whitespace-nowrap">
|
||||||
<Label label={attr.label} />
|
<Label label={attr.label} />
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div id="context-menu" class="antiTable-cells__firstCell-menuRow" on:click={(ev) => showMenu(ev, attr)}>
|
<div id="context-menu" class="antiTable-cells__firstCell-menuRow" on:click={(ev) => showMenu(ev, attr)}>
|
||||||
@ -231,7 +248,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="select-text">
|
<td class="select-text whitespace-nowrap">
|
||||||
<Label label={attr.type.label} />
|
<Label label={attr.type.label} />
|
||||||
{#if attrType !== undefined}
|
{#if attrType !== undefined}
|
||||||
: <Label label={attrType} />
|
: <Label label={attrType} />
|
||||||
@ -252,6 +269,11 @@
|
|||||||
<td>
|
<td>
|
||||||
<Component is={view.component.BooleanTruePresenter} props={{ value: attr.isCustom ?? false }} />
|
<Component is={view.component.BooleanTruePresenter} props={{ value: attr.isCustom ?? false }} />
|
||||||
</td>
|
</td>
|
||||||
|
{#if attributeMapper}
|
||||||
|
<td>
|
||||||
|
<svelte:component this={attributeMapper.component} {...attributeMapper.props} attribute={attr} />
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
export let classes: Ref<Class<Doc>>[] = ['contact:class:Contact' as Ref<Class<Doc>>]
|
export let classes: Ref<Class<Doc>>[] = ['contact:class:Contact' as Ref<Class<Doc>>]
|
||||||
export let _class: Ref<Class<Doc>> | undefined
|
export let _class: Ref<Class<Doc>> | undefined
|
||||||
|
export let ofClass: Ref<Class<Doc>> | undefined
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
@ -30,12 +31,16 @@
|
|||||||
function getDescendants (_class: Ref<Class<Doc>>): Ref<Class<Doc>>[] {
|
function getDescendants (_class: Ref<Class<Doc>>): Ref<Class<Doc>>[] {
|
||||||
const result: Ref<Class<Doc>>[] = []
|
const result: Ref<Class<Doc>>[] = []
|
||||||
const desc = hierarchy.getDescendants(_class)
|
const desc = hierarchy.getDescendants(_class)
|
||||||
|
const vars = [ClassifierKind.MIXIN]
|
||||||
|
if (ofClass === undefined) {
|
||||||
|
vars.push(ClassifierKind.CLASS)
|
||||||
|
}
|
||||||
for (const clazz of desc) {
|
for (const clazz of desc) {
|
||||||
const cls = hierarchy.getClass(clazz)
|
const cls = hierarchy.getClass(clazz)
|
||||||
if (
|
if (
|
||||||
cls.extends === _class &&
|
cls.extends === _class &&
|
||||||
!cls.hidden &&
|
!cls.hidden &&
|
||||||
[ClassifierKind.CLASS, ClassifierKind.MIXIN].includes(cls.kind) &&
|
vars.includes(cls.kind) &&
|
||||||
cls.label !== undefined &&
|
cls.label !== undefined &&
|
||||||
(!hierarchy.hasMixin(cls, settings.mixin.Editable) || hierarchy.as(cls, settings.mixin.Editable).value)
|
(!hierarchy.hasMixin(cls, settings.mixin.Editable) || hierarchy.as(cls, settings.mixin.Editable).value)
|
||||||
) {
|
) {
|
||||||
|
@ -13,20 +13,32 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import core, { Class, Doc, Ref } from '@hcengineering/core'
|
import core, { Class, Doc, Obj, Ref } from '@hcengineering/core'
|
||||||
|
import { IntlString } from '@hcengineering/platform'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { getCurrentLocation, Icon, Label, navigate } from '@hcengineering/ui'
|
import { AnySvelteComponent, getCurrentLocation, Icon, Label, navigate } from '@hcengineering/ui'
|
||||||
import setting from '../plugin'
|
import setting from '../plugin'
|
||||||
|
import { filterDescendants } from '../utils'
|
||||||
import ClassAttributes from './ClassAttributes.svelte'
|
import ClassAttributes from './ClassAttributes.svelte'
|
||||||
import ClassHierarchy from './ClassHierarchy.svelte'
|
import ClassHierarchy from './ClassHierarchy.svelte'
|
||||||
|
|
||||||
|
export let ofClass: Ref<Class<Obj>> | undefined
|
||||||
|
export let attributeMapper:
|
||||||
|
| {
|
||||||
|
component: AnySvelteComponent
|
||||||
|
label: IntlString
|
||||||
|
props: Record<string, any>
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
|
export let withoutHeader = false
|
||||||
|
|
||||||
const loc = getCurrentLocation()
|
const loc = getCurrentLocation()
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
|
|
||||||
let _class: Ref<Class<Doc>> | undefined = loc.query?._class as Ref<Class<Doc>> | undefined
|
let _class: Ref<Class<Doc>> | undefined = ofClass ?? (loc.query?._class as Ref<Class<Doc>> | undefined)
|
||||||
|
|
||||||
$: if (_class !== undefined) {
|
$: if (_class !== undefined && ofClass === undefined) {
|
||||||
const loc = getCurrentLocation()
|
const loc = getCurrentLocation()
|
||||||
loc.query = undefined
|
loc.query = undefined
|
||||||
navigate(loc)
|
navigate(loc)
|
||||||
@ -36,28 +48,24 @@
|
|||||||
|
|
||||||
let classes: Ref<Class<Doc>>[] = []
|
let classes: Ref<Class<Doc>>[] = []
|
||||||
clQuery.query(core.class.Class, {}, (res) => {
|
clQuery.query(core.class.Class, {}, (res) => {
|
||||||
classes = res
|
classes = filterDescendants(hierarchy, ofClass, res)
|
||||||
.filter(
|
|
||||||
(p) =>
|
|
||||||
hierarchy.hasMixin(p, setting.mixin.Editable) &&
|
|
||||||
hierarchy.as(p, setting.mixin.Editable).value &&
|
|
||||||
!hierarchy.hasMixin(p, setting.mixin.UserMixin)
|
|
||||||
)
|
|
||||||
.map((p) => p._id)
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="antiComponent">
|
<div class="antiComponent">
|
||||||
|
{#if !withoutHeader}
|
||||||
<div class="ac-header short divide">
|
<div class="ac-header short divide">
|
||||||
<div class="ac-header__icon"><Icon icon={setting.icon.Clazz} size={'medium'} /></div>
|
<div class="ac-header__icon"><Icon icon={setting.icon.Clazz} size={'medium'} /></div>
|
||||||
<div class="ac-header__title"><Label label={setting.string.ClassSetting} /></div>
|
<div class="ac-header__title"><Label label={setting.string.ClassSetting} /></div>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
<div class="ac-body columns hScroll">
|
<div class="ac-body columns hScroll">
|
||||||
<div class="ac-column">
|
<div class="ac-column">
|
||||||
<div class="overflow-y-auto">
|
<div class="overflow-y-auto">
|
||||||
<ClassHierarchy
|
<ClassHierarchy
|
||||||
{classes}
|
{classes}
|
||||||
{_class}
|
{_class}
|
||||||
|
{ofClass}
|
||||||
on:select={(e) => {
|
on:select={(e) => {
|
||||||
_class = e.detail
|
_class = e.detail
|
||||||
}}
|
}}
|
||||||
@ -66,7 +74,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ac-column max">
|
<div class="ac-column max">
|
||||||
{#if _class !== undefined}
|
{#if _class !== undefined}
|
||||||
<ClassAttributes {_class} />
|
<ClassAttributes {_class} {ofClass} {attributeMapper} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
import view from '@hcengineering/view-resources/src/plugin'
|
import view from '@hcengineering/view-resources/src/plugin'
|
||||||
|
|
||||||
export let value: Enum | undefined
|
export let value: Enum | undefined
|
||||||
let name: string = value?.name ?? ''
|
export let name: string = value?.name ?? ''
|
||||||
let values: string[] = value?.enumValues ?? []
|
export let values: string[] = value?.enumValues ?? []
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -13,25 +13,30 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button, Component, eventToHTMLElement, Label, Link } from '@hcengineering/ui'
|
|
||||||
import { getResource } from '@hcengineering/platform'
|
|
||||||
import { showPopup } from '@hcengineering/ui'
|
|
||||||
import type { Integration, IntegrationType } from '@hcengineering/setting'
|
|
||||||
import setting from '../plugin'
|
|
||||||
import { getClient } from '@hcengineering/presentation'
|
|
||||||
import { getCurrentAccount, Ref, Space } from '@hcengineering/core'
|
import { getCurrentAccount, Ref, Space } from '@hcengineering/core'
|
||||||
|
import { getResource } from '@hcengineering/platform'
|
||||||
|
import { getClient } from '@hcengineering/presentation'
|
||||||
|
import type { Integration, IntegrationType } from '@hcengineering/setting'
|
||||||
|
import {
|
||||||
|
AnyComponent,
|
||||||
|
Button,
|
||||||
|
Component,
|
||||||
|
eventToHTMLElement,
|
||||||
|
Label,
|
||||||
|
PopupPosAlignment,
|
||||||
|
showPopup
|
||||||
|
} from '@hcengineering/ui'
|
||||||
|
import setting from '../plugin'
|
||||||
|
|
||||||
export let integrationType: IntegrationType
|
export let integrationType: IntegrationType
|
||||||
export let integration: Integration | undefined
|
export let integration: Integration | undefined
|
||||||
const accountId = getCurrentAccount()._id
|
const accountId = getCurrentAccount()._id
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const onDisconnectP = getResource(integrationType.onDisconnect)
|
|
||||||
const space = accountId as string as Ref<Space>
|
const space = accountId as string as Ref<Space>
|
||||||
|
|
||||||
async function close (res: any): Promise<void> {
|
async function close (res: any): Promise<void> {
|
||||||
if (res?.value) {
|
if (res?.value && integration !== undefined) {
|
||||||
await client.createDoc(setting.class.Integration, space, {
|
await client.update(integration, {
|
||||||
type: integrationType._id,
|
|
||||||
value: res.value,
|
value: res.value,
|
||||||
disabled: false
|
disabled: false
|
||||||
})
|
})
|
||||||
@ -52,18 +57,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function disconnect (): Promise<void> {
|
async function disconnect (): Promise<void> {
|
||||||
if (integration !== undefined) {
|
if (integration !== undefined && integrationType.onDisconnect !== undefined) {
|
||||||
await (
|
const disconnect = await getResource(integrationType.onDisconnect)
|
||||||
await onDisconnectP
|
await disconnect()
|
||||||
)()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleAdd = (e: any) => {
|
const handleConfigure = async (e: any, component?: AnyComponent, pos?: PopupPosAlignment): Promise<void> => {
|
||||||
showPopup(integrationType.createComponent, {}, eventToHTMLElement(e), close)
|
if (component === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (integration === undefined) {
|
||||||
|
const id = await client.createDoc(setting.class.Integration, space, {
|
||||||
|
type: integrationType._id,
|
||||||
|
value: '',
|
||||||
|
disabled: false
|
||||||
|
})
|
||||||
|
integration = await client.findOne(setting.class.Integration, { _id: id })
|
||||||
|
}
|
||||||
|
showPopup(component, { integration }, pos ?? eventToHTMLElement(e), close)
|
||||||
}
|
}
|
||||||
const handleReconnect = (e: any) => {
|
const handleReconnect = (e: any) => {
|
||||||
if (integrationType.reconnectComponent) {
|
if (integrationType.reconnectComponent) {
|
||||||
showPopup(integrationType.reconnectComponent, {}, eventToHTMLElement(e), reconnect)
|
showPopup(integrationType.reconnectComponent, { integration }, eventToHTMLElement(e), reconnect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -72,23 +87,34 @@
|
|||||||
<div class="flex-row-center header">
|
<div class="flex-row-center header">
|
||||||
<div class="icon mr-4"><Component is={integrationType.icon} /></div>
|
<div class="icon mr-4"><Component is={integrationType.icon} /></div>
|
||||||
<div class="flex-grow flex-col">
|
<div class="flex-grow flex-col">
|
||||||
<div class="fs-title max-label overflow-label"><Label label={integrationType.label} /></div>
|
<div class="fs-title overflow-label"><Label label={integrationType.label} /></div>
|
||||||
<div class="text-sm content-dark-color max-label overflow-label">
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
<Label label={integrationType.description} />
|
<Label label={integrationType.description} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp</div>
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
{#if integration}
|
{#if (integration?.value ?? '') === ''}
|
||||||
{#if integration.disabled && integrationType.reconnectComponent}
|
{#if integrationType.createComponent}
|
||||||
|
<Button
|
||||||
|
label={setting.string.Add}
|
||||||
|
kind={'primary'}
|
||||||
|
on:click={(ev) => handleConfigure(ev, integrationType.createComponent)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{:else if (integration?.disabled ?? false) && integrationType.reconnectComponent}
|
||||||
<Button label={setting.string.Reconnect} kind={'primary'} on:click={handleReconnect} />
|
<Button label={setting.string.Reconnect} kind={'primary'} on:click={handleReconnect} />
|
||||||
{:else}
|
{:else if integration?.value !== ''}
|
||||||
|
{#if integrationType.onDisconnect}
|
||||||
<Button label={setting.string.Disconnect} on:click={disconnect} />
|
<Button label={setting.string.Disconnect} on:click={disconnect} />
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{#if integrationType.configureComponent !== undefined}
|
||||||
<Button label={setting.string.Add} kind={'primary'} on:click={handleAdd} />
|
<Button
|
||||||
<Link label={'Learn more'} />
|
label={setting.string.Configure}
|
||||||
|
kind={'primary'}
|
||||||
|
on:click={(ev) => handleConfigure(ev, integrationType.configureComponent, 'top')}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -107,8 +133,8 @@
|
|||||||
}
|
}
|
||||||
.icon {
|
.icon {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 2.25rem;
|
min-width: 2.25rem;
|
||||||
height: 2.25rem;
|
min-height: 2.25rem;
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
margin: 0 1.5rem 0.25rem;
|
margin: 0 1.5rem 0.25rem;
|
||||||
|
@ -23,14 +23,13 @@
|
|||||||
|
|
||||||
export let type: EnumOf | undefined
|
export let type: EnumOf | undefined
|
||||||
export let editable: boolean = true
|
export let editable: boolean = true
|
||||||
|
export let value: Enum | undefined
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
let value: Enum | undefined
|
|
||||||
|
|
||||||
$: value && dispatch('change', { type: TypeEnum(value._id) })
|
$: value && dispatch('change', { type: TypeEnum(value._id) })
|
||||||
$: ref = type?.of
|
$: ref = value?._id ?? type?.of
|
||||||
|
|
||||||
const create = {
|
const create = {
|
||||||
label: setting.string.CreateEnum,
|
label: setting.string.CreateEnum,
|
||||||
|
@ -36,13 +36,15 @@ import Terms from './components/Terms.svelte'
|
|||||||
import BooleanTypeEditor from './components/typeEditors/BooleanTypeEditor.svelte'
|
import BooleanTypeEditor from './components/typeEditors/BooleanTypeEditor.svelte'
|
||||||
import DateTypeEditor from './components/typeEditors/DateTypeEditor.svelte'
|
import DateTypeEditor from './components/typeEditors/DateTypeEditor.svelte'
|
||||||
import EnumTypeEditor from './components/typeEditors/EnumTypeEditor.svelte'
|
import EnumTypeEditor from './components/typeEditors/EnumTypeEditor.svelte'
|
||||||
|
import HyperlinkTypeEditor from './components/typeEditors/HyperlinkTypeEditor.svelte'
|
||||||
import NumberTypeEditor from './components/typeEditors/NumberTypeEditor.svelte'
|
import NumberTypeEditor from './components/typeEditors/NumberTypeEditor.svelte'
|
||||||
import RefEditor from './components/typeEditors/RefEditor.svelte'
|
import RefEditor from './components/typeEditors/RefEditor.svelte'
|
||||||
import StringTypeEditor from './components/typeEditors/StringTypeEditor.svelte'
|
import StringTypeEditor from './components/typeEditors/StringTypeEditor.svelte'
|
||||||
import HyperlinkTypeEditor from './components/typeEditors/HyperlinkTypeEditor.svelte'
|
|
||||||
import WorkspaceSettings from './components/WorkspaceSettings.svelte'
|
import WorkspaceSettings from './components/WorkspaceSettings.svelte'
|
||||||
import setting from './plugin'
|
import setting from './plugin'
|
||||||
|
|
||||||
|
export { ClassSetting }
|
||||||
|
|
||||||
async function DeleteMixin (object: Mixin<Class<Doc>>): Promise<void> {
|
async function DeleteMixin (object: Mixin<Class<Doc>>): Promise<void> {
|
||||||
const docs = await getClient().findAll(object._id, {}, { limit: 1 })
|
const docs = await getClient().findAll(object._id, {}, { limit: 1 })
|
||||||
|
|
||||||
|
47
plugins/setting-resources/src/utils.ts
Normal file
47
plugins/setting-resources/src/utils.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Class, Doc, Hierarchy, Ref } from '@hcengineering/core'
|
||||||
|
import setting from '@hcengineering/setting'
|
||||||
|
|
||||||
|
function isEditable (hierarchy: Hierarchy, p: Class<Doc>): boolean {
|
||||||
|
let ancestors = [p._id]
|
||||||
|
try {
|
||||||
|
ancestors = [...hierarchy.getAncestors(p._id), p._id]
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
for (const ao of ancestors) {
|
||||||
|
try {
|
||||||
|
const cl = hierarchy.getClass(ao)
|
||||||
|
if (hierarchy.hasMixin(cl, setting.mixin.Editable) && hierarchy.as(cl, setting.mixin.Editable).value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
export function filterDescendants (
|
||||||
|
hierarchy: Hierarchy,
|
||||||
|
ofClass: Ref<Class<Doc>> | undefined,
|
||||||
|
res: Array<Class<Doc>>
|
||||||
|
): Array<Ref<Class<Doc>>> {
|
||||||
|
let _classes = res
|
||||||
|
.filter((it) => {
|
||||||
|
try {
|
||||||
|
return ofClass != null ? hierarchy.isDerived(it._id, ofClass) : true
|
||||||
|
} catch (err: any) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((it) => !(it.hidden ?? false))
|
||||||
|
.filter((p) => isEditable(hierarchy, p))
|
||||||
|
|
||||||
|
let len: number
|
||||||
|
const _set = new Set(_classes.map((it) => it._id))
|
||||||
|
do {
|
||||||
|
len = _classes.length
|
||||||
|
_classes = _classes.filter((it) => (it.extends != null ? !_set.has(it.extends) : false))
|
||||||
|
} while (len !== _classes.length)
|
||||||
|
|
||||||
|
return _classes.map((p) => p._id)
|
||||||
|
}
|
@ -13,10 +13,10 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Asset, IntlString, plugin, Resource } from '@hcengineering/platform'
|
|
||||||
import type { Plugin } from '@hcengineering/platform'
|
|
||||||
import { AnyComponent } from '@hcengineering/ui'
|
|
||||||
import type { Class, Doc, Mixin, Ref } from '@hcengineering/core'
|
import type { Class, Doc, Mixin, Ref } from '@hcengineering/core'
|
||||||
|
import type { Plugin } from '@hcengineering/platform'
|
||||||
|
import { Asset, IntlString, plugin, Resource } from '@hcengineering/platform'
|
||||||
|
import { AnyComponent } from '@hcengineering/ui'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -30,9 +30,12 @@ export interface IntegrationType extends Doc {
|
|||||||
label: IntlString
|
label: IntlString
|
||||||
description: IntlString
|
description: IntlString
|
||||||
icon: AnyComponent
|
icon: AnyComponent
|
||||||
createComponent: AnyComponent
|
|
||||||
onDisconnect: Handler
|
createComponent?: AnyComponent
|
||||||
|
onDisconnect?: Handler
|
||||||
reconnectComponent?: AnyComponent
|
reconnectComponent?: AnyComponent
|
||||||
|
|
||||||
|
configureComponent?: AnyComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,7 +146,8 @@ export default plugin(settingId, {
|
|||||||
SelectWorkspace: '' as IntlString,
|
SelectWorkspace: '' as IntlString,
|
||||||
Reconnect: '' as IntlString,
|
Reconnect: '' as IntlString,
|
||||||
ClassSetting: '' as IntlString,
|
ClassSetting: '' as IntlString,
|
||||||
Owners: '' as IntlString
|
Owners: '' as IntlString,
|
||||||
|
Configure: '' as IntlString
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
EditProfile: '' as Asset,
|
EditProfile: '' as Asset,
|
||||||
|
@ -34,7 +34,9 @@ import CreateTagElement from './components/CreateTagElement.svelte'
|
|||||||
import { ObjQueryType } from '@hcengineering/core'
|
import { ObjQueryType } from '@hcengineering/core'
|
||||||
import { getRefs } from './utils'
|
import { getRefs } from './utils'
|
||||||
import { Filter } from '@hcengineering/view'
|
import { Filter } from '@hcengineering/view'
|
||||||
|
import WeightPopup from './components/WeightPopup.svelte'
|
||||||
|
|
||||||
|
export { WeightPopup }
|
||||||
export async function tagsInResult (filter: Filter, onUpdate: () => void): Promise<ObjQueryType<any>> {
|
export async function tagsInResult (filter: Filter, onUpdate: () => void): Promise<ObjQueryType<any>> {
|
||||||
const result = await getRefs(filter, onUpdate)
|
const result = await getRefs(filter, onUpdate)
|
||||||
return { $in: result }
|
return { $in: result }
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
import { ListView, resizeObserver } from '@hcengineering/ui'
|
import { ListView, resizeObserver } from '@hcengineering/ui'
|
||||||
import ObjectPresenter from './ObjectPresenter.svelte'
|
import ObjectPresenter from './ObjectPresenter.svelte'
|
||||||
import { tick } from 'svelte'
|
import { tick } from 'svelte'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
export let viewContext: ViewContext
|
export let viewContext: ViewContext
|
||||||
|
|
||||||
@ -171,6 +172,7 @@
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActionContext
|
<ActionContext
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { IntlString } from '@hcengineering/platform'
|
import type { IntlString } from '@hcengineering/platform'
|
||||||
import { EditBox, Label, showPopup, eventToHTMLElement } from '@hcengineering/ui'
|
import { EditBox, Label, showPopup, eventToHTMLElement, Button } from '@hcengineering/ui'
|
||||||
import EditBoxPopup from './EditBoxPopup.svelte'
|
import EditBoxPopup from './EditBoxPopup.svelte'
|
||||||
|
|
||||||
// export let label: IntlString
|
// export let label: IntlString
|
||||||
@ -24,7 +24,7 @@
|
|||||||
export let focus: boolean
|
export let focus: boolean
|
||||||
// export let maxWidth: string = '10rem'
|
// export let maxWidth: string = '10rem'
|
||||||
export let onChange: (value: number | undefined) => void
|
export let onChange: (value: number | undefined) => void
|
||||||
export let kind: 'no-border' | 'link' = 'no-border'
|
export let kind: 'no-border' | 'link' | 'button' = 'no-border'
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
|
|
||||||
let shown: boolean = false
|
let shown: boolean = false
|
||||||
@ -38,6 +38,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if kind === 'link'}
|
{#if kind === 'link'}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
class="link-container"
|
class="link-container"
|
||||||
on:click={(ev) => {
|
on:click={(ev) => {
|
||||||
@ -58,6 +59,30 @@
|
|||||||
<span class="dark-color"><Label label={placeholder} /></span>
|
<span class="dark-color"><Label label={placeholder} /></span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{:else if kind === 'button'}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<Button
|
||||||
|
size={'small'}
|
||||||
|
on:click={(ev) => {
|
||||||
|
if (!shown && !readonly) {
|
||||||
|
showPopup(EditBoxPopup, { value, format: 'number' }, eventToHTMLElement(ev), (res) => {
|
||||||
|
if (Number.isFinite(res)) {
|
||||||
|
value = res
|
||||||
|
onChange(value)
|
||||||
|
}
|
||||||
|
shown = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="content">
|
||||||
|
{#if value !== undefined}
|
||||||
|
<span class="overflow-label">{value}</span>
|
||||||
|
{:else}
|
||||||
|
<span class="dark-color"><Label label={placeholder} /></span>
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
</Button>
|
||||||
{:else if readonly}
|
{:else if readonly}
|
||||||
{#if value !== undefined}
|
{#if value !== undefined}
|
||||||
<span class="overflow-label">{value}</span>
|
<span class="overflow-label">{value}</span>
|
||||||
|
27
rush.json
27
rush.json
@ -16,7 +16,7 @@
|
|||||||
* path segment in the "$schema" field for all your Rush config files. This will ensure
|
* path segment in the "$schema" field for all your Rush config files. This will ensure
|
||||||
* correct error-underlining and tab-completion for editors such as VS Code.
|
* correct error-underlining and tab-completion for editors such as VS Code.
|
||||||
*/
|
*/
|
||||||
"rushVersion": "5.78.0",
|
"rushVersion": "5.86.0",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The next field selects which package manager should be installed and determines its version.
|
* The next field selects which package manager should be installed and determines its version.
|
||||||
@ -176,7 +176,7 @@
|
|||||||
* and set projectFolderMaxDepth to a large number.
|
* and set projectFolderMaxDepth to a large number.
|
||||||
*/
|
*/
|
||||||
// "projectFolderMinDepth": 2,
|
// "projectFolderMinDepth": 2,
|
||||||
// "projectFolderMaxDepth": 2,
|
"projectFolderMaxDepth": 3,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Today the npmjs.com registry enforces fairly strict naming rules for packages, but in the early
|
* Today the npmjs.com registry enforces fairly strict naming rules for packages, but in the early
|
||||||
@ -1413,6 +1413,27 @@
|
|||||||
"packageName": "@hcengineering/minio",
|
"packageName": "@hcengineering/minio",
|
||||||
"projectFolder": "server/minio",
|
"projectFolder": "server/minio",
|
||||||
"shouldPublish": true
|
"shouldPublish": true
|
||||||
}
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"packageName": "@hcengineering/bitrix",
|
||||||
|
"projectFolder": "plugins/bitrix",
|
||||||
|
"shouldPublish": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "@hcengineering/bitrix-assets",
|
||||||
|
"projectFolder": "plugins/bitrix-assets",
|
||||||
|
"shouldPublish": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "@hcengineering/bitrix-resources",
|
||||||
|
"projectFolder": "plugins/bitrix-resources",
|
||||||
|
"shouldPublish": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "@hcengineering/model-bitrix",
|
||||||
|
"projectFolder": "models/bitrix",
|
||||||
|
"shouldPublish": true
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -517,8 +517,13 @@ class TServerStorage implements ServerStorage {
|
|||||||
;({ passed, onEnd } = await this.verifyApplyIf(ctx, applyIf))
|
;({ passed, onEnd } = await this.verifyApplyIf(ctx, applyIf))
|
||||||
result = passed
|
result = passed
|
||||||
if (passed) {
|
if (passed) {
|
||||||
// Store apply if transaction
|
// Store apply if transaction's
|
||||||
await ctx.with('domain-tx', { _class, objClass }, async () => await this.getAdapter(DOMAIN_TX).tx(tx))
|
await ctx.with('domain-tx', { _class, objClass }, async () => {
|
||||||
|
const atx = await this.getAdapter(DOMAIN_TX)
|
||||||
|
for (const ctx of applyIf.txes) {
|
||||||
|
await atx.tx(ctx)
|
||||||
|
}
|
||||||
|
})
|
||||||
derived = await this.processDerivedTxes(applyIf.txes, ctx, triggerFx)
|
derived = await this.processDerivedTxes(applyIf.txes, ctx, triggerFx)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user