UBERF-5603 (#4754)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2024-02-23 14:39:49 +06:00 committed by GitHub
parent cc8a1d549a
commit ffc8d1dfac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
184 changed files with 2755 additions and 561 deletions

View File

@ -152,6 +152,15 @@ dependencies:
'@rush-temp/gmail-resources': '@rush-temp/gmail-resources':
specifier: file:./projects/gmail-resources.tgz specifier: file:./projects/gmail-resources.tgz
version: file:projects/gmail-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) version: file:projects/gmail-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)
'@rush-temp/guest':
specifier: file:./projects/guest.tgz
version: file:projects/guest.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2)
'@rush-temp/guest-assets':
specifier: file:./projects/guest-assets.tgz
version: file:projects/guest-assets.tgz(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2)
'@rush-temp/guest-resources':
specifier: file:./projects/guest-resources.tgz
version: file:projects/guest-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)
'@rush-temp/hr': '@rush-temp/hr':
specifier: file:./projects/hr.tgz specifier: file:./projects/hr.tgz
version: file:projects/hr.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2) version: file:projects/hr.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2)
@ -236,6 +245,9 @@ dependencies:
'@rush-temp/model-gmail': '@rush-temp/model-gmail':
specifier: file:./projects/model-gmail.tgz specifier: file:./projects/model-gmail.tgz
version: file:projects/model-gmail.tgz(svelte@4.2.11) version: file:projects/model-gmail.tgz(svelte@4.2.11)
'@rush-temp/model-guest':
specifier: file:./projects/model-guest.tgz
version: file:projects/model-guest.tgz(svelte@4.2.11)
'@rush-temp/model-hr': '@rush-temp/model-hr':
specifier: file:./projects/model-hr.tgz specifier: file:./projects/model-hr.tgz
version: file:projects/model-hr.tgz(svelte@4.2.11) version: file:projects/model-hr.tgz(svelte@4.2.11)
@ -284,6 +296,9 @@ dependencies:
'@rush-temp/model-server-gmail': '@rush-temp/model-server-gmail':
specifier: file:./projects/model-server-gmail.tgz specifier: file:./projects/model-server-gmail.tgz
version: file:projects/model-server-gmail.tgz(svelte@4.2.11) version: file:projects/model-server-gmail.tgz(svelte@4.2.11)
'@rush-temp/model-server-guest':
specifier: file:./projects/model-server-guest.tgz
version: file:projects/model-server-guest.tgz(svelte@4.2.11)
'@rush-temp/model-server-hr': '@rush-temp/model-server-hr':
specifier: file:./projects/model-server-hr.tgz specifier: file:./projects/model-server-hr.tgz
version: file:projects/model-server-hr.tgz(svelte@4.2.11) version: file:projects/model-server-hr.tgz(svelte@4.2.11)
@ -488,6 +503,12 @@ dependencies:
'@rush-temp/server-gmail-resources': '@rush-temp/server-gmail-resources':
specifier: file:./projects/server-gmail-resources.tgz specifier: file:./projects/server-gmail-resources.tgz
version: file:projects/server-gmail-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2) version: file:projects/server-gmail-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2)
'@rush-temp/server-guest':
specifier: file:./projects/server-guest.tgz
version: file:projects/server-guest.tgz(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2)
'@rush-temp/server-guest-resources':
specifier: file:./projects/server-guest-resources.tgz
version: file:projects/server-guest-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2)
'@rush-temp/server-hr': '@rush-temp/server-hr':
specifier: file:./projects/server-hr.tgz specifier: file:./projects/server-hr.tgz
version: file:projects/server-hr.tgz(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2) version: file:projects/server-hr.tgz(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2)
@ -18111,6 +18132,116 @@ packages:
- ts-node - ts-node
dev: false dev: false
file:projects/guest-assets.tgz(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2):
resolution: {integrity: sha512-RwM1NfgdyK2g9TTlQfMS4vTkMrwVzDCB7dtCuQDugHQLgLGb/Ea+Z9srtAHwY8ERKFZx6T/pKwNfODyacB1AzQ==, tarball: file:projects/guest-assets.tgz}
id: file:projects/guest-assets.tgz
name: '@rush-temp/guest-assets'
version: 0.0.0
dependencies:
'@types/jest': 29.5.12
'@types/node': 20.11.19
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
prettier: 3.2.5
prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11)
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- babel-jest
- babel-plugin-macros
- esbuild
- node-notifier
- supports-color
- svelte
- ts-node
dev: false
file:projects/guest-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
resolution: {integrity: sha512-7+lTvSCKLVvKBseFEtIJLuBf98DhhcXdu1bp7YxtmjO1nvZuPgWfw8aVdnuKLiZANpKhWxoYOS+yvpwYumML0Q==, tarball: file:projects/guest-resources.tgz}
id: file:projects/guest-resources.tgz
name: '@rush-temp/guest-resources'
version: 0.0.0
dependencies:
'@types/jest': 29.5.12
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.11)(ts-node@10.9.2)
fast-copy: 3.0.1
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
prettier: 3.2.5
prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11)
sass: 1.71.1
svelte: 4.2.11
svelte-check: 3.6.4(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.11)
svelte-eslint-parser: 0.33.1(svelte@4.2.11)
svelte-loader: 3.1.9(svelte@4.2.11)
svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.11)(typescript@5.3.3)
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- '@types/node'
- babel-jest
- babel-plugin-macros
- coffeescript
- esbuild
- less
- node-notifier
- postcss
- postcss-load-config
- pug
- stylus
- sugarss
- supports-color
- ts-node
dev: false
file:projects/guest.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2):
resolution: {integrity: sha512-4i+LNJNUK+8o49FVZYIQZSG6nxSPni42hV4fLWFhOhz6KHcDsWcKY7n8I3Mc87ZJ8e1YteSZn95ZFLPEkJYaIQ==, tarball: file:projects/guest.tgz}
id: file:projects/guest.tgz
name: '@rush-temp/guest'
version: 0.0.0
dependencies:
'@types/jest': 29.5.12
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
prettier: 3.2.5
prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11)
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- '@types/node'
- babel-jest
- babel-plugin-macros
- esbuild
- node-notifier
- supports-color
- svelte
- ts-node
dev: false
file:projects/hr-assets.tgz(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2): file:projects/hr-assets.tgz(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2):
resolution: {integrity: sha512-iySEToC2UKeQtUhHx21qbzPRz1G4UvCNhUuQtHS4uuzLVJ8CJkLv/l4EHkdXZr7y35lNiLqfkeeb+XO+3n0+Xg==, tarball: file:projects/hr-assets.tgz} resolution: {integrity: sha512-iySEToC2UKeQtUhHx21qbzPRz1G4UvCNhUuQtHS4uuzLVJ8CJkLv/l4EHkdXZr7y35lNiLqfkeeb+XO+3n0+Xg==, tarball: file:projects/hr-assets.tgz}
id: file:projects/hr-assets.tgz id: file:projects/hr-assets.tgz
@ -18764,7 +18895,7 @@ packages:
dev: false dev: false
file:projects/model-all.tgz(svelte@4.2.11): file:projects/model-all.tgz(svelte@4.2.11):
resolution: {integrity: sha512-oRRJx0V+rtUYxrRRDBQwb0ttCzClI/SVBXS57BFT2Qx1xYZ6fPSjZQWlca0KFztUqbg4qLnoib7Tq26otiPYEw==, tarball: file:projects/model-all.tgz} resolution: {integrity: sha512-dHsSkXsC7WkS9h8RlqjpSVF9TlqTA2UTiK5ehmxM3gJxpPFtrHrH8qD48u08TSh/nZJ2GlzinE5z6AvEuIHVsQ==, 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
@ -18894,7 +19025,7 @@ packages:
dev: false dev: false
file:projects/model-contact.tgz(svelte@4.2.11): file:projects/model-contact.tgz(svelte@4.2.11):
resolution: {integrity: sha512-1KCFugNTofKCnrBf9i+b5/XOflGD7esyG7mzy8YkX1n4AyI9YlFOLUuURGucEol5uKKU797NGRa2BO6iECAWbw==, tarball: file:projects/model-contact.tgz} resolution: {integrity: sha512-Rv8gLCvBayo+s//ldMnTatYDhJX6vXj46H9nE3L88EJK1m+9Y7dkYKNE7j6tmRNrfPIy+jHdLgumwXX313Ts/g==, tarball: file:projects/model-contact.tgz}
id: file:projects/model-contact.tgz id: file:projects/model-contact.tgz
name: '@rush-temp/model-contact' name: '@rush-temp/model-contact'
version: 0.0.0 version: 0.0.0
@ -18958,6 +19089,27 @@ packages:
- svelte - svelte
dev: false dev: false
file:projects/model-guest.tgz(svelte@4.2.11):
resolution: {integrity: sha512-AT15/UZNrG5UB+SntcmV9cyza8Fh/u50uw7xijag/O0J4cwzvdBO1fWAOhstQ0FmIVY+FUnttelz2cZNf1wYjg==, tarball: file:projects/model-guest.tgz}
id: file:projects/model-guest.tgz
name: '@rush-temp/model-guest'
version: 0.0.0
dependencies:
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
prettier: 3.2.5
prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11)
typescript: 5.3.3
transitivePeerDependencies:
- supports-color
- svelte
dev: false
file:projects/model-hr.tgz(svelte@4.2.11): file:projects/model-hr.tgz(svelte@4.2.11):
resolution: {integrity: sha512-m1fNr1rzBg+FqCPoeWyo1GE0BmA1TRTTgNe3s48jF0b16O0EVtlW6c24vaex1Xaf7rPyrFAYyoVd4XLMGr1Row==, tarball: file:projects/model-hr.tgz} resolution: {integrity: sha512-m1fNr1rzBg+FqCPoeWyo1GE0BmA1TRTTgNe3s48jF0b16O0EVtlW6c24vaex1Xaf7rPyrFAYyoVd4XLMGr1Row==, tarball: file:projects/model-hr.tgz}
id: file:projects/model-hr.tgz id: file:projects/model-hr.tgz
@ -19295,6 +19447,27 @@ packages:
- svelte - svelte
dev: false dev: false
file:projects/model-server-guest.tgz(svelte@4.2.11):
resolution: {integrity: sha512-akEz5ZppVM2lB6j5w2nZUoGsmluulr/MwMPlyNsxYNYC1tgqC+u14khPAOWkoRyjdsPGGHFdbyMRDEQNBE/OKg==, tarball: file:projects/model-server-guest.tgz}
id: file:projects/model-server-guest.tgz
name: '@rush-temp/model-server-guest'
version: 0.0.0
dependencies:
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
prettier: 3.2.5
prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11)
typescript: 5.3.3
transitivePeerDependencies:
- supports-color
- svelte
dev: false
file:projects/model-server-hr.tgz(svelte@4.2.11): file:projects/model-server-hr.tgz(svelte@4.2.11):
resolution: {integrity: sha512-M4L3fW3gYeKBdXk3PaHy4u1tuCq0AAFIe6F/SWSeZioefq0f6GW+nerxFmAO3l/leySffba+rdXNmmOAzXleNw==, tarball: file:projects/model-server-hr.tgz} resolution: {integrity: sha512-M4L3fW3gYeKBdXk3PaHy4u1tuCq0AAFIe6F/SWSeZioefq0f6GW+nerxFmAO3l/leySffba+rdXNmmOAzXleNw==, tarball: file:projects/model-server-hr.tgz}
id: file:projects/model-server-hr.tgz id: file:projects/model-server-hr.tgz
@ -19674,7 +19847,7 @@ packages:
dev: false dev: false
file:projects/model-task.tgz(svelte@4.2.11): file:projects/model-task.tgz(svelte@4.2.11):
resolution: {integrity: sha512-x9V62r2Vhq0crzWctRsKFFXj7hWW01o82UGgFYZMWDKmOK9rH+Ab4EBwjkxJRg1IDJdTlCOh6BaXP50FZ8KUhg==, tarball: file:projects/model-task.tgz} resolution: {integrity: sha512-dwcT+Z5GRNUUHfngIuBccTtt1yKE5ju1uOdAM2CcUAMWPRxtoh/ahFIYTPQMbIUIDkGqKA5F62c6C1+NTL1FhQ==, tarball: file:projects/model-task.tgz}
id: file:projects/model-task.tgz id: file:projects/model-task.tgz
name: '@rush-temp/model-task' name: '@rush-temp/model-task'
version: 0.0.0 version: 0.0.0
@ -20335,7 +20508,7 @@ packages:
dev: false dev: false
file:projects/pod-server.tgz(svelte@4.2.11): file:projects/pod-server.tgz(svelte@4.2.11):
resolution: {integrity: sha512-Vu7R/lyLRjCyqDTofzOdVCFk67PxBM5neEHd82HBLrNniUjVrmCfEFt4IBQOgpoTy1cM73swFSsl7UEyLA0VOQ==, tarball: file:projects/pod-server.tgz} resolution: {integrity: sha512-t0dTTvzR0GNGvvEi8f/KAk65SeP43eYlOH5Bh1ObHU0zb7NAH8idybiYypKKkpRrZHydTDL/C1iFrjXTrqqVKQ==, tarball: file:projects/pod-server.tgz}
id: file:projects/pod-server.tgz id: file:projects/pod-server.tgz
name: '@rush-temp/pod-server' name: '@rush-temp/pod-server'
version: 0.0.0 version: 0.0.0
@ -20482,7 +20655,7 @@ packages:
dev: false dev: false
file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2): file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2):
resolution: {integrity: sha512-wxoPP5P+qy7KnUurqydjBwLhbA7sDZ2DKpp0IZeHAUM1XVUyGG7+lmtH4D5s3iCVCwzvZ8qZks66ncp3ejqeFQ==, tarball: file:projects/prod.tgz} resolution: {integrity: sha512-sz8Xw/l82dtTDjeUD5fIOHpy9rjyTrbw1H7gh31h8nZxztMjPRZw9kRs9bzs9KidAPLnWEA/jL5T9j8QfaU01w==, 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
@ -21367,6 +21540,70 @@ packages:
- ts-node - ts-node
dev: false dev: false
file:projects/server-guest-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2):
resolution: {integrity: sha512-oxxjV/UtqYFgmLBSfc+vkVFUgLsaMFU/x77GCv8dEHlLlXJTUx5uqkhVB/xh6r3G3j0pT+XaUwhedEWqOWVb/A==, tarball: file:projects/server-guest-resources.tgz}
id: file:projects/server-guest-resources.tgz
name: '@rush-temp/server-guest-resources'
version: 0.0.0
dependencies:
'@types/jest': 29.5.12
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
prettier: 3.2.5
prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11)
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- '@types/node'
- babel-jest
- babel-plugin-macros
- esbuild
- node-notifier
- supports-color
- svelte
- ts-node
dev: false
file:projects/server-guest.tgz(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2):
resolution: {integrity: sha512-M1JF30TVm4MhIwj+KQme/VjmeoGGjUwhsntBcABceM+IewzJigjYhmVR4XTKURx9IbdHv1ibqqZhxjxkW/aFZg==, tarball: file:projects/server-guest.tgz}
id: file:projects/server-guest.tgz
name: '@rush-temp/server-guest'
version: 0.0.0
dependencies:
'@types/jest': 29.5.12
'@types/node': 20.11.19
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
prettier: 3.2.5
prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11)
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- babel-jest
- babel-plugin-macros
- esbuild
- node-notifier
- supports-color
- svelte
- ts-node
dev: false
file:projects/server-hr-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2): file:projects/server-hr-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.11)(ts-node@10.9.2):
resolution: {integrity: sha512-9ynW2uOReTvLE+Io8c0xqxZNaVh+BbgbRJdAV/Y3GptWFzoyizvF7tfpwJaa/KWz53rDcVRlrXtHg/izZS6g9A==, tarball: file:projects/server-hr-resources.tgz} resolution: {integrity: sha512-9ynW2uOReTvLE+Io8c0xqxZNaVh+BbgbRJdAV/Y3GptWFzoyizvF7tfpwJaa/KWz53rDcVRlrXtHg/izZS6g9A==, tarball: file:projects/server-hr-resources.tgz}
id: file:projects/server-hr-resources.tgz id: file:projects/server-hr-resources.tgz
@ -23281,7 +23518,7 @@ packages:
dev: false dev: false
file:projects/tool.tgz(bufferutil@4.0.8)(svelte@4.2.11): file:projects/tool.tgz(bufferutil@4.0.8)(svelte@4.2.11):
resolution: {integrity: sha512-7SvydeuLUJiHLfgw7qRAuhTOopzoN6XU4XlWoQoDi+YYE4fj+c6PCZPP3GVotIOXCC3uNZe4wrFyrGClRhU0IA==, tarball: file:projects/tool.tgz} resolution: {integrity: sha512-OrzOa2Flf5T2Yto4XM6PVEb19EWtTh3ektrC/qUedc4e4Fc/0Ihqqe7gyvD09KKfrczfagnn2061YZy9xFRh5w==, tarball: file:projects/tool.tgz}
id: file:projects/tool.tgz id: file:projects/tool.tgz
name: '@rush-temp/tool' name: '@rush-temp/tool'
version: 0.0.0 version: 0.0.0
@ -23568,7 +23805,7 @@ packages:
dev: false dev: false
file:projects/view-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2): file:projects/view-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
resolution: {integrity: sha512-+A80Ph9sdIfb1nBd6Th+OmqDik7Ky4ImRa+b9i1XPkBQEAM4Fv9m6A+Ebq9lIi6pjJmiF8f4v4S+mVaZr0o3Hg==, tarball: file:projects/view-resources.tgz} resolution: {integrity: sha512-SMUz1ewMVxZdhnE79sVLeW8cf5uNr+e1QtZDELM/JTTPLBy9IthoGoHCXf9NaWWHTVp2uEPDF4r74/dJw8Vmhg==, tarball: file:projects/view-resources.tgz}
id: file:projects/view-resources.tgz id: file:projects/view-resources.tgz
name: '@rush-temp/view-resources' name: '@rush-temp/view-resources'
version: 0.0.0 version: 0.0.0

View File

@ -175,6 +175,9 @@
"@hcengineering/server-view": "^0.6.0", "@hcengineering/server-view": "^0.6.0",
"@hcengineering/server-view-resources": "^0.6.0", "@hcengineering/server-view-resources": "^0.6.0",
"@hcengineering/server-activity": "^0.6.0", "@hcengineering/server-activity": "^0.6.0",
"@hcengineering/server-activity-resources": "^0.6.0" "@hcengineering/server-activity-resources": "^0.6.0",
"@hcengineering/guest": "^0.6.0",
"@hcengineering/guest-assets": "^0.6.0",
"@hcengineering/guest-resources": "^0.6.0"
} }
} }

View File

@ -23,6 +23,7 @@ import { chunterId } from '@hcengineering/chunter'
import client, { clientId } from '@hcengineering/client' import client, { clientId } from '@hcengineering/client'
import contactPlugin, { contactId } from '@hcengineering/contact' import contactPlugin, { contactId } from '@hcengineering/contact'
import gmail, { gmailId } from '@hcengineering/gmail' import gmail, { gmailId } from '@hcengineering/gmail'
import guest, { guestId } from '@hcengineering/guest'
import { hrId } from '@hcengineering/hr' import { hrId } from '@hcengineering/hr'
import { imageCropperId } from '@hcengineering/image-cropper' import { imageCropperId } from '@hcengineering/image-cropper'
import { inventoryId } from '@hcengineering/inventory' import { inventoryId } from '@hcengineering/inventory'
@ -53,6 +54,7 @@ import '@hcengineering/calendar-assets'
import '@hcengineering/chunter-assets' import '@hcengineering/chunter-assets'
import '@hcengineering/contact-assets' import '@hcengineering/contact-assets'
import '@hcengineering/gmail-assets' import '@hcengineering/gmail-assets'
import '@hcengineering/guest-assets'
import '@hcengineering/hr-assets' import '@hcengineering/hr-assets'
import '@hcengineering/inventory-assets' import '@hcengineering/inventory-assets'
import '@hcengineering/lead-assets' import '@hcengineering/lead-assets'
@ -76,9 +78,9 @@ import presentation, { presentationId } from '@hcengineering/presentation'
import textEditor, { textEditorId } from '@hcengineering/text-editor' import textEditor, { textEditorId } from '@hcengineering/text-editor'
import { setMetadata } from '@hcengineering/platform' import { setMetadata } from '@hcengineering/platform'
import { preferenceId } from '@hcengineering/preference'
import { setDefaultLanguage } from '@hcengineering/theme' import { setDefaultLanguage } from '@hcengineering/theme'
import { uiId } from '@hcengineering/ui/src/plugin' import { uiId } from '@hcengineering/ui/src/plugin'
import { preferenceId } from '@hcengineering/preference'
interface Config { interface Config {
ACCOUNTS_URL: string ACCOUNTS_URL: string
@ -130,6 +132,7 @@ function configureI18n(): void {
addStringsLoader(trackerId, async (lang: string) => await import(`@hcengineering/tracker-assets/lang/${lang}.json`)) addStringsLoader(trackerId, async (lang: string) => await import(`@hcengineering/tracker-assets/lang/${lang}.json`))
addStringsLoader(viewId, async (lang: string) => await import(`@hcengineering/view-assets/lang/${lang}.json`)) addStringsLoader(viewId, async (lang: string) => await import(`@hcengineering/view-assets/lang/${lang}.json`))
addStringsLoader(workbenchId, async (lang: string) => await import(`@hcengineering/workbench-assets/lang/${lang}.json`)) addStringsLoader(workbenchId, async (lang: string) => await import(`@hcengineering/workbench-assets/lang/${lang}.json`))
addStringsLoader(guestId, async (lang: string) => await import(`@hcengineering/guest-assets/lang/${lang}.json`))
} }
export async function configurePlatform() { export async function configurePlatform() {
@ -169,7 +172,8 @@ export async function configurePlatform() {
new Map([ new Map([
[workbenchId, workbench.component.WorkbenchApp], [workbenchId, workbench.component.WorkbenchApp],
[loginId, login.component.LoginApp], [loginId, login.component.LoginApp],
[calendarId, calendar.component.ConnectApp] [calendarId, calendar.component.ConnectApp],
[guestId, guest.component.GuestApp]
]) ])
) )
@ -204,6 +208,7 @@ export async function configurePlatform() {
addLocation(bitrixId, () => import(/* webpackChunkName: "bitrix" */ '@hcengineering/bitrix-resources')) addLocation(bitrixId, () => import(/* webpackChunkName: "bitrix" */ '@hcengineering/bitrix-resources'))
addLocation(requestId, () => import(/* webpackChunkName: "request" */ '@hcengineering/request-resources')) addLocation(requestId, () => import(/* webpackChunkName: "request" */ '@hcengineering/request-resources'))
addLocation(supportId, () => import(/* webpackChunkName: "support" */ '@hcengineering/support-resources')) addLocation(supportId, () => import(/* webpackChunkName: "support" */ '@hcengineering/support-resources'))
addLocation(guestId, () => import(/* webpackChunkName: "guest" */ '@hcengineering/guest-resources'))
setMetadata(client.metadata.FilterModel, true) setMetadata(client.metadata.FilterModel, true)
setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin]) setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin])

View File

@ -119,6 +119,8 @@
"@hcengineering/server-view-resources": "^0.6.0", "@hcengineering/server-view-resources": "^0.6.0",
"@hcengineering/server-activity": "^0.6.0", "@hcengineering/server-activity": "^0.6.0",
"@hcengineering/server-activity-resources": "^0.6.0", "@hcengineering/server-activity-resources": "^0.6.0",
"@hcengineering/server-guest": "^0.6.0",
"@hcengineering/server-guest-resources": "^0.6.0",
"@hcengineering/setting": "^0.6.11", "@hcengineering/setting": "^0.6.11",
"@hcengineering/tags": "^0.6.12", "@hcengineering/tags": "^0.6.12",
"@hcengineering/task": "^0.6.13", "@hcengineering/task": "^0.6.13",

View File

@ -41,6 +41,7 @@ import { serverTaskId } from '@hcengineering/server-task'
import { serverTelegramId } from '@hcengineering/server-telegram' import { serverTelegramId } from '@hcengineering/server-telegram'
import { serverTrackerId } from '@hcengineering/server-tracker' import { serverTrackerId } from '@hcengineering/server-tracker'
import { serverViewId } from '@hcengineering/server-view' import { serverViewId } from '@hcengineering/server-view'
import { serverGuestId } from '@hcengineering/server-guest'
addLocation(serverActivityId, () => import('@hcengineering/server-activity-resources')) addLocation(serverActivityId, () => import('@hcengineering/server-activity-resources'))
addLocation(serverAttachmentId, () => import('@hcengineering/server-attachment-resources')) addLocation(serverAttachmentId, () => import('@hcengineering/server-attachment-resources'))
@ -61,6 +62,7 @@ addLocation(serverTelegramId, () => import('@hcengineering/server-telegram-resou
addLocation(serverHrId, () => import('@hcengineering/server-hr-resources')) addLocation(serverHrId, () => import('@hcengineering/server-hr-resources'))
addLocation(serverRequestId, () => import('@hcengineering/server-request-resources')) addLocation(serverRequestId, () => import('@hcengineering/server-request-resources'))
addLocation(serverViewId, () => import('@hcengineering/server-view-resources')) addLocation(serverViewId, () => import('@hcengineering/server-view-resources'))
addLocation(serverGuestId, () => import('@hcengineering/server-guest-resources'))
function prepareTools (): { function prepareTools (): {
mongodbUri: string mongodbUri: string

View File

@ -48,7 +48,7 @@ import { type Db, MongoClient } from 'mongodb'
import { clearTelegramHistory } from './telegram' import { clearTelegramHistory } from './telegram'
import { diffWorkspace, updateField } from './workspace' import { diffWorkspace, updateField } from './workspace'
import { type Data, getWorkspaceId, RateLimiter, type Tx, type Version } from '@hcengineering/core' import { type Data, getWorkspaceId, RateLimiter, type Tx, type Version, type AccountRole } from '@hcengineering/core'
import { type MinioService } from '@hcengineering/minio' import { type MinioService } from '@hcengineering/minio'
import { consoleModelLogger, type MigrateOperation } from '@hcengineering/model' import { consoleModelLogger, type MigrateOperation } from '@hcengineering/model'
import { openAIConfigDefaults } from '@hcengineering/openai' import { openAIConfigDefaults } from '@hcengineering/openai'
@ -228,7 +228,7 @@ export function devTool (
program program
.command('set-user-role <email> <workspace> <role>') .command('set-user-role <email> <workspace> <role>')
.description('set user role') .description('set user role')
.action(async (email: string, workspace: string, role: number, cmd) => { .action(async (email: string, workspace: string, role: AccountRole, cmd) => {
console.log(`set user ${email} role for ${workspace}...`) console.log(`set user ${email} role for ${workspace}...`)
await setRole(email, workspace, productId, role) await setRole(email, workspace, productId, role)
}) })

View File

@ -83,6 +83,8 @@
"@hcengineering/model-server-activity": "^0.6.0", "@hcengineering/model-server-activity": "^0.6.0",
"@hcengineering/model-server-openai": "^0.6.0", "@hcengineering/model-server-openai": "^0.6.0",
"@hcengineering/model-server-translate": "^0.6.0", "@hcengineering/model-server-translate": "^0.6.0",
"@hcengineering/model-support": "^0.6.0" "@hcengineering/model-support": "^0.6.0",
"@hcengineering/model-guest": "^0.6.0",
"@hcengineering/model-server-guest": "^0.6.0"
} }
} }

View File

@ -67,6 +67,8 @@ import { textEditorId, createModel as textEditorModel } from '@hcengineering/mod
import tracker, { trackerId, createModel as trackerModel } from '@hcengineering/model-tracker' import tracker, { trackerId, createModel as trackerModel } from '@hcengineering/model-tracker'
import view, { viewId, createModel as viewModel } from '@hcengineering/model-view' import view, { viewId, createModel as viewModel } from '@hcengineering/model-view'
import workbench, { workbenchId, createModel as workbenchModel } from '@hcengineering/model-workbench' import workbench, { workbenchId, createModel as workbenchModel } from '@hcengineering/model-workbench'
import { guestId, createModel as guestModel } from '@hcengineering/model-guest'
import { serverGuestId, createModel as serverGuestModel } from '@hcengineering/model-server-guest'
import { openAIId, createModel as serverOpenAI } from '@hcengineering/model-server-openai' import { openAIId, createModel as serverOpenAI } from '@hcengineering/model-server-openai'
import { createModel as serverTranslate, translateId } from '@hcengineering/model-server-translate' import { createModel as serverTranslate, translateId } from '@hcengineering/model-server-translate'
@ -111,6 +113,7 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[
[coreModel, coreId], [coreModel, coreId],
[activityModel, activityId], [activityModel, activityId],
[attachmentModel, attachmentId], [attachmentModel, attachmentId],
[guestModel, guestId],
[tagsModel, tagsId], [tagsModel, tagsId],
[viewModel, viewId], [viewModel, viewId],
[workbenchModel, workbenchId], [workbenchModel, workbenchId],
@ -298,7 +301,8 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[
[serverViewModel, serverViewId], [serverViewModel, serverViewId],
[serverActivityModel, serverActivityId], [serverActivityModel, serverActivityId],
[serverTranslate, translateId], [serverTranslate, translateId],
[serverOpenAI, openAIId] [serverOpenAI, openAIId],
[serverGuestModel, serverGuestId]
] ]
for (const [b, id, config] of builders) { for (const [b, id, config] of builders) {

View File

@ -18,6 +18,7 @@ import { type MigrateOperation } from '@hcengineering/model'
import { attachmentOperation } from '@hcengineering/model-attachment' import { attachmentOperation } from '@hcengineering/model-attachment'
import { chunterOperation } from '@hcengineering/model-chunter' import { chunterOperation } from '@hcengineering/model-chunter'
import { contactOperation } from '@hcengineering/model-contact' import { contactOperation } from '@hcengineering/model-contact'
import { guestOperation } from '@hcengineering/model-guest'
import { coreOperation } from '@hcengineering/model-core' import { coreOperation } from '@hcengineering/model-core'
import { gmailOperation } from '@hcengineering/model-gmail' import { gmailOperation } from '@hcengineering/model-gmail'
import { leadOperation } from '@hcengineering/model-lead' import { leadOperation } from '@hcengineering/model-lead'
@ -54,6 +55,7 @@ export const migrateOperations: [string, MigrateOperation][] = [
['recruit', recruitOperation], ['recruit', recruitOperation],
['view', viewOperation], ['view', viewOperation],
['contact', contactOperation], ['contact', contactOperation],
['guest', guestOperation],
['tags', tagsOperation], ['tags', tagsOperation],
['setting', settingOperation], ['setting', settingOperation],
['tracker', trackerOperation], ['tracker', trackerOperation],

View File

@ -48,6 +48,7 @@
"@hcengineering/templates": "^0.6.7", "@hcengineering/templates": "^0.6.7",
"@hcengineering/ui": "^0.6.11", "@hcengineering/ui": "^0.6.11",
"@hcengineering/view": "^0.6.9", "@hcengineering/view": "^0.6.9",
"@hcengineering/model-guest": "^0.6.0",
"cross-fetch": "^3.1.5" "cross-fetch": "^3.1.5"
} }
} }

View File

@ -73,6 +73,7 @@ import templates from '@hcengineering/templates'
import { type AnyComponent } from '@hcengineering/ui/src/types' import { type AnyComponent } from '@hcengineering/ui/src/types'
import { type Action } from '@hcengineering/view' import { type Action } from '@hcengineering/view'
import contact from './plugin' import contact from './plugin'
import { createPublicLinkAction } from '@hcengineering/model-guest'
export { contactId } from '@hcengineering/contact' export { contactId } from '@hcengineering/contact'
export { contactOperation } from './migration' export { contactOperation } from './migration'
@ -1088,4 +1089,6 @@ export function createModel (builder: Builder): void {
[], [],
['comments', 'attachments'] ['comments', 'attachments']
) )
createPublicLinkAction(builder, contact.class.Contact, contact.action.PublicLink)
} }

View File

@ -123,7 +123,8 @@ export default mergeIds(contactId, contact, {
KickEmployee: '' as Ref<Action>, KickEmployee: '' as Ref<Action>,
DeleteEmployee: '' as Ref<Action>, DeleteEmployee: '' as Ref<Action>,
MergePersons: '' as Ref<Action<Doc, any>>, MergePersons: '' as Ref<Action<Doc, any>>,
OpenChannel: '' as Ref<Action> OpenChannel: '' as Ref<Action>,
PublicLink: '' as Ref<Action<Doc, any>>
}, },
actionImpl: { actionImpl: {
KickEmployee: '' as ViewAction, KickEmployee: '' as ViewAction,

View File

@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}

4
models/guest/.npmignore Normal file
View File

@ -0,0 +1,4 @@
*
!/lib/**
!CHANGELOG.md
/lib/**/__tests__/

View File

@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "model"
}

42
models/guest/package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "@hcengineering/model-guest",
"version": "0.6.0",
"main": "lib/index.js",
"author": "Anticrm Platform Contributors",
"svelte": "src/index.ts",
"types": "types/index.d.ts",
"template": "@hcengineering/model-package",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"build:watch": "compile",
"format": "format src",
"_phase:build": "compile transpile src",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"prettier": "^3.1.0",
"prettier-plugin-svelte": "^3.1.0",
"typescript": "^5.3.3"
},
"dependencies": {
"@hcengineering/core": "^0.6.28",
"@hcengineering/model": "^0.6.7",
"@hcengineering/model-core": "^0.6.0",
"@hcengineering/model-view": "^0.6.0",
"@hcengineering/guest": "^0.6.0",
"@hcengineering/guest-resources": "^0.6.0",
"@hcengineering/platform": "^0.6.9",
"@hcengineering/ui": "^0.6.11",
"@hcengineering/view": "^0.6.9"
}
}

35
models/guest/src/index.ts Normal file
View File

@ -0,0 +1,35 @@
import { AccountRole, type Doc, type Domain, type Ref } from '@hcengineering/core'
import { type PublicLink, type Restrictions, guestAccountEmail } from '@hcengineering/guest'
import { type Builder, Model } from '@hcengineering/model'
import core, { TDoc } from '@hcengineering/model-core'
import { type Location } from '@hcengineering/ui'
import guest from './plugin'
const GUEST_DOMAIN = 'guest' as Domain
@Model(guest.class.PublicLink, core.class.Doc, GUEST_DOMAIN)
export class TPublicLink extends TDoc implements PublicLink {
url!: string
location!: Location
restrictions!: Restrictions
revokable!: boolean
attachedTo!: Ref<Doc>
}
export function createModel (builder: Builder): void {
builder.createModel(TPublicLink)
builder.createDoc(
core.class.Account,
core.space.Model,
{
email: guestAccountEmail,
role: AccountRole.Guest
},
guest.account.Guest
)
}
export { guestId } from '@hcengineering/guest'
export * from './migration'
export * from './utils'

View File

@ -0,0 +1,97 @@
import {
type Account,
AccountRole,
DOMAIN_TX,
type TxCreateDoc,
TxOperations,
type TxUpdateDoc
} from '@hcengineering/core'
import { guestId } from '@hcengineering/guest'
import {
type MigrateOperation,
type MigrationClient,
type MigrationUpgradeClient,
type ModelLogger,
tryMigrate
} from '@hcengineering/model'
import core from '@hcengineering/model-core'
import guest from './plugin'
async function createSpace (tx: TxOperations): Promise<void> {
const current = await tx.findOne(core.class.Space, {
_id: guest.space.Links
})
if (current === undefined) {
await tx.createDoc(
core.class.Space,
core.space.Space,
{
name: 'Links',
description: 'Space for all guest links',
private: false,
archived: false,
members: []
},
guest.space.Links
)
}
}
async function createDefaults (client: MigrationUpgradeClient): Promise<void> {
const txOp = new TxOperations(client, core.account.System)
await createSpace(txOp)
}
export const guestOperation: MigrateOperation = {
async migrate (client: MigrationClient, logger: ModelLogger): Promise<void> {
await tryMigrate(client, guestId, [
{
state: 'migrateRoles',
func: async (client) => {
const stateMap = {
0: AccountRole.User,
1: AccountRole.Maintainer,
2: AccountRole.Owner
}
const createTxes = await client.find<TxCreateDoc<Account>>(DOMAIN_TX, {
_class: core.class.TxCreateDoc,
'attributes.role': { $in: [0, 1, 2] }
})
for (const tx of createTxes) {
await client.update(
DOMAIN_TX,
{
_id: tx._id
},
{
$set: {
'attributes.role': (stateMap as any)[tx.attributes.role]
}
}
)
}
const updateTxes = await client.find<TxUpdateDoc<Account>>(DOMAIN_TX, {
_class: core.class.TxUpdateDoc,
'operations.role': { $in: [0, 1, 2] }
})
for (const tx of updateTxes) {
await client.update(
DOMAIN_TX,
{
_id: tx._id
},
{
$set: {
'operations.role': (stateMap as any)[(tx.operations as any).role]
}
}
)
}
}
}
])
},
async upgrade (client: MigrationUpgradeClient): Promise<void> {
await createDefaults(client)
}
}

View File

@ -0,0 +1,21 @@
import { type Account, type Doc, type Ref } from '@hcengineering/core'
import { guestId } from '@hcengineering/guest'
import guest from '@hcengineering/guest-resources/src/plugin'
import { mergeIds } from '@hcengineering/platform'
import { type AnyComponent } from '@hcengineering/ui'
import { type Action, type ActionCategory } from '@hcengineering/view'
export default mergeIds(guestId, guest, {
account: {
Guest: '' as Ref<Account>
},
action: {
CreatePublicLink: '' as Ref<Action<Doc, any>>
},
category: {
Guest: '' as Ref<ActionCategory>
},
component: {
CreatePublicLink: '' as AnyComponent
}
})

31
models/guest/src/utils.ts Normal file
View File

@ -0,0 +1,31 @@
import { type Class, type Doc, type Ref } from '@hcengineering/core'
import { type Builder } from '@hcengineering/model'
import view, { createAction } from '@hcengineering/model-view'
import { type Action } from '@hcengineering/view'
import guest from './plugin'
export function createPublicLinkAction (builder: Builder, _class: Ref<Class<Doc>>, _id: Ref<Action<Doc, any>>): void {
createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: guest.component.CreatePublicLink,
element: 'top',
fillProps: {
_objects: 'value'
}
},
label: guest.string.PublicLink,
icon: guest.icon.Link,
keyBinding: [],
input: 'any',
category: guest.category.Guest,
target: _class,
context: {
mode: ['context', 'browser']
}
},
_id
)
}

View File

@ -0,0 +1,9 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"tsBuildInfoFile": ".build/build.tsbuildinfo"
}
}

View File

@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}

View File

@ -0,0 +1,4 @@
*
!/lib/**
!CHANGELOG.md
/lib/**/__tests__/

View File

@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "model"
}

View File

@ -0,0 +1,39 @@
{
"name": "@hcengineering/model-server-guest",
"version": "0.6.0",
"main": "lib/index.js",
"svelte": "src/index.ts",
"types": "types/index.d.ts",
"author": "Anticrm Platform Contributors",
"template": "@hcengineering/model-package",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"build:watch": "compile",
"format": "format src",
"_phase:build": "compile transpile src",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"prettier": "^3.1.0",
"prettier-plugin-svelte": "^3.1.0",
"typescript": "^5.3.3"
},
"dependencies": {
"@hcengineering/core": "^0.6.28",
"@hcengineering/model": "^0.6.7",
"@hcengineering/platform": "^0.6.9",
"@hcengineering/guest": "^0.6.0",
"@hcengineering/server-guest": "^0.6.0",
"@hcengineering/server-core": "^0.6.1"
}
}

View File

@ -0,0 +1,34 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { type Builder } from '@hcengineering/model'
import core from '@hcengineering/core'
import guest from '@hcengineering/guest'
import serverCore from '@hcengineering/server-core'
import serverGuest from '@hcengineering/server-guest'
export { serverGuestId } from '@hcengineering/server-guest'
export function createModel (builder: Builder): void {
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverGuest.trigger.OnPublicLinkCreate,
txMatch: {
_class: core.class.TxCreateDoc,
objectClass: guest.class.PublicLink,
'attributes.url': ''
}
})
}

View File

@ -0,0 +1,9 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"tsBuildInfoFile": ".build/build.tsbuildinfo"
}
}

View File

@ -38,6 +38,7 @@
"@hcengineering/model-core": "^0.6.0", "@hcengineering/model-core": "^0.6.0",
"@hcengineering/model-presentation": "^0.6.0", "@hcengineering/model-presentation": "^0.6.0",
"@hcengineering/model-tags": "^0.6.0", "@hcengineering/model-tags": "^0.6.0",
"@hcengineering/model-guest": "^0.6.0",
"@hcengineering/model-view": "^0.6.0", "@hcengineering/model-view": "^0.6.0",
"@hcengineering/model-workbench": "^0.6.1", "@hcengineering/model-workbench": "^0.6.1",
"@hcengineering/platform": "^0.6.9", "@hcengineering/platform": "^0.6.9",

View File

@ -83,6 +83,7 @@ import {
import type { AnyComponent } from '@hcengineering/ui' import type { AnyComponent } from '@hcengineering/ui'
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors' import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
import { type ViewAction } from '@hcengineering/view' import { type ViewAction } from '@hcengineering/view'
import { createPublicLinkAction } from '@hcengineering/model-guest'
import task from './plugin' import task from './plugin'
@ -557,6 +558,8 @@ export function createModel (builder: Builder): void {
}, },
task.ids.ManageProjects task.ids.ManageProjects
) )
createPublicLinkAction(builder, task.class.Task, task.action.PublicLink)
} }
/** /**

View File

@ -15,7 +15,7 @@
// //
import {} from '@hcengineering/notification' import {} from '@hcengineering/notification'
import type { Ref, Space } from '@hcengineering/core' import type { Doc, Ref, Space } from '@hcengineering/core'
import { mergeIds, type IntlString } from '@hcengineering/platform' import { mergeIds, type IntlString } from '@hcengineering/platform'
import { type TagCategory } from '@hcengineering/tags' import { type TagCategory } from '@hcengineering/tags'
import { taskId } from '@hcengineering/task' import { taskId } from '@hcengineering/task'
@ -28,7 +28,8 @@ export default mergeIds(taskId, task, {
EditStatuses: '' as Ref<Action>, EditStatuses: '' as Ref<Action>,
ArchiveSpace: '' as Ref<Action>, ArchiveSpace: '' as Ref<Action>,
UnarchiveSpace: '' as Ref<Action>, UnarchiveSpace: '' as Ref<Action>,
ArchiveState: '' as Ref<Action> ArchiveState: '' as Ref<Action>,
PublicLink: '' as Ref<Action<Doc, any>>
}, },
actionImpl: { actionImpl: {
EditStatuses: '' as ViewAction, EditStatuses: '' as ViewAction,

View File

@ -233,7 +233,6 @@ export class TIssue extends TTask implements Issue {
estimation!: number estimation!: number
@Prop(TypeReportedTime(), tracker.string.ReportedTime) @Prop(TypeReportedTime(), tracker.string.ReportedTime)
@ReadOnly()
reportedTime!: number reportedTime!: number
@Prop(TypeRemainingTime(), tracker.string.RemainingTime) @Prop(TypeRemainingTime(), tracker.string.RemainingTime)

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
// //
import { Client } from '..' import { AccountRole, Client } from '..'
import type { Class, Doc, Obj, Ref } from '../classes' import type { Class, Doc, Obj, Ref } from '../classes'
import core from '../component' import core from '../component'
import { Hierarchy } from '../hierarchy' import { Hierarchy } from '../hierarchy'
@ -250,7 +250,7 @@ describe('memdb', () => {
}) })
const account = await model.createDoc(core.class.Account, core.space.Model, { const account = await model.createDoc(core.class.Account, core.space.Model, {
email: 'email', email: 'email',
role: 0 role: AccountRole.User
}) })
await model.updateDoc(core.class.Space, core.space.Model, space, { $push: { members: account } }) await model.updateDoc(core.class.Space, core.space.Model, space, { $push: { members: account } })
const txSpace = await model.findAll(core.class.Space, { _id: space }) const txSpace = await model.findAll(core.class.Space, { _id: space })

View File

@ -366,9 +366,20 @@ export interface Account extends Doc {
* @public * @public
*/ */
export enum AccountRole { export enum AccountRole {
User, Guest = 'GUEST',
Maintainer, User = 'USER',
Owner Maintainer = 'MAINTAINER',
Owner = 'OWNER'
}
/**
* @public
*/
export const roleOrder: Record<AccountRole, number> = {
[AccountRole.Guest]: 1,
[AccountRole.User]: 2,
[AccountRole.Maintainer]: 3,
[AccountRole.Owner]: 4
} }
/** /**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View File

@ -48,8 +48,10 @@
} }
} }
$: isReadonly = (attribute.readonly ?? false) || !editable
function onChange (value: any): void { function onChange (value: any): void {
if (!editable) return if (isReadonly) return
const doc = object as Doc const doc = object as Doc
void updateAttribute(client, doc, _class, { key: attributeKey, attr: attribute }, value) void updateAttribute(client, doc, _class, { key: attributeKey, attr: attribute }, value)
} }
@ -64,6 +66,9 @@
label={attribute?.label} label={attribute?.label}
placeholder={attribute?.label} placeholder={attribute?.label}
type={attribute?.type} type={attribute?.type}
readonly={isReadonly}
editable={!isReadonly}
disabled={isReadonly}
{maxWidth} {maxWidth}
{attributeKey} {attributeKey}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })} value={getAttribute(client, object, { key: attributeKey, attr: attribute })}

View File

@ -98,7 +98,7 @@
class:is-dragged-over-down={draggingIndex !== null && index > draggingIndex && index === hoveringIndex} class:is-dragged-over-down={draggingIndex !== null && index > draggingIndex && index === hoveringIndex}
class:drag-over-highlight={index === dragOverIndex} class:drag-over-highlight={index === dragOverIndex}
draggable={editable} draggable={editable}
on:contextmenu|preventDefault={(ev) => checkIsNotDraft(object) && showContextMenu?.(ev, object)} on:contextmenu={(ev) => checkIsNotDraft(object) && showContextMenu?.(ev, object)}
on:dragstart={(ev) => { on:dragstart={(ev) => {
handleDragStart(ev, index) handleDragStart(ev, index)
}} }}

View File

@ -59,6 +59,7 @@
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<span <span
class:cursor-pointer={!disabled} class:cursor-pointer={!disabled}
class:cursor-default={disabled}
class:noUnderline={noUnderline || disabled} class:noUnderline={noUnderline || disabled}
class:noOverflow class:noOverflow
class:inline class:inline

View File

@ -20,6 +20,7 @@
import { hasComponent } from './utils' import { hasComponent } from './utils'
export let models: readonly BreadcrumbsModel[] export let models: readonly BreadcrumbsModel[]
export let disabled: boolean = false
$: trimmed = models.length > 3 $: trimmed = models.length > 3
$: narrowModel = trimmed ? [models[0], models[models.length - 1]] : models $: narrowModel = trimmed ? [models[0], models[models.length - 1]] : models
@ -51,7 +52,7 @@
</div> </div>
{:else} {:else}
{@const { title, href, onClick } = model} {@const { title, href, onClick } = model}
<NavLink {href} noUnderline {onClick}> <NavLink {href} noUnderline {onClick} {disabled}>
<div class="title">{title}</div> <div class="title">{title}</div>
</NavLink> </NavLink>
{/if} {/if}

View File

@ -14,7 +14,7 @@
// //
import type { Account, Arr, Class, Data, Doc, Domain, Mixin, Obj, Ref, TxCreateDoc, TxCUD } from '@hcengineering/core' import type { Account, Arr, Class, Data, Doc, Domain, Mixin, Obj, Ref, TxCreateDoc, TxCUD } from '@hcengineering/core'
import core, { AttachedDoc, ClassifierKind, DOMAIN_MODEL, DOMAIN_TX, TxFactory } from '@hcengineering/core' import core, { AccountRole, AttachedDoc, ClassifierKind, DOMAIN_MODEL, DOMAIN_TX, TxFactory } from '@hcengineering/core'
import type { IntlString, Plugin } from '@hcengineering/platform' import type { IntlString, Plugin } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform'
@ -194,8 +194,8 @@ export function genMinModel (): TxCUD<Doc>[] {
const u1 = 'User1' as Ref<Account> const u1 = 'User1' as Ref<Account>
const u2 = 'User2' as Ref<Account> const u2 = 'User2' as Ref<Account>
txes.push( txes.push(
createDoc(core.class.Account, { email: 'user1@site.com', role: 0 }, u1), createDoc(core.class.Account, { email: 'user1@site.com', role: AccountRole.User }, u1),
createDoc(core.class.Account, { email: 'user2@site.com', role: 0 }, u2), createDoc(core.class.Account, { email: 'user2@site.com', role: AccountRole.User }, u2),
createDoc(core.class.Space, { createDoc(core.class.Space, {
name: 'Sp1', name: 'Sp1',
description: '', description: '',

View File

@ -14,6 +14,7 @@
// //
import core, { import core, {
AccountRole,
createClient, createClient,
Doc, Doc,
generateId, generateId,
@ -105,7 +106,7 @@ describe('query', () => {
await factory.createDoc(core.class.Account, core.space.Model, { await factory.createDoc(core.class.Account, core.space.Model, {
email: 'user1@site.com', email: 'user1@site.com',
role: 0 role: AccountRole.User
}) })
await factory.createDoc<Channel>(core.class.Space, core.space.Model, { await factory.createDoc<Channel>(core.class.Space, core.space.Model, {
private: true, private: true,

View File

@ -27,7 +27,7 @@
export let object: Doc export let object: Doc
export let key: KeyedAttribute export let key: KeyedAttribute
export let readonly = false
export let textNodeActions: TextNodeAction[] = [] export let textNodeActions: TextNodeAction[] = []
export let refActions: RefAction[] = [] export let refActions: RefAction[] = []
@ -118,6 +118,7 @@
{attachFile} {attachFile}
{placeholder} {placeholder}
{boundary} {boundary}
{readonly}
field={key.key} field={key.key}
on:focus on:focus
on:blur on:blur

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -14,7 +14,8 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { Asset, IntlString } from '@hcengineering/platform' import type { Asset, IntlString } from '@hcengineering/platform'
import { onMount, ComponentType, createEventDispatcher } from 'svelte' import { ComponentType, onMount } from 'svelte'
import { checkAdaptiveMatching, deviceOptionsStore as deviceInfo } from '..'
import { registerFocus } from '../focus' import { registerFocus } from '../focus'
import { tooltip } from '../tooltips' import { tooltip } from '../tooltips'
import type { import type {
@ -22,11 +23,10 @@
ButtonKind, ButtonKind,
ButtonShape, ButtonShape,
ButtonSize, ButtonSize,
LabelAndProps,
IconProps, IconProps,
LabelAndProps,
WidthType WidthType
} from '../types' } from '../types'
import { checkAdaptiveMatching, deviceOptionsStore as deviceInfo } from '..'
import Icon from './Icon.svelte' import Icon from './Icon.svelte'
import Label from './Label.svelte' import Label from './Label.svelte'
import Spinner from './Spinner.svelte' import Spinner from './Spinner.svelte'

View File

@ -25,6 +25,8 @@
export let contentPanel: HTMLElement export let contentPanel: HTMLElement
export let kind: 'default' | 'modern' = 'default' export let kind: 'default' | 'modern' = 'default'
export let embedded: boolean = false
export let readonly: boolean = false
let modalHTML: HTMLElement let modalHTML: HTMLElement
let componentInstance: any let componentInstance: any
@ -164,6 +166,8 @@
rightSection={props.rightSection} rightSection={props.rightSection}
position={props.element} position={props.element}
{kind} {kind}
{readonly}
{embedded}
bind:popupOptions={options} bind:popupOptions={options}
on:open={_open} on:open={_open}
on:close={_close} on:close={_close}

View File

@ -13,17 +13,16 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from 'svelte' import activity, { ActivityMessage, ActivityMessageExtension } from '@hcengineering/activity'
import { Action, IconMoreV, showPopup } from '@hcengineering/ui' import { Action, IconMoreV, showPopup } from '@hcengineering/ui'
import { Menu } from '@hcengineering/view-resources' import { Menu } from '@hcengineering/view-resources'
import activity, { ActivityMessage, ActivityMessageExtension } from '@hcengineering/activity' import { createEventDispatcher } from 'svelte'
import AddReactionAction from './reactions/AddReactionAction.svelte'
import SaveMessageAction from './SaveMessageAction.svelte'
import PinMessageAction from './PinMessageAction.svelte'
import ActivityMessageExtensionComponent from './activity-message/ActivityMessageExtension.svelte'
import ActivityMessageAction from './ActivityMessageAction.svelte' import ActivityMessageAction from './ActivityMessageAction.svelte'
import { isReactionMessage } from '../activityMessagesUtils' import PinMessageAction from './PinMessageAction.svelte'
import SaveMessageAction from './SaveMessageAction.svelte'
import ActivityMessageExtensionComponent from './activity-message/ActivityMessageExtension.svelte'
import AddReactionAction from './reactions/AddReactionAction.svelte'
export let message: ActivityMessage | undefined export let message: ActivityMessage | undefined
export let extensions: ActivityMessageExtension[] = [] export let extensions: ActivityMessageExtension[] = []

View File

@ -23,7 +23,7 @@
import core, { getDisplayTime } from '@hcengineering/core' import core, { getDisplayTime } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
import { Action, Label } from '@hcengineering/ui' import { Action, Label } from '@hcengineering/ui'
import { getActions } from '@hcengineering/view-resources' import { getActions, restrictionStore } from '@hcengineering/view-resources'
import ReactionsPresenter from '../reactions/ReactionsPresenter.svelte' import ReactionsPresenter from '../reactions/ReactionsPresenter.svelte'
import ActivityMessageExtensionComponent from './ActivityMessageExtension.svelte' import ActivityMessageExtensionComponent from './ActivityMessageExtension.svelte'
@ -104,6 +104,9 @@
$: isHidden = !!viewlet?.onlyWithParent && parentMessage === undefined $: isHidden = !!viewlet?.onlyWithParent && parentMessage === undefined
$: withActionMenu = $: withActionMenu =
withActions && !embedded && (actions.length > 0 || allActionIds.some((id) => !excludedActions.includes(id))) withActions && !embedded && (actions.length > 0 || allActionIds.some((id) => !excludedActions.includes(id)))
let readonly: boolean = false
$: readonly = $restrictionStore.disableComments
</script> </script>
{#if !isHidden} {#if !isHidden}
@ -170,14 +173,14 @@
props={{ object: message, embedded, onReply }} props={{ object: message, embedded, onReply }}
/> />
{/if} {/if}
<ReactionsPresenter object={message} /> <ReactionsPresenter object={message} {readonly} />
{#if parentMessage && showEmbedded} {#if parentMessage && showEmbedded}
<div class="mt-2" /> <div class="mt-2" />
<ActivityMessagePresenter value={parentMessage} embedded hideFooter withActions={false} /> <ActivityMessagePresenter value={parentMessage} embedded hideFooter withActions={false} />
{/if} {/if}
</div> </div>
{#if withActions} {#if withActions && !readonly}
<div class="actions" class:opened={isActionsOpened}> <div class="actions" class:opened={isActionsOpened}>
<ActivityMessageActions <ActivityMessageActions
message={isReactionMessage(message) ? parentMessage : message} message={isReactionMessage(message) ? parentMessage : message}

View File

@ -24,6 +24,7 @@
export let reactions: Reaction[] = [] export let reactions: Reaction[] = []
export let object: Doc | undefined = undefined export let object: Doc | undefined = undefined
export let readonly: boolean = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const client = getClient() const client = getClient()
@ -39,6 +40,7 @@
reactionsAccounts = reactionsAccounts reactionsAccounts = reactionsAccounts
} }
function getClickHandler (emoji: string) { function getClickHandler (emoji: string) {
if (readonly) return
return (e: CustomEvent) => { return (e: CustomEvent) => {
e.stopPropagation() e.stopPropagation()
e.preventDefault() e.preventDefault()
@ -47,6 +49,7 @@
} }
function openEmojiPalette (ev: Event) { function openEmojiPalette (ev: Event) {
if (readonly) return
ev.preventDefault() ev.preventDefault()
ev.stopPropagation() ev.stopPropagation()
showPopup(EmojiPopup, {}, ev.target as HTMLElement, (emoji: string) => { showPopup(EmojiPopup, {}, ev.target as HTMLElement, (emoji: string) => {
@ -61,6 +64,7 @@
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
class="item border-radius-1" class="item border-radius-1"
class:cursor-pointer={!readonly}
use:tooltip={{ component: ReactionsTooltip, props: { reactionAccounts: accounts } }} use:tooltip={{ component: ReactionsTooltip, props: { reactionAccounts: accounts } }}
on:click={getClickHandler(emoji)} on:click={getClickHandler(emoji)}
> >
@ -70,7 +74,7 @@
</div> </div>
</div> </div>
{/each} {/each}
{#if object && reactionsAccounts.size > 0} {#if object && reactionsAccounts.size > 0 && !readonly}
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="item flex-row-center border-radius-1" class:withoutBackground={true} on:click={openEmojiPalette}> <div class="item flex-row-center border-radius-1" class:withoutBackground={true} on:click={openEmojiPalette}>
@ -102,7 +106,6 @@
height: 1.5rem; height: 1.5rem;
background: var(--secondary-button-disabled); background: var(--secondary-button-disabled);
border: none; border: none;
cursor: pointer;
&:hover { &:hover {
border: 1px solid var(--theme-darker-color); border: 1px solid var(--theme-darker-color);

View File

@ -13,13 +13,14 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { createQuery, getClient } from '@hcengineering/presentation'
import activity, { ActivityMessage, Reaction } from '@hcengineering/activity' import activity, { ActivityMessage, Reaction } from '@hcengineering/activity'
import { createQuery, getClient } from '@hcengineering/presentation'
import Reactions from './Reactions.svelte'
import { updateDocReactions } from '../../utils' import { updateDocReactions } from '../../utils'
import Reactions from './Reactions.svelte'
export let object: ActivityMessage | undefined export let object: ActivityMessage | undefined
export let readonly = false
const client = getClient() const client = getClient()
const reactionsQuery = createQuery() const reactionsQuery = createQuery()
@ -35,12 +36,13 @@
} }
const handleClick = (ev: CustomEvent) => { const handleClick = (ev: CustomEvent) => {
if (readonly) return
updateDocReactions(client, reactions, object, ev.detail) updateDocReactions(client, reactions, object, ev.detail)
} }
</script> </script>
{#if object && hasReactions} {#if object && hasReactions}
<div class="footer flex-col p-inline contrast mt-2 min-h-6"> <div class="footer flex-col p-inline contrast mt-2 min-h-6">
<Reactions {reactions} {object} on:click={handleClick} /> <Reactions {reactions} {object} {readonly} on:click={handleClick} />
</div> </div>
{/if} {/if}

View File

@ -16,7 +16,7 @@
import type { Attachment } from '@hcengineering/attachment' import type { Attachment } from '@hcengineering/attachment'
import { getResource } from '@hcengineering/platform' import { getResource } from '@hcengineering/platform'
import { PDFViewer, getFileUrl } from '@hcengineering/presentation' import { PDFViewer, getFileUrl } from '@hcengineering/presentation'
import { ActionIcon, IconMoreH, IconOpen, Menu, closeTooltip, showPopup } from '@hcengineering/ui' import { Action as UIAction, ActionIcon, IconMoreH, IconOpen, Menu, closeTooltip, showPopup } from '@hcengineering/ui'
import view, { Action } from '@hcengineering/view' import view, { Action } from '@hcengineering/view'
import attachmentPlugin from '../plugin' import attachmentPlugin from '../plugin'
@ -24,6 +24,7 @@
export let attachment: Attachment export let attachment: Attachment
export let isSaved = false export let isSaved = false
export let removable = false
let download: HTMLAnchorElement let download: HTMLAnchorElement
@ -56,38 +57,40 @@
action: attachmentPlugin.actionImpl.AddAttachmentToSaved action: attachmentPlugin.actionImpl.AddAttachmentToSaved
} as unknown as Action) } as unknown as Action)
const openAction: UIAction = {
label: view.string.Open,
icon: IconOpen,
action: async (props: any, evt: Event) => {
showPreview(evt as MouseEvent)
}
}
const showMenu = (ev: Event) => { const showMenu = (ev: Event) => {
const actions: UIAction[] = []
if (openable) {
actions.push(openAction)
}
actions.push({
label: saveAttachmentAction.label,
icon: saveAttachmentAction.icon,
action: async (props: any, evt: Event) => {
const impl = await getResource(saveAttachmentAction.action)
await impl(attachment, evt)
}
})
if (removable) {
actions.push({
label: attachmentPlugin.string.DeleteFile,
action: async (props: any, evt: Event) => {
const impl = await getResource(attachmentPlugin.actionImpl.DeleteAttachment)
await impl(attachment, evt)
}
})
}
showPopup( showPopup(
Menu, Menu,
{ {
actions: [ actions
...(openable
? [
{
label: view.string.Open,
icon: IconOpen,
action: async (props: any, evt: MouseEvent) => {
showPreview(evt)
}
}
]
: []),
{
label: saveAttachmentAction.label,
icon: saveAttachmentAction.icon,
action: async (props: any, evt: MouseEvent) => {
const impl = await getResource(saveAttachmentAction.action)
await impl(attachment, evt)
}
},
{
label: attachmentPlugin.string.DeleteFile,
action: async (props: any, evt: MouseEvent) => {
const impl = await getResource(attachmentPlugin.actionImpl.DeleteAttachment)
await impl(attachment, evt)
}
}
]
}, },
ev.target as HTMLElement ev.target as HTMLElement
) )

View File

@ -27,6 +27,7 @@
export let value: Attachment export let value: Attachment
export let isSaved: boolean = false export let isSaved: boolean = false
export let listProvider: ListSelectionProvider | undefined = undefined export let listProvider: ListSelectionProvider | undefined = undefined
export let removable: boolean = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: type = getType(value.type) $: type = getType(value.type)
@ -58,14 +59,14 @@
alt={value.name} alt={value.name}
/> />
<div class="actions conner"> <div class="actions conner">
<AttachmentActions attachment={value} {isSaved} /> <AttachmentActions attachment={value} {isSaved} {removable} />
</div> </div>
</div> </div>
{:else if type === 'audio'} {:else if type === 'audio'}
<div class="buttonContainer"> <div class="buttonContainer">
<AudioPlayer {value} /> <AudioPlayer {value} />
<div class="actions conner" style:padding={'0.125rem 0.25rem'}> <div class="actions conner" style:padding={'0.125rem 0.25rem'}>
<AttachmentActions attachment={value} {isSaved} /> <AttachmentActions attachment={value} {isSaved} {removable} />
</div> </div>
</div> </div>
{:else if type === 'video'} {:else if type === 'video'}
@ -78,14 +79,14 @@
</div> </div>
</video> </video>
<div class="actions conner"> <div class="actions conner">
<AttachmentActions attachment={value} {isSaved} /> <AttachmentActions attachment={value} {isSaved} {removable} />
</div> </div>
</div> </div>
{:else} {:else}
<div class="flex buttonContainer extraWidth"> <div class="flex buttonContainer extraWidth">
<AttachmentPresenter {value} /> <AttachmentPresenter {value} />
<div class="actions conner"> <div class="actions conner">
<AttachmentActions attachment={value} {isSaved} /> <AttachmentActions attachment={value} {isSaved} {removable} />
</div> </div>
</div> </div>
{/if} {/if}

View File

@ -34,6 +34,7 @@
export let enableAttachments: boolean = true export let enableAttachments: boolean = true
export let useAttachmentPreview: boolean = false export let useAttachmentPreview: boolean = false
export let readonly: boolean = false
const client = getClient() const client = getClient()
@ -43,7 +44,7 @@
let extraActions: RefAction[] = [] let extraActions: RefAction[] = []
let modelRefActions: RefAction[] = [] let modelRefActions: RefAction[] = []
$: if (enableAttachments) { $: if (enableAttachments && !readonly) {
extraActions = [ extraActions = [
{ {
label: textEditor.string.Attach, label: textEditor.string.Attach,
@ -59,10 +60,12 @@
void getModelRefActions().then((actions) => { void getModelRefActions().then((actions) => {
modelRefActions = actions modelRefActions = actions
}) })
$: refActions = defaultRefActions $: refActions = readonly
.concat(extraActions) ? []
.concat(modelRefActions) : defaultRefActions
.sort((a, b) => a.order - b.order) .concat(extraActions)
.concat(modelRefActions)
.sort((a, b) => a.order - b.order)
let progress = false let progress = false
let attachments: Attachment[] = [] let attachments: Attachment[] = []
@ -89,6 +92,7 @@
} }
async function fileSelected (): Promise<void> { async function fileSelected (): Promise<void> {
if (readonly) return
progress = true progress = true
const list = inputFile.files const list = inputFile.files
if (list === null || list.length === 0) return if (list === null || list.length === 0) return
@ -155,6 +159,7 @@
} }
export async function pasteAction (evt: ClipboardEvent): Promise<void> { export async function pasteAction (evt: ClipboardEvent): Promise<void> {
if (readonly) return
if (!isAllowedPaste(evt)) { if (!isAllowedPaste(evt)) {
return return
} }
@ -172,6 +177,7 @@
} }
export async function fileDrop (e: DragEvent): Promise<void> { export async function fileDrop (e: DragEvent): Promise<void> {
if (readonly) return
progress = true progress = true
const list = e.dataTransfer?.files const list = e.dataTransfer?.files
if (list !== undefined && list.length !== 0) { if (list !== undefined && list.length !== 0) {
@ -231,6 +237,7 @@
{placeholder} {placeholder}
{boundary} {boundary}
{refActions} {refActions}
{readonly}
attachFile={async (file) => { attachFile={async (file) => {
return await createAttachment(file) return await createAttachment(file)
}} }}
@ -248,6 +255,7 @@
{#if (attachments.length > 0 && enableAttachments) || progress} {#if (attachments.length > 0 && enableAttachments) || progress}
<AttachmentsGrid <AttachmentsGrid
{attachments} {attachments}
{readonly}
{progress} {progress}
{progressItems} {progressItems}
{useAttachmentPreview} {useAttachmentPreview}

View File

@ -24,6 +24,7 @@
export let useAttachmentPreview = false export let useAttachmentPreview = false
export let progress = false export let progress = false
export let progressItems: Ref<Doc>[] = [] export let progressItems: Ref<Doc>[] = []
export let readonly: boolean = false
let element: HTMLElement let element: HTMLElement
let attachmentPopupId: string = '' let attachmentPopupId: string = ''
@ -46,11 +47,16 @@
<div class="attachment-grid"> <div class="attachment-grid">
{#each attachments as attachment (attachment._id)} {#each attachments as attachment (attachment._id)}
{#if useAttachmentPreview} {#if useAttachmentPreview}
<AttachmentPreview value={attachment} {listProvider} on:open={(res) => (attachmentPopupId = res.detail)} /> <AttachmentPreview
value={attachment}
removable={!readonly}
{listProvider}
on:open={(res) => (attachmentPopupId = res.detail)}
/>
{:else} {:else}
<AttachmentPresenter <AttachmentPresenter
value={attachment} value={attachment}
removable removable={!readonly}
showPreview showPreview
progress={progressItems.includes(attachment._id)} progress={progressItems.includes(attachment._id)}
on:remove on:remove

View File

@ -20,17 +20,8 @@
import { Panel } from '@hcengineering/panel' import { Panel } from '@hcengineering/panel'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import { StyledTextBox } from '@hcengineering/text-editor' import { StyledTextBox } from '@hcengineering/text-editor'
import { import { Button, EditBox, IconMoreH } from '@hcengineering/ui'
Button, import { DocAttributeBar, ParentsNavigator, invokeAction, showMenu } from '@hcengineering/view-resources'
CircleButton,
EditBox,
IconAdd,
IconMoreH,
Label,
getEventPopupPositionElement,
showPopup
} from '@hcengineering/ui'
import { ContextMenu, DocAttributeBar, ParentsNavigator, invokeAction } from '@hcengineering/view-resources'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import board from '../plugin' import board from '../plugin'
import { getCardActions } from '../utils/CardActionUtils' import { getCardActions } from '../utils/CardActionUtils'
@ -113,11 +104,7 @@
kind="ghost" kind="ghost"
size="medium" size="medium"
on:click={(e) => { on:click={(e) => {
showPopup( showMenu(e, { object, baseMenuClass: board.class.Card, mode: 'editor' })
ContextMenu,
{ object, baseMenuClass: board.class.Card, mode: 'editor' },
getEventPopupPositionElement(e)
)
}} }}
/> />
</svelte:fragment> </svelte:fragment>

View File

@ -22,19 +22,9 @@
import { ChatMessagesPresenter } from '@hcengineering/notification-resources' import { ChatMessagesPresenter } from '@hcengineering/notification-resources'
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
import tags from '@hcengineering/tags' import tags from '@hcengineering/tags'
import { import { Button, Component, EditBox, Icon, IconMoreV, Label, numberToHexColor } from '@hcengineering/ui'
Button,
Component,
EditBox,
Icon,
IconMoreV,
Label,
getPopupPositionElement,
numberToHexColor,
showPopup
} from '@hcengineering/ui'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { ContextMenu } from '@hcengineering/view-resources' import { showMenu } from '@hcengineering/view-resources'
import board from '../plugin' import board from '../plugin'
import { hasDate, openCardPanel, updateCard, updateCardMembers } from '../utils/CardUtils' import { hasDate, openCardPanel, updateCard, updateCardMembers } from '../utils/CardUtils'
import DatePresenter from './presenters/DatePresenter.svelte' import DatePresenter from './presenters/DatePresenter.svelte'
@ -53,9 +43,9 @@
isEditMode = false isEditMode = false
} }
function enterEditMode (): void { function enterEditMode (e: MouseEvent): void {
isEditMode = true isEditMode = true
showPopup(ContextMenu, { object }, getPopupPositionElement(ref, { h: 'right', v: 'top' }), exitEditMode) showMenu(e, { object }, exitEditMode)
} }
function showCard () { function showCard () {

View File

@ -14,7 +14,7 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import board, { Card } from '@hcengineering/board' import { Card } from '@hcengineering/board'
import { import {
CategoryType, CategoryType,
Class, Class,
@ -30,10 +30,8 @@
import { ActionContext, createQuery } from '@hcengineering/presentation' import { ActionContext, createQuery } from '@hcengineering/presentation'
import type { DocWithRank, Project } from '@hcengineering/task' import type { DocWithRank, Project } from '@hcengineering/task'
import task, { getStates } from '@hcengineering/task' import task, { getStates } from '@hcengineering/task'
import { getEventPositionElement, showPopup } from '@hcengineering/ui'
import { typeStore } from '@hcengineering/task-resources' import { typeStore } from '@hcengineering/task-resources'
import { import {
ContextMenu,
ListSelectionProvider, ListSelectionProvider,
SelectDirection, SelectDirection,
focusStore, focusStore,
@ -41,6 +39,7 @@
getGroupByValues, getGroupByValues,
groupBy, groupBy,
setGroupByValues, setGroupByValues,
showMenu,
statusStore statusStore
} from '@hcengineering/view-resources' } from '@hcengineering/view-resources'
import { onMount } from 'svelte' import { onMount } from 'svelte'
@ -77,15 +76,6 @@
const selection = listProvider.selection const selection = listProvider.selection
const showMenu = async (ev: MouseEvent, object: Doc): Promise<void> => {
ev.preventDefault()
if (object._class !== board.class.Card) {
return
}
showPopup(ContextMenu, { object }, getEventPositionElement(ev))
}
let resultQuery: DocumentQuery<DocWithRank> let resultQuery: DocumentQuery<DocWithRank>
$: resultQuery = { ...query, isArchived: { $nin: [true] }, space } $: resultQuery = { ...query, isArchived: { $nin: [true] }, space }
@ -144,7 +134,9 @@
on:check={(evt) => { on:check={(evt) => {
listProvider.updateSelection(evt.detail.docs, evt.detail.value) listProvider.updateSelection(evt.detail.docs, evt.detail.value)
}} }}
on:contextmenu={(evt) => showMenu(evt.detail.evt, evt.detail.objects)} on:contextmenu={(evt) => {
showMenu(evt.detail.evt, { object: evt.detail.objects })
}}
selection={listProvider.current($focusStore)} selection={listProvider.current($focusStore)}
> >
<svelte:fragment slot="card" let:object> <svelte:fragment slot="card" let:object>

View File

@ -1,21 +1,8 @@
<script lang="ts"> <script lang="ts">
import { State } from '@hcengineering/task' import { State } from '@hcengineering/task'
import { import { Button, getPlatformColorDef, getPlatformColorForTextDef, IconMoreV, themeStore } from '@hcengineering/ui'
Button, import { showMenu } from '@hcengineering/view-resources'
getEventPositionElement,
getPlatformColorDef,
getPlatformColorForTextDef,
IconMoreV,
showPopup,
themeStore
} from '@hcengineering/ui'
import { ContextMenu } from '@hcengineering/view-resources'
export let state: State export let state: State
const showMenu = async (ev: MouseEvent): Promise<void> => {
ev.preventDefault()
showPopup(ContextMenu, { object: state }, getEventPositionElement(ev))
}
</script> </script>
{#if state} {#if state}
@ -29,7 +16,13 @@
<div class="flex-between h-full font-medium pr-2 pl-4"> <div class="flex-between h-full font-medium pr-2 pl-4">
<span class="lines-limit-2">{state.name}</span> <span class="lines-limit-2">{state.name}</span>
<div class="flex"> <div class="flex">
<Button icon={IconMoreV} kind="ghost" on:click={showMenu} /> <Button
icon={IconMoreV}
kind="ghost"
on:click={(e) => {
showMenu(e, { object: state })
}}
/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -37,7 +37,7 @@
ticker, ticker,
isWeekend isWeekend
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { Menu } from '@hcengineering/view-resources' import { Menu, showMenu } from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy, onMount } from 'svelte' import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import type { import type {
CalendarADGrid, CalendarADGrid,
@ -759,12 +759,6 @@
dispatch('dragEnter', { date: new Date(new Date(newTime).setMinutes(stickyMinutes, 0, 0)) }) dispatch('dragEnter', { date: new Date(new Date(newTime).setMinutes(stickyMinutes, 0, 0)) })
} }
} }
function showMenu (ev: MouseEvent, event: Event) {
ev.preventDefault()
closeTooltip()
showPopup(Menu, { object: event }, getEventPositionElement(ev))
}
</script> </script>
<Scroller <Scroller
@ -1011,7 +1005,7 @@
mouseDownElement(e, ev, 'top') mouseDownElement(e, ev, 'top')
}} }}
on:contextmenu={(e) => { on:contextmenu={(e) => {
showMenu(e, ev) showMenu(e, { object: ev })
}} }}
/> />
<div <div
@ -1022,7 +1016,7 @@
mouseDownElement(e, ev, 'bottom') mouseDownElement(e, ev, 'bottom')
}} }}
on:contextmenu={(e) => { on:contextmenu={(e) => {
showMenu(e, ev) showMenu(e, { object: ev })
}} }}
/> />
<EventElement <EventElement

View File

@ -16,16 +16,9 @@
import calendar, { CalendarEventPresenter, Event } from '@hcengineering/calendar' import calendar, { CalendarEventPresenter, Event } from '@hcengineering/calendar'
import { Doc } from '@hcengineering/core' import { Doc } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
import { import { Component, MILLISECONDS_IN_MINUTE, showPopup, tooltip } from '@hcengineering/ui'
Component,
MILLISECONDS_IN_MINUTE,
closeTooltip,
getEventPositionElement,
showPopup,
tooltip
} from '@hcengineering/ui'
import view, { ObjectEditor } from '@hcengineering/view' import view, { ObjectEditor } from '@hcengineering/view'
import { Menu } from '@hcengineering/view-resources' import { showMenu } from '@hcengineering/view-resources'
import { calendarStore, isVisible } from '../utils' import { calendarStore, isVisible } from '../utils'
import EventPresenter from './EventPresenter.svelte' import EventPresenter from './EventPresenter.svelte'
@ -55,13 +48,11 @@
let div: HTMLDivElement let div: HTMLDivElement
function showMenu (ev: MouseEvent) {
ev.preventDefault()
closeTooltip()
showPopup(Menu, { object: event }, getEventPositionElement(ev))
}
$: visible = isVisible(event, $calendarStore) $: visible = isVisible(event, $calendarStore)
function contextMenu (e: MouseEvent): void {
showMenu(e, { object: event })
}
</script> </script>
{#if event} {#if event}
@ -74,7 +65,7 @@
class:empty class:empty
use:tooltip={{ component: EventPresenter, props: { value: event, hideDetails: !visible } }} use:tooltip={{ component: EventPresenter, props: { value: event, hideDetails: !visible } }}
on:click|stopPropagation={click} on:click|stopPropagation={click}
on:contextmenu={showMenu} on:contextmenu={contextMenu}
> >
{#if !empty && presenter?.presenter} {#if !empty && presenter?.presenter}
<Component is={presenter.presenter} props={{ event, narrow, oneRow, hideDetails: !visible }} /> <Component is={presenter.presenter} props={{ event, narrow, oneRow, hideDetails: !visible }} />

View File

@ -18,6 +18,7 @@
import { Button, ButtonKind, ButtonSize, IconThread } from '@hcengineering/ui' import { Button, ButtonKind, ButtonSize, IconThread } from '@hcengineering/ui'
import ChatMessagePopup from './ChatMessagePopup.svelte' import ChatMessagePopup from './ChatMessagePopup.svelte'
import { restrictionStore } from '@hcengineering/view-resources'
export let value: number | undefined export let value: number | undefined
export let object: Doc export let object: Doc
@ -26,6 +27,8 @@
export let showCounter: boolean = true export let showCounter: boolean = true
export let compactMode: boolean = false export let compactMode: boolean = false
export let withInput: boolean = true export let withInput: boolean = true
$: disabled = $restrictionStore.disableComments
</script> </script>
{#if value && value > 0} {#if value && value > 0}
@ -34,7 +37,7 @@
{size} {size}
showTooltip={{ showTooltip={{
component: ChatMessagePopup, component: ChatMessagePopup,
props: { objectId: object._id, object, withInput } props: { objectId: object._id, object, withInput: withInput && !disabled }
}} }}
> >
<div slot="icon"> <div slot="icon">

View File

@ -35,11 +35,11 @@
import { openDoc } from '@hcengineering/view-resources' import { openDoc } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { PersonLabelTooltip, personByIdStore } from '..' import { PersonLabelTooltip, personByIdStore } from '..'
import { AssigneeCategory } from '../assignee'
import AssigneePopup from './AssigneePopup.svelte' import AssigneePopup from './AssigneePopup.svelte'
import EmployeePresenter from './EmployeePresenter.svelte' import EmployeePresenter from './EmployeePresenter.svelte'
import UserInfo from './UserInfo.svelte' import UserInfo from './UserInfo.svelte'
import IconPerson from './icons/Person.svelte' import IconPerson from './icons/Person.svelte'
import { AssigneeCategory } from '../assignee'
export let _class: Ref<Class<Employee>> = contact.mixin.Employee export let _class: Ref<Class<Employee>> = contact.mixin.Employee
export let excluded: Ref<Contact>[] | undefined = undefined export let excluded: Ref<Contact>[] | undefined = undefined
@ -156,7 +156,16 @@
onEmployeeEdit={_click} onEmployeeEdit={_click}
/> />
{:else} {:else}
<Button {focusIndex} width={width ?? 'min-content'} {size} {kind} {justify} {showTooltip} on:click={_click}> <Button
{focusIndex}
disabled={readonly}
width={width ?? 'min-content'}
{size}
{kind}
{justify}
{showTooltip}
on:click={_click}
>
<span <span
slot="content" slot="content"
class="overflow-label flex-grow" class="overflow-label flex-grow"

View File

@ -19,6 +19,7 @@
import { ButtonKind, ButtonSize, closeTooltip, showPopup } from '@hcengineering/ui' import { ButtonKind, ButtonSize, closeTooltip, showPopup } from '@hcengineering/ui'
import { Channel, ChannelProvider } from '@hcengineering/contact' import { Channel, ChannelProvider } from '@hcengineering/contact'
import { restrictionStore } from '@hcengineering/view-resources'
import contact from '../plugin' import contact from '../plugin'
import ChannelsDropdown from './ChannelsDropdown.svelte' import ChannelsDropdown from './ChannelsDropdown.svelte'
@ -26,7 +27,7 @@
export let attachedClass: Ref<Class<Doc>> export let attachedClass: Ref<Class<Doc>>
export let integrations: Set<Ref<Doc>> | undefined = undefined export let integrations: Set<Ref<Doc>> | undefined = undefined
export let editable: boolean = true export let editable: boolean = true
export let allowOpen = true export let allowOpen: boolean = !$restrictionStore.disableNavigation
export let focusIndex = -1 export let focusIndex = -1
export let kind: ButtonKind = 'link-bordered' export let kind: ButtonKind = 'link-bordered'

View File

@ -25,6 +25,7 @@
import Company from './icons/Company.svelte' import Company from './icons/Company.svelte'
export let object: Organization export let object: Organization
export let readonly: boolean = false
const client = getClient() const client = getClient()
@ -58,6 +59,7 @@
<div class="flex-grow flex-col"> <div class="flex-grow flex-col">
<div class="name"> <div class="name">
<EditBox <EditBox
disabled={readonly}
placeholder={contact.string.PersonFirstNamePlaceholder} placeholder={contact.string.PersonFirstNamePlaceholder}
bind:value={object.name} bind:value={object.name}
on:change={nameChange} on:change={nameChange}
@ -66,7 +68,14 @@
</div> </div>
<div class="separator" /> <div class="separator" />
<Scroller contentDirection={'horizontal'} padding={'.125rem .125rem .5rem'} stickedScrollBars thinScrollBars> <Scroller contentDirection={'horizontal'} padding={'.125rem .125rem .5rem'} stickedScrollBars thinScrollBars>
<ChannelsEditor attachedTo={object._id} attachedClass={object._class} {integrations} focusIndex={10} on:click /> <ChannelsEditor
editable={!readonly}
attachedTo={object._id}
attachedClass={object._class}
{integrations}
focusIndex={10}
on:click
/>
</Scroller> </Scroller>
</div> </div>
</div> </div>

View File

@ -14,17 +14,18 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { combineName, PersonAccount, getFirstName, getLastName, Person } from '@hcengineering/contact' import { Person, PersonAccount, combineName, getFirstName, getLastName } from '@hcengineering/contact'
import { getCurrentAccount, Ref } from '@hcengineering/core' import { Ref, getCurrentAccount } from '@hcengineering/core'
import { AttributeEditor, createQuery, getClient } from '@hcengineering/presentation' import { AttributeEditor, createQuery, getClient } from '@hcengineering/presentation'
import setting, { IntegrationType } from '@hcengineering/setting' import setting, { IntegrationType } from '@hcengineering/setting'
import { createFocusManager, EditBox, FocusHandler, Scroller } from '@hcengineering/ui' import { EditBox, FocusHandler, Scroller, createFocusManager } from '@hcengineering/ui'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import contact from '../plugin' import contact from '../plugin'
import ChannelsEditor from './ChannelsEditor.svelte' import ChannelsEditor from './ChannelsEditor.svelte'
import EditableAvatar from './EditableAvatar.svelte' import EditableAvatar from './EditableAvatar.svelte'
export let object: Person export let object: Person
export let readonly: boolean = false
const client = getClient() const client = getClient()
const account = getCurrentAccount() as PersonAccount const account = getCurrentAccount() as PersonAccount
@ -84,6 +85,7 @@
<div class="flex-no-shrink mr-8"> <div class="flex-no-shrink mr-8">
{#key object} {#key object}
<EditableAvatar <EditableAvatar
disabled={readonly}
avatar={object.avatar} avatar={object.avatar}
size={'x-large'} size={'x-large'}
name={object.name} name={object.name}
@ -95,6 +97,7 @@
<div class="flex-grow flex-col"> <div class="flex-grow flex-col">
<div class="name"> <div class="name">
<EditBox <EditBox
disabled={readonly}
placeholder={contact.string.PersonFirstNamePlaceholder} placeholder={contact.string.PersonFirstNamePlaceholder}
bind:value={firstName} bind:value={firstName}
on:change={firstNameChange} on:change={firstNameChange}
@ -103,6 +106,7 @@
</div> </div>
<div class="name"> <div class="name">
<EditBox <EditBox
disabled={readonly}
placeholder={contact.string.PersonLastNamePlaceholder} placeholder={contact.string.PersonLastNamePlaceholder}
bind:value={lastName} bind:value={lastName}
on:change={lastNameChange} on:change={lastNameChange}
@ -110,7 +114,14 @@
/> />
</div> </div>
<div class="location"> <div class="location">
<AttributeEditor maxWidth="20rem" _class={contact.class.Person} {object} key="city" focusIndex={3} /> <AttributeEditor
maxWidth="20rem"
_class={contact.class.Person}
{object}
editable={!readonly}
key="city"
focusIndex={3}
/>
</div> </div>
<div class="separator" /> <div class="separator" />
@ -124,6 +135,7 @@
<ChannelsEditor <ChannelsEditor
attachedTo={object._id} attachedTo={object._id}
attachedClass={object._class} attachedClass={object._class}
editable={!readonly}
bind:integrations bind:integrations
shape={'circle'} shape={'circle'}
focusIndex={10} focusIndex={10}

View File

@ -26,6 +26,7 @@
export let objectId: Ref<Doc> export let objectId: Ref<Doc>
export let space: Ref<Space> export let space: Ref<Space>
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
export let readonly: boolean = false
export let members: number export let members: number
@ -40,6 +41,7 @@
let loading = true let loading = true
const createApp = async (ev: MouseEvent): Promise<void> => { const createApp = async (ev: MouseEvent): Promise<void> => {
if (readonly) return
showPopup( showPopup(
UsersPopup, UsersPopup,
{ {
@ -77,7 +79,9 @@
viewletQuery={{ _id: contact.viewlet.TableMember }} viewletQuery={{ _id: contact.viewlet.TableMember }}
/> />
<ViewletSettingButton kind={'ghost'} bind:viewlet /> <ViewletSettingButton kind={'ghost'} bind:viewlet />
<Button id={contact.string.AddMember} icon={IconAdd} kind={'ghost'} on:click={createApp} /> {#if !readonly}
<Button id={contact.string.AddMember} icon={IconAdd} kind={'ghost'} on:click={createApp} />
{/if}
</div> </div>
</svelte:fragment> </svelte:fragment>
@ -89,6 +93,7 @@
options={viewlet.options} options={viewlet.options}
query={{ attachedTo: objectId }} query={{ attachedTo: objectId }}
loadingProps={{ length: members }} loadingProps={{ length: members }}
{readonly}
/> />
{:else} {:else}
<div class="antiSection-empty solid flex-col mt-3"> <div class="antiSection-empty solid flex-col mt-3">
@ -97,9 +102,11 @@
</span> </span>
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<span class="over-underline content-color" on:click={createApp}> {#if !readonly}
<Label label={contact.string.AddMember} /> <span class="over-underline content-color" on:click={createApp}>
</span> <Label label={contact.string.AddMember} />
</span>
{/if}
</div> </div>
{/if} {/if}
</svelte:fragment> </svelte:fragment>

View File

@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/assets/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="link" viewBox="0 0 32 32">
<path d="M29.25 6.75997C28.6926 6.20061 28.0302 5.75679 27.3009 5.45396C26.5716 5.15112 25.7897 4.99524 25 4.99524C24.2103 4.99524 23.4284 5.15112 22.6991 5.45396C22.278 5.62881 21.8792 5.85065 21.5101 6.1146C21.0614 6.43545 21.0693 7.07931 21.4594 7.46935C21.8499 7.85988 22.4827 7.84994 22.9575 7.5679C23.1218 7.47029 23.2933 7.38434 23.4707 7.31086C23.9571 7.10938 24.4785 7.00567 25.005 7.00567C25.5315 7.00567 26.0528 7.10938 26.5393 7.31086C27.0257 7.51235 27.4677 7.80767 27.84 8.17997C28.2123 8.55227 28.5076 8.99425 28.7091 9.48068C28.9106 9.96711 29.0143 10.4885 29.0143 11.015C29.0143 11.5415 28.9106 12.0628 28.7091 12.5493C28.5076 13.0357 28.2123 13.4777 27.84 13.85L19.84 21.85C19.0894 22.6019 18.0709 23.0248 17.0085 23.0257C15.9461 23.0267 14.9269 22.6055 14.175 21.855C13.4231 21.1044 13.0001 20.0859 12.9992 19.0235C12.9983 17.9611 13.4194 16.9419 14.17 16.19L14.8803 15.4746C15.2675 15.0846 15.2659 14.4537 14.8787 14.0637C14.4886 13.6709 13.8519 13.6681 13.4604 14.0596L12.75 14.77C12.1906 15.3274 11.7468 15.9897 11.444 16.7191C11.1411 17.4484 10.9852 18.2303 10.9852 19.02C10.9852 19.8097 11.1411 20.5916 11.444 21.3209C11.7468 22.0502 12.1906 22.7125 12.75 23.27C13.8815 24.387 15.41 25.0092 17 25C17.7927 25.0032 18.5782 24.8494 19.3111 24.5473C20.044 24.2452 20.7098 23.8009 21.27 23.24L29.27 15.24C30.3909 14.1123 31.0184 12.5858 31.0147 10.9958C31.0109 9.40582 30.3762 7.88232 29.25 6.75997Z" />
<path d="M4.18997 24.82C3.81656 24.4483 3.52026 24.0065 3.31807 23.52C3.11589 23.0335 3.01181 22.5118 3.01181 21.985C3.01181 21.4581 3.11589 20.9365 3.31807 20.4499C3.52026 19.9634 3.81656 19.5216 4.18997 19.15L12.19 11.15C12.5616 10.7766 13.0034 10.4803 13.4899 10.2781C13.9765 10.0759 14.4981 9.97181 15.025 9.97181C15.5518 9.97181 16.0735 10.0759 16.56 10.2781C17.0465 10.4803 17.4883 10.7766 17.86 11.15C18.231 11.5246 18.5231 11.9698 18.7189 12.4594C18.9147 12.9489 19.0103 13.4728 19 14C19.003 14.5288 18.9012 15.0529 18.7004 15.5421C18.4995 16.0313 18.2037 16.4758 17.83 16.85L16.4072 18.2929C16.0213 18.6842 16.0289 19.3189 16.4175 19.7075C16.808 20.098 17.4466 20.1034 17.8371 19.7129L19.25 18.3C20.3785 17.1715 21.0124 15.6409 21.0124 14.045C21.0124 12.449 20.3785 10.9185 19.25 9.78997C18.1215 8.66147 16.5909 8.02749 14.995 8.02749C13.399 8.02749 11.8685 8.66147 10.74 9.78997L2.73997 17.79C2.17911 18.3476 1.73401 19.0106 1.43029 19.7408C1.12657 20.471 0.970215 21.2541 0.970215 22.045C0.970215 22.8358 1.12657 23.6189 1.43029 24.3492C1.73401 25.0794 2.17911 25.7424 2.73997 26.3C3.87879 27.4084 5.41087 28.0198 6.99997 28C8.26594 28.0012 9.49169 27.6069 10.5118 26.885C10.964 26.5651 10.961 25.921 10.5693 25.5293C10.1781 25.1381 9.54699 25.1518 9.0717 25.4348C8.90786 25.5324 8.73689 25.6184 8.56 25.6919C8.07349 25.8941 7.55182 25.9981 7.02497 25.9981C6.49812 25.9981 5.97645 25.8941 5.48994 25.6919C5.00342 25.4897 4.56164 25.1934 4.18997 24.82Z" />
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "assets"
}

View File

@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}

View File

@ -0,0 +1,9 @@
{
"string": {
"CreatePublicLink": "Create public link",
"PublicLink": "Public link",
"Revoke": "Revoke",
"RevokeConfirmation": "This will cause previously shared link to stop working. Are you sure you want to revoke this public link?",
"LinkWasRevoked": "Public link was revoked"
}
}

View File

@ -0,0 +1,9 @@
{
"string": {
"CreatePublicLink": "Создать публичную ссылку",
"PublicLink": "Публичная ссылка",
"Revoke": "Отозвать",
"RevokeConfirmation": "Это приведет к тому, что ранее созданная ссылка перестанет работать. Вы уверены, что хотите отозвать эту публичную ссылку?",
"LinkWasRevoked": "Ссылка была отозвана"
}
}

View File

@ -0,0 +1,44 @@
{
"name": "@hcengineering/guest-assets",
"version": "0.6.0",
"main": "src/index.ts",
"author": "Anticrm Platform Contributors",
"template": "@hcengineering/assets-package",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"test": "jest --passWithNoTests --silent",
"build:docs": "",
"format": "format src",
"build:watch": "compile",
"_phase:build": "compile transpile src",
"_phase:test": "jest --passWithNoTests --silent",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.4.0",
"eslint-plugin-promise": "^6.1.1",
"eslint": "^8.54.0",
"prettier": "^3.1.0",
"@types/node": "~20.11.16",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"prettier-plugin-svelte": "^3.1.0",
"typescript": "^5.3.3"
},
"dependencies": {
"@hcengineering/platform": "^0.6.9",
"@hcengineering/guest": "^0.6.0"
},
"repository": "https://github.com/hcenginneing/anticrm",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}

View File

@ -0,0 +1,6 @@
import { makeLocalesTest } from '@hcengineering/platform'
it(
'Locales are equale',
makeLocalesTest((lang) => import(`../../lang/${lang}.json`))
)

View File

@ -0,0 +1,23 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { addStringsLoader, loadMetadata } from '@hcengineering/platform'
import guest, { guestId } from '@hcengineering/guest'
const icons = require('../assets/icons.svg') as string // eslint-disable-line
loadMetadata(guest.icon, {
Link: `${icons}#link`
})
addStringsLoader(guestId, async (lang: string) => await import(`../lang/${lang}.json`))

View File

@ -0,0 +1,10 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/assets/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"types": ["node", "jest"],
"tsBuildInfoFile": ".build/build.tsbuildinfo"
}
}

View File

@ -0,0 +1,4 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'],
parserOptions: { tsconfigRootDir: __dirname }
}

View File

@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "ui"
}

View File

@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)']
}

View File

@ -0,0 +1,54 @@
{
"name": "@hcengineering/guest-resources",
"version": "0.6.0",
"main": "src/index.ts",
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "compile ui",
"build:docs": "api-extractor run --local",
"format": "format src",
"build:watch": "compile ui",
"_phase:build": "compile ui",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"svelte-loader": "^3.1.9",
"sass": "^1.53.0",
"svelte-preprocess": "^5.1.0",
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.4.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-svelte": "^2.34.0",
"prettier-plugin-svelte": "^3.1.0",
"eslint": "^8.54.0",
"prettier": "^3.1.0",
"svelte-check": "^3.6.0",
"typescript": "^5.3.3",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"svelte-eslint-parser": "^0.33.1"
},
"dependencies": {
"svelte": "^4.2.5",
"@hcengineering/platform": "^0.6.9",
"@hcengineering/core": "^0.6.28",
"@hcengineering/client": "^0.6.14",
"@hcengineering/workbench": "^0.6.9",
"@hcengineering/workbench-resources": "^0.6.1",
"@hcengineering/ui": "^0.6.11",
"@hcengineering/view": "^0.6.9",
"@hcengineering/presentation": "^0.6.2",
"@hcengineering/guest": "^0.6.0",
"@hcengineering/login": "^0.6.8",
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/analytics": "^0.6.0",
"fast-copy": "~3.0.1"
}
}

View File

@ -0,0 +1,5 @@
module.exports = {
plugins: [
require('autoprefixer')
]
}

View File

@ -0,0 +1,119 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Doc } from '@hcengineering/core'
import presentaion, {
Card,
MessageBox,
copyTextToClipboard,
createQuery,
getClient
} from '@hcengineering/presentation'
import guest from '../plugin'
import { Button, Loading, Location, showPopup } from '@hcengineering/ui'
import { getObjectLinkFragment } from '@hcengineering/view-resources'
import view from '@hcengineering/view'
import { PublicLink } from '@hcengineering/guest'
import { createEventDispatcher } from 'svelte'
export let value: Doc
const client = getClient()
const query = createQuery()
let loading = true
let link: PublicLink | undefined
const dispatch = createEventDispatcher()
async function createLink (location: Location, revokable: boolean = true): Promise<void> {
await client.createDoc(guest.class.PublicLink, guest.space.Links, {
attachedTo: value._id,
location,
revokable,
restrictions: {
readonly: true,
disableNavigation: true,
disableActions: true,
disableComments: true
},
url: ''
})
}
async function generate (object: Doc): Promise<void> {
const panelComponent = client.getHierarchy().classHierarchyMixin(object._class, view.mixin.ObjectPanel)
const comp = panelComponent?.component ?? view.component.EditDoc
const loc = await getObjectLinkFragment(client.getHierarchy(), object, {}, comp)
await createLink(loc)
}
async function checkNeedGenerate (value: Doc, loading: boolean, link: PublicLink | undefined): Promise<void> {
if (!loading || link !== undefined) return
await generate(value)
}
query.query(
guest.class.PublicLink,
{ attachedTo: value._id },
(res) => {
link = res[0]
void checkNeedGenerate(value, loading, link)
loading = false
},
{ limit: 1 }
)
function close () {
dispatch('close')
}
function copy (): void {
if (link?.url === undefined || link.url === '') return
copyTextToClipboard(link.url)
}
async function revoke (): Promise<void> {
if (!revokable || link === undefined) return
showPopup(
MessageBox,
{
label: guest.string.Revoke,
message: guest.string.RevokeConfirmation
},
'top',
(res) => {
if (res === true && link !== undefined) {
client.remove(link)
}
dispatch('close')
}
)
}
$: revokable = link?.revokable ?? false
</script>
<Card label={guest.string.PublicLink} canSave={true} okLabel={presentaion.string.Close} on:close okAction={close}>
{#if link?.url === undefined || link.url === ''}
<Loading />
{:else}
<div class="over-underline link w-full overflow-label" on:click={copy}>{link.url}</div>
{/if}
<svelte:fragment slot="buttons">
{#if revokable}
<Button label={guest.string.Revoke} kind={'dangerous'} size={'large'} on:click={revoke} />
{/if}
</svelte:fragment>
</Card>

View File

@ -0,0 +1,337 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Analytics } from '@hcengineering/analytics'
import core, { Class, Doc, Ref, Space } from '@hcengineering/core'
import { getMetadata, getResource } from '@hcengineering/platform'
import { ActionContext, getClient } from '@hcengineering/presentation'
import {
AnyComponent,
Component,
Label,
Location,
PanelInstance,
Popup,
PopupAlignment,
ResolvedLocation,
Separator,
TooltipInstance,
areLocationsEqual,
closePanel,
getCurrentLocation,
getLocation,
navigate,
openPanel,
setResolvedLocation
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { ListSelectionProvider, restrictionStore, updateFocus } from '@hcengineering/view-resources'
import workbench, { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@hcengineering/workbench'
import { buildNavModel, SpaceView } from '@hcengineering/workbench-resources'
import guest from '../plugin'
import { decodeTokenPayload } from '../utils'
const excludedApps = getMetadata(workbench.metadata.ExcludedApplications) ?? []
const client = getClient()
async function load (): Promise<boolean> {
const loc = getCurrentLocation()
const token = loc.query?.token
if (token == null) return false
const decoded = decodeTokenPayload(token)
const link = await client.findOne(guest.class.PublicLink, { _id: decoded.linkId })
if (link == null) return false
restrictionStore.set(link.restrictions)
await doSyncLoc(link.location)
return true
}
let contentPanel: HTMLElement
let panelInstance: PanelInstance
let popupInstance: Popup
const apps: Application[] = client
.getModel()
.findAllSync<Application>(workbench.class.Application, { hidden: false, _id: { $nin: excludedApps } })
async function resolveShortLink (loc: Location): Promise<ResolvedLocation | undefined> {
if (loc.path[2] !== undefined && loc.path[2].trim().length > 0) {
const app = apps.find((p) => p.alias === loc.path[2])
if (app?.locationResolver) {
const resolver = await getResource(app.locationResolver)
return await resolver(loc)
}
}
}
function mergeLoc (loc: Location, resolved: ResolvedLocation): Location {
const resolvedApp = resolved.loc.path[2]
const resolvedSpace = resolved.loc.path[3]
const resolvedSpecial = resolved.loc.path[4]
if (resolvedApp === undefined) {
loc.path = [loc.path[0], loc.path[1], ...resolved.defaultLocation.path.splice(2)]
} else {
loc.path[2] = resolvedApp
if (resolvedSpace === undefined) {
loc.path[3] = currentSpace ?? (currentSpecial as string) ?? resolved.defaultLocation.path[3]
loc.path[4] = (currentSpecial as string) ?? resolved.defaultLocation.path[4]
} else {
loc.path[3] = resolvedSpace
loc.path[4] = resolvedSpecial ?? currentSpecial ?? resolved.defaultLocation.path[4]
}
}
for (let index = 0; index < loc.path.length; index++) {
const path = loc.path[index]
if (path === undefined) {
loc.path.length = index
break
}
}
loc.fragment = resolved.loc.fragment ?? loc.fragment ?? resolved.defaultLocation.fragment
return loc
}
let currentSpace: Ref<Space> | undefined
let currentSpecial: string | undefined
let specialComponent: SpecialNavModel | undefined
let asideId: string | undefined
let currentFragment: string | undefined = ''
let currentApplication: Application | undefined
let navigatorModel: NavigatorModel | undefined
let currentView: ViewConfiguration | undefined
function setSpaceSpecial (spaceSpecial: string | undefined): void {
if (currentSpecial !== undefined && spaceSpecial === currentSpecial) return
if (asideId !== undefined && spaceSpecial === asideId) return
if (spaceSpecial === undefined) return
specialComponent = getSpecialComponent(spaceSpecial)
if (specialComponent !== undefined) {
currentSpecial = spaceSpecial
} else if (navigatorModel?.aside !== undefined || currentApplication?.aside !== undefined) {
asideId = spaceSpecial
}
}
function getSpecialComponent (id: string): SpecialNavModel | undefined {
const sp = navigatorModel?.specials?.find((x) => x.id === id)
if (sp !== undefined) {
return sp
}
for (const s of navigatorModel?.spaces ?? []) {
const sp = s.specials?.find((x) => x.id === id)
if (sp !== undefined) {
return sp
}
}
}
async function syncLoc (loc: Location): Promise<void> {
if (loc.path.length > 3 && getSpecialComponent(loc.path[3]) === undefined) {
// resolve short links
const resolvedLoc = await resolveShortLink(loc)
if (resolvedLoc !== undefined && !areLocationsEqual(loc, resolvedLoc.loc)) {
loc = mergeLoc(loc, resolvedLoc)
}
}
setResolvedLocation(loc)
const app = loc.path[2]
const space = loc.path[3] as Ref<Space>
const special = loc.path[4]
const fragment = loc.fragment
currentApplication = await client.findOne<Application>(workbench.class.Application, { alias: app })
navigatorModel = await buildNavModel(client, currentApplication)
if (currentSpecial === undefined || currentSpecial !== space) {
const newSpecial = space !== undefined ? getSpecialComponent(space) : undefined
if (newSpecial !== undefined) {
specialComponent = newSpecial
currentSpecial = space
} else {
await updateSpace(space)
setSpaceSpecial(special)
}
}
if (special !== currentSpecial && (navigatorModel?.aside || currentApplication?.aside)) {
asideId = special
}
if (fragment !== currentFragment) {
currentFragment = fragment
if (fragment !== undefined && fragment.trim().length > 0) {
await setOpenPanelFocus(fragment)
} else {
closePanel()
}
}
}
async function setOpenPanelFocus (fragment: string): Promise<void> {
const props = decodeURIComponent(fragment).split('|')
if (props.length >= 3) {
const doc = await client.findOne<Doc>(props[2] as Ref<Class<Doc>>, { _id: props[1] as Ref<Doc> })
if (doc !== undefined) {
const provider = ListSelectionProvider.Find(doc._id)
updateFocus({
provider,
focus: doc
})
openPanel(
props[0] as AnyComponent,
props[1],
props[2],
(props[3] ?? undefined) as PopupAlignment,
(props[4] ?? undefined) as AnyComponent
)
} else {
closePanel(false)
}
} else {
closePanel(false)
}
}
async function doSyncLoc (loc: Location): Promise<void> {
await syncLoc(loc)
await updateWindowTitle(loc)
}
async function updateSpace (spaceId?: Ref<Space>): Promise<void> {
if (spaceId === currentSpace) return
if (spaceId === undefined) return
const space = await client.findOne<Space>(core.class.Space, { _id: spaceId })
if (space === undefined) return
currentSpace = spaceId
const spaceClass = client.getHierarchy().getClass(space._class)
const view = client.getHierarchy().as(spaceClass, workbench.mixin.SpaceView)
currentView = view.view
}
async function updateWindowTitle (loc: Location): Promise<void> {
const ws = loc.path[1]
const docTitle = await getWindowTitle(loc)
if (docTitle !== undefined && docTitle !== '') {
document.title = ws == null ? docTitle : `${docTitle} - ${ws}`
} else {
const title = getMetadata(workbench.metadata.PlatformTitle) ?? 'Platform'
document.title = ws == null ? title : `${ws} - ${title}`
}
}
function closeAside (): void {
const loc = getLocation()
loc.path.length = 4
asideId = undefined
navigate(loc)
}
async function getWindowTitle (loc: Location): Promise<string | undefined> {
if (loc.fragment == null) return
const hierarchy = client.getHierarchy()
const [, _id, _class] = decodeURIComponent(loc.fragment).split('|')
if (_class == null) return
const mixin = hierarchy.classHierarchyMixin(_class as Ref<Class<Doc>>, view.mixin.ObjectTitle)
if (mixin === undefined) return
const titleProvider = await getResource(mixin.titleProvider)
try {
return await titleProvider(client, _id as Ref<Doc>)
} catch (err: any) {
Analytics.handleError(err)
console.error(err)
}
}
let aside: HTMLElement
let cover: HTMLElement
</script>
{#await load() then res}
{#if res}
<div class="workbench-container" style:flex-direction={'row'}>
<div class="workbench-container inner">
<div class="antiPanel-component antiComponent" bind:this={contentPanel}>
{#if currentApplication && currentApplication.component}
<Component is={currentApplication.component} props={{ currentSpace, visibleNav: false }} />
{:else if specialComponent}
<Component
is={specialComponent.component}
props={{
model: navigatorModel,
...specialComponent.componentProps,
currentSpace,
visibleNav: false
}}
/>
{:else if currentView?.component !== undefined}
<Component
is={currentView.component}
props={{ ...currentView.componentProps, currentView, visibleNav: false }}
/>
{:else}
<SpaceView {currentSpace} {currentView} />
{/if}
</div>
{#if asideId}
{@const asideComponent = navigatorModel?.aside ?? currentApplication?.aside}
{#if asideComponent !== undefined}
<Separator name={'workbench'} index={1} />
<div class="antiPanel-component antiComponent aside" bind:this={aside}>
<Component is={asideComponent} props={{ currentSpace, _id: asideId }} on:close={closeAside} />
</div>
{/if}
{/if}
</div>
</div>
<div bind:this={cover} class="cover" />
<TooltipInstance />
<PanelInstance bind:this={panelInstance} {contentPanel} readonly={$restrictionStore.readonly} embedded>
<svelte:fragment slot="panel-header">
<ActionContext context={{ mode: 'panel' }} />
</svelte:fragment>
</PanelInstance>
<Popup bind:this={popupInstance} {contentPanel}>
<svelte:fragment slot="popup-header">
<ActionContext context={{ mode: 'popup' }} />
</svelte:fragment>
</Popup>
{:else}
<div class="version-wrapper">
<div class="antiPopup version-popup">
<h1><Label label={guest.string.LinkWasRevoked} /></h1>
</div>
</div>
{/if}
{/await}
<style lang="scss">
.version-wrapper {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.version-popup {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
</style>

View File

@ -0,0 +1,59 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { getMetadata } from '@hcengineering/platform'
import { Label, Loading, Notifications, location } from '@hcengineering/ui'
import { connect, versionError } from '../connect'
import { guestId } from '@hcengineering/guest'
import workbench from '@hcengineering/workbench'
import Guest from './Guest.svelte'
</script>
{#if $location.path[0] === guestId}
{#await connect(getMetadata(workbench.metadata.PlatformTitle) ?? 'Platform')}
<Loading />
{:then client}
{#if !client && versionError}
<div class="version-wrapper">
<div class="antiPopup version-popup">
<h1><Label label={workbench.string.ServerUnderMaintenance} /></h1>
{versionError}
</div>
</div>
{:else if client}
<Notifications>
<Guest />
</Notifications>
{/if}
{:catch error}
<div>{error} -- {error.stack}</div>
{/await}
{/if}
<style lang="scss">
.version-wrapper {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.version-popup {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
</style>

View File

@ -0,0 +1,188 @@
import { Analytics } from '@hcengineering/analytics'
import client from '@hcengineering/client'
import core, {
ClientConnectEvent,
versionToString,
type AccountClient,
type Client,
type Version,
setCurrentAccount
} from '@hcengineering/core'
import login, { loginId } from '@hcengineering/login'
import { getMetadata, getResource, setMetadata } from '@hcengineering/platform'
import presentation, { closeClient, refreshClient, setClient } from '@hcengineering/presentation'
import { fetchMetadataLocalStorage, getCurrentLocation, navigate, setMetadataLocalStorage } from '@hcengineering/ui'
export let versionError: string | undefined = ''
let _token: string | undefined
let _client: AccountClient | undefined
let _clientSet: boolean = false
export async function connect (title: string): Promise<Client | undefined> {
const loc = getCurrentLocation()
const token = loc.query?.token
const ws = loc.path[1]
if (ws === undefined || token == null) {
navigate({
path: [loginId]
})
return
}
setMetadata(presentation.metadata.Token, token)
document.cookie =
encodeURIComponent(presentation.metadata.Token.replaceAll(':', '-')) + '=' + encodeURIComponent(token) + '; path=/'
const getEndpoint = await getResource(login.function.GetEndpoint)
const endpoint = await getEndpoint()
if (endpoint == null) {
navigate({
path: [loginId]
})
return
}
if (_token !== token && _client !== undefined) {
await _client.close()
_client = undefined
}
if (_client !== undefined) {
return _client
}
_token = token
let version: Version | undefined
let serverEndpoint = endpoint.replace(/^ws/g, 'http')
if (serverEndpoint.endsWith('/')) {
serverEndpoint = serverEndpoint.substring(0, serverEndpoint.length - 1)
}
const clientFactory = await getResource(client.function.GetClient)
_client = await clientFactory(
token,
endpoint,
() => {
location.reload()
},
() => {
clearMetadata(ws)
navigate({
path: [loginId],
query: {}
})
},
// We need to refresh all active live queries and clear old queries.
(event: ClientConnectEvent) => {
console.log('WorkbenchClient: onConnect', event)
try {
if ((_clientSet && event === ClientConnectEvent.Connected) || event === ClientConnectEvent.Refresh) {
void refreshClient()
}
if (event === ClientConnectEvent.Upgraded) {
window.location.reload()
}
void (async () => {
if (_client !== undefined) {
const newVersion = await _client.findOne<Version>(core.class.Version, {})
console.log('Reconnect Model version', newVersion)
const currentVersionStr = versionToString(version as Version)
const reconnectVersionStr = versionToString(newVersion as Version)
if (currentVersionStr !== reconnectVersionStr) {
// It seems upgrade happened
// location.reload()
versionError = `${currentVersionStr} != ${reconnectVersionStr}`
}
const serverVersion: { version: string } = await (
await fetch(serverEndpoint + '/api/v1/version', {})
).json()
console.log('Server version', serverVersion.version)
if (serverVersion.version !== '' && serverVersion.version !== currentVersionStr) {
versionError = `${currentVersionStr} => ${serverVersion.version}`
}
}
})()
} catch (err) {
console.error(err)
}
}
)
console.log('logging in as guest')
Analytics.handleEvent('GUEST LOGIN')
Analytics.setTag('workspace', ws)
const me = await _client?.getAccount()
if (me !== undefined) {
Analytics.setUser(me.email)
Analytics.setTag('workspace', ws)
console.log('login: employee account', me)
setCurrentAccount(me)
}
try {
version = await _client.findOne<Version>(core.class.Version, {})
console.log('Model version', version)
const requiredVersion = getMetadata(presentation.metadata.RequiredVersion)
if (requiredVersion !== undefined && version !== undefined) {
console.log('checking min model version', requiredVersion)
const versionStr = versionToString(version)
if (version === undefined || requiredVersion !== versionStr) {
versionError = `${versionStr} => ${requiredVersion}`
return undefined
}
}
try {
const serverVersion: { version: string } = await (await fetch(serverEndpoint + '/api/v1/version', {})).json()
console.log('Server version', serverVersion.version)
if (
serverVersion.version !== '' &&
(version === undefined || serverVersion.version !== versionToString(version))
) {
const versionStr = version !== undefined ? versionToString(version) : 'unknown'
versionError = `${versionStr} => ${serverVersion.version}`
return
}
} catch (err: any) {
versionError = 'server version not available'
return
}
} catch (err: any) {
console.log(err)
const requirdVersion = getMetadata(presentation.metadata.RequiredVersion)
console.log('checking min model version', requirdVersion)
if (requirdVersion !== undefined) {
versionError = `'unknown' => ${requirdVersion}`
return undefined
}
}
// Update window title
document.title = [ws, title].filter((it) => it).join(' - ')
_clientSet = true
await setClient(_client)
return _client
}
function clearMetadata (ws: string): void {
const tokens = fetchMetadataLocalStorage(login.metadata.LoginTokens)
if (tokens !== null) {
const loc = getCurrentLocation()
// eslint-disable-next-line
delete tokens[loc.path[1]]
setMetadataLocalStorage(login.metadata.LoginTokens, tokens)
}
setMetadata(presentation.metadata.Token, null)
setMetadataLocalStorage(login.metadata.LastToken, null)
document.cookie =
encodeURIComponent(presentation.metadata.Token.replaceAll(':', '-')) + '=' + encodeURIComponent('') + '; path=/'
setMetadataLocalStorage(login.metadata.LoginEndpoint, null)
setMetadataLocalStorage(login.metadata.LoginEmail, null)
void closeClient()
}

View File

@ -0,0 +1,10 @@
import { type Resources } from '@hcengineering/platform'
import CreatePublicLink from './components/CreatePublicLink.svelte'
import GuestApp from './components/GuestApp.svelte'
export default async (): Promise<Resources> => ({
component: {
GuestApp,
CreatePublicLink
}
})

View File

@ -0,0 +1,12 @@
import guest, { guestId } from '@hcengineering/guest'
import { type IntlString, mergeIds } from '@hcengineering/platform'
export default mergeIds(guestId, guest, {
string: {
CreatePublicLink: '' as IntlString,
PublicLink: '' as IntlString,
Revoke: '' as IntlString,
RevokeConfirmation: '' as IntlString,
LinkWasRevoked: '' as IntlString
}
})

View File

@ -0,0 +1,11 @@
export function decodeTokenPayload (token: string): any {
const parts = token.split('.')
const payload = parts[1]
const decodedPayload = atob(payload)
const parsedPayload = JSON.parse(decodedPayload)
return parsedPayload
}

View File

@ -0,0 +1,5 @@
const sveltePreprocess = require('svelte-preprocess')
module.exports = {
preprocess: sveltePreprocess()
};

View File

@ -0,0 +1,8 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/ui/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib"
}
}

View File

@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}

4
plugins/guest/.npmignore Normal file
View File

@ -0,0 +1,4 @@
*
!/lib/**
!CHANGELOG.md
/lib/**/__tests__/

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig"
}

View File

@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}

View File

@ -0,0 +1,44 @@
{
"name": "@hcengineering/guest",
"version": "0.6.0",
"main": "lib/index.js",
"svelte": "src/index.ts",
"types": "types/index.d.ts",
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"build:watch": "compile",
"format": "format src",
"test": "jest --passWithNoTests --silent",
"_phase:build": "compile transpile src",
"_phase:test": "jest --passWithNoTests --silent",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"prettier": "^3.1.0",
"typescript": "^5.3.3",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"prettier-plugin-svelte": "^3.1.0"
},
"dependencies": {
"@hcengineering/core": "^0.6.28",
"@hcengineering/platform": "^0.6.9",
"@hcengineering/ui": "^0.6.11"
},
"repository": "https://github.com/hcenginneing/anticrm",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}

View File

@ -0,0 +1,37 @@
import { Class, Doc, Ref, Space } from '@hcengineering/core'
import type { Asset, Plugin } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform'
import { AnyComponent, Location } from '@hcengineering/ui'
export interface PublicLink extends Doc {
attachedTo: Ref<Doc>
url: string
location: Location
restrictions: Restrictions
revokable: boolean
}
export interface Restrictions {
readonly: boolean
disableComments: boolean
disableNavigation: boolean
disableActions: boolean
}
export const guestAccountEmail = '#guest@hc.engineering'
export const guestId = 'guest' as Plugin
export default plugin(guestId, {
class: {
PublicLink: '' as Ref<Class<PublicLink>>
},
icon: {
Link: '' as Asset
},
space: {
Links: '' as Ref<Space>
},
component: {
GuestApp: '' as AnyComponent
}
})

View File

@ -0,0 +1,9 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"tsBuildInfoFile": ".build/build.tsbuildinfo"
}
}

View File

@ -27,7 +27,7 @@
getEventPositionElement, getEventPositionElement,
showPopup showPopup
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { Menu, openDoc } from '@hcengineering/view-resources' import { Menu, openDoc, showMenu } from '@hcengineering/view-resources'
import hr from '../plugin' import hr from '../plugin'
import { addMember } from '../utils' import { addMember } from '../utils'
import CreateDepartment from './CreateDepartment.svelte' import CreateDepartment from './CreateDepartment.svelte'
@ -76,10 +76,6 @@
showPopup(CreateDepartment, { space: value._id }, eventToHTMLElement(e)) showPopup(CreateDepartment, { space: value._id }, eventToHTMLElement(e))
} }
function showMenu (e: MouseEvent) {
showPopup(Menu, { object: value, baseMenuClass: value._class }, getEventPositionElement(e))
}
function edit (e: MouseEvent): void { function edit (e: MouseEvent): void {
openDoc(client.getHierarchy(), value) openDoc(client.getHierarchy(), value)
} }
@ -92,6 +88,10 @@
$: values = allEmployees.filter((it) => it.department === value._id && it._id !== dragPersonId) $: values = allEmployees.filter((it) => it.department === value._id && it._id !== dragPersonId)
$: dragging = value._id === dragOver?._id && dragPersonId !== undefined $: dragging = value._id === dragOver?._id && dragPersonId !== undefined
function onContext (e: MouseEvent): void {
showMenu(e, { object: value, baseMenuClass: value._class })
}
</script> </script>
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
@ -99,7 +99,7 @@
class="w-full mt-2 mb-4 container flex clear-mins flex-no-shrink" class="w-full mt-2 mb-4 container flex clear-mins flex-no-shrink"
class:cursor-pointer={currentDescendants.length} class:cursor-pointer={currentDescendants.length}
on:click|stopPropagation={edit} on:click|stopPropagation={edit}
on:contextmenu|preventDefault={showMenu} on:contextmenu={onContext}
class:dragging class:dragging
> >
<div <div

View File

@ -50,7 +50,7 @@
} }
</script> </script>
{#if object && type && type.label} {#if object && type?.label}
<Card <Card
label={hr.string.EditRequestType} label={hr.string.EditRequestType}
labelProps={{ type: type.label }} labelProps={{ type: type.label }}

Some files were not shown because too many files have changed in this diff Show More