Rework reviews (#1156)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-03-18 13:37:49 +07:00 committed by GitHub
parent c37d6b8e0e
commit a4af938871
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 1422 additions and 522 deletions

View File

@ -13,6 +13,9 @@ specifiers:
'@rush-temp/attachment': file:./projects/attachment.tgz '@rush-temp/attachment': file:./projects/attachment.tgz
'@rush-temp/attachment-assets': file:./projects/attachment-assets.tgz '@rush-temp/attachment-assets': file:./projects/attachment-assets.tgz
'@rush-temp/attachment-resources': file:./projects/attachment-resources.tgz '@rush-temp/attachment-resources': file:./projects/attachment-resources.tgz
'@rush-temp/calendar': file:./projects/calendar.tgz
'@rush-temp/calendar-assets': file:./projects/calendar-assets.tgz
'@rush-temp/calendar-resources': file:./projects/calendar-resources.tgz
'@rush-temp/chunter': file:./projects/chunter.tgz '@rush-temp/chunter': file:./projects/chunter.tgz
'@rush-temp/chunter-assets': file:./projects/chunter-assets.tgz '@rush-temp/chunter-assets': file:./projects/chunter-assets.tgz
'@rush-temp/chunter-resources': file:./projects/chunter-resources.tgz '@rush-temp/chunter-resources': file:./projects/chunter-resources.tgz
@ -50,6 +53,7 @@ specifiers:
'@rush-temp/model-activity': file:./projects/model-activity.tgz '@rush-temp/model-activity': file:./projects/model-activity.tgz
'@rush-temp/model-all': file:./projects/model-all.tgz '@rush-temp/model-all': file:./projects/model-all.tgz
'@rush-temp/model-attachment': file:./projects/model-attachment.tgz '@rush-temp/model-attachment': file:./projects/model-attachment.tgz
'@rush-temp/model-calendar': file:./projects/model-calendar.tgz
'@rush-temp/model-chunter': file:./projects/model-chunter.tgz '@rush-temp/model-chunter': file:./projects/model-chunter.tgz
'@rush-temp/model-contact': file:./projects/model-contact.tgz '@rush-temp/model-contact': file:./projects/model-contact.tgz
'@rush-temp/model-core': file:./projects/model-core.tgz '@rush-temp/model-core': file:./projects/model-core.tgz
@ -167,7 +171,6 @@ specifiers:
'@types/koa__cors': ^3.0.3 '@types/koa__cors': ^3.0.3
'@types/mime-types': ~2.1.1 '@types/mime-types': ~2.1.1
'@types/minio': ~7.0.11 '@types/minio': ~7.0.11
'@types/node': ~16.11.12
'@types/pdfkit': ~0.12.3 '@types/pdfkit': ~0.12.3
'@types/prosemirror-model': ~1.16.0 '@types/prosemirror-model': ~1.16.0
'@types/request': ~2.48.8 '@types/request': ~2.48.8
@ -257,6 +260,9 @@ dependencies:
'@rush-temp/attachment': file:projects/attachment.tgz '@rush-temp/attachment': file:projects/attachment.tgz
'@rush-temp/attachment-assets': file:projects/attachment-assets.tgz_typescript@4.5.4 '@rush-temp/attachment-assets': file:projects/attachment-assets.tgz_typescript@4.5.4
'@rush-temp/attachment-resources': file:projects/attachment-resources.tgz_096c09b0b673a57c275d9767a12070b1 '@rush-temp/attachment-resources': file:projects/attachment-resources.tgz_096c09b0b673a57c275d9767a12070b1
'@rush-temp/calendar': file:projects/calendar.tgz
'@rush-temp/calendar-assets': file:projects/calendar-assets.tgz_typescript@4.5.4
'@rush-temp/calendar-resources': file:projects/calendar-resources.tgz_096c09b0b673a57c275d9767a12070b1
'@rush-temp/chunter': file:projects/chunter.tgz '@rush-temp/chunter': file:projects/chunter.tgz
'@rush-temp/chunter-assets': file:projects/chunter-assets.tgz_typescript@4.5.4 '@rush-temp/chunter-assets': file:projects/chunter-assets.tgz_typescript@4.5.4
'@rush-temp/chunter-resources': file:projects/chunter-resources.tgz_096c09b0b673a57c275d9767a12070b1 '@rush-temp/chunter-resources': file:projects/chunter-resources.tgz_096c09b0b673a57c275d9767a12070b1
@ -269,11 +275,11 @@ dependencies:
'@rush-temp/core': file:projects/core.tgz '@rush-temp/core': file:projects/core.tgz
'@rush-temp/dev-account': file:projects/dev-account.tgz '@rush-temp/dev-account': file:projects/dev-account.tgz
'@rush-temp/dev-client-resources': file:projects/dev-client-resources.tgz '@rush-temp/dev-client-resources': file:projects/dev-client-resources.tgz
'@rush-temp/dev-server': file:projects/dev-server.tgz_@types+node@16.11.14 '@rush-temp/dev-server': file:projects/dev-server.tgz
'@rush-temp/dev-storage': file:projects/dev-storage.tgz '@rush-temp/dev-storage': file:projects/dev-storage.tgz
'@rush-temp/devmodel': file:projects/devmodel.tgz_typescript@4.5.4 '@rush-temp/devmodel': file:projects/devmodel.tgz_typescript@4.5.4
'@rush-temp/devmodel-resources': file:projects/devmodel-resources.tgz_6cae74ea501386c76c405ad0408e0339 '@rush-temp/devmodel-resources': file:projects/devmodel-resources.tgz_6cae74ea501386c76c405ad0408e0339
'@rush-temp/elastic': file:projects/elastic.tgz_@types+node@16.11.14 '@rush-temp/elastic': file:projects/elastic.tgz
'@rush-temp/front': file:projects/front.tgz '@rush-temp/front': file:projects/front.tgz
'@rush-temp/generator': file:projects/generator.tgz '@rush-temp/generator': file:projects/generator.tgz
'@rush-temp/gmail': file:projects/gmail.tgz '@rush-temp/gmail': file:projects/gmail.tgz
@ -294,6 +300,7 @@ dependencies:
'@rush-temp/model-activity': file:projects/model-activity.tgz_typescript@4.5.4 '@rush-temp/model-activity': file:projects/model-activity.tgz_typescript@4.5.4
'@rush-temp/model-all': file:projects/model-all.tgz_typescript@4.5.4 '@rush-temp/model-all': file:projects/model-all.tgz_typescript@4.5.4
'@rush-temp/model-attachment': file:projects/model-attachment.tgz_typescript@4.5.4 '@rush-temp/model-attachment': file:projects/model-attachment.tgz_typescript@4.5.4
'@rush-temp/model-calendar': file:projects/model-calendar.tgz_typescript@4.5.4
'@rush-temp/model-chunter': file:projects/model-chunter.tgz_typescript@4.5.4 '@rush-temp/model-chunter': file:projects/model-chunter.tgz_typescript@4.5.4
'@rush-temp/model-contact': file:projects/model-contact.tgz_typescript@4.5.4 '@rush-temp/model-contact': file:projects/model-contact.tgz_typescript@4.5.4
'@rush-temp/model-core': file:projects/model-core.tgz_typescript@4.5.4 '@rush-temp/model-core': file:projects/model-core.tgz_typescript@4.5.4
@ -411,7 +418,6 @@ dependencies:
'@types/koa__cors': 3.1.0 '@types/koa__cors': 3.1.0
'@types/mime-types': 2.1.1 '@types/mime-types': 2.1.1
'@types/minio': 7.0.11 '@types/minio': 7.0.11
'@types/node': 16.11.14
'@types/pdfkit': 0.12.3 '@types/pdfkit': 0.12.3
'@types/prosemirror-model': 1.16.0 '@types/prosemirror-model': 1.16.0
'@types/request': 2.48.8 '@types/request': 2.48.8
@ -478,7 +484,7 @@ dependencies:
svgo-loader: 3.0.0 svgo-loader: 3.0.0
toposort: 2.0.2 toposort: 2.0.2
ts-loader: 9.2.6_typescript@4.5.4+webpack@5.65.0 ts-loader: 9.2.6_typescript@4.5.4+webpack@5.65.0
ts-node: 10.5.0_5d12c2add188ff0e728b4ade3dacd39b ts-node: 10.5.0_typescript@4.5.4
typescript: 4.5.4 typescript: 4.5.4
uuid: 8.3.2 uuid: 8.3.2
webpack: 5.65.0_9def3870c80213359789f9191dbd286a webpack: 5.65.0_9def3870c80213359789f9191dbd286a
@ -8563,7 +8569,7 @@ packages:
dependencies: dependencies:
import-cwd: 3.0.0 import-cwd: 3.0.0
lilconfig: 2.0.4 lilconfig: 2.0.4
ts-node: 10.5.0_5d12c2add188ff0e728b4ade3dacd39b ts-node: 10.5.0_typescript@4.5.4
yaml: 1.10.2 yaml: 1.10.2
dev: false dev: false
@ -10452,6 +10458,36 @@ packages:
yn: 3.1.1 yn: 3.1.1
dev: false dev: false
/ts-node/10.5.0_typescript@4.5.4:
resolution: {integrity: sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==}
hasBin: true
peerDependencies:
'@swc/core': '>=1.2.50'
'@swc/wasm': '>=1.2.50'
'@types/node': '*'
typescript: '>=2.7'
peerDependenciesMeta:
'@swc/core':
optional: true
'@swc/wasm':
optional: true
dependencies:
'@cspotcode/source-map-support': 0.7.0
'@tsconfig/node10': 1.0.8
'@tsconfig/node12': 1.0.9
'@tsconfig/node14': 1.0.1
'@tsconfig/node16': 1.0.2
acorn: 8.6.0
acorn-walk: 8.2.0
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 4.5.4
v8-compile-cache-lib: 3.0.0
yn: 3.1.1
dev: false
/tsconfig-paths/3.12.0: /tsconfig-paths/3.12.0:
resolution: {integrity: sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==} resolution: {integrity: sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==}
dependencies: dependencies:
@ -11416,6 +11452,83 @@ packages:
- supports-color - supports-color
dev: false dev: false
file:projects/calendar-assets.tgz_typescript@4.5.4:
resolution: {integrity: sha512-muyoJ0EuKPUpsAEL1bLgdR90OqIoohgdfUFDwyWP1qmtAe9k1my76bJSIiU1JSfybyssHYiR8jpM/SuIonWg3Q==, tarball: file:projects/calendar-assets.tgz}
id: file:projects/calendar-assets.tgz
name: '@rush-temp/calendar-assets'
version: 0.0.0
dependencies:
'@rushstack/heft': 0.41.8
'@types/heft-jest': 1.0.2
'@types/node': 16.11.14
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
eslint: 7.32.0
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
eslint-plugin-import: 2.25.3_eslint@7.32.0
eslint-plugin-node: 11.1.0_eslint@7.32.0
eslint-plugin-promise: 5.2.0_eslint@7.32.0
prettier: 2.5.1
transitivePeerDependencies:
- supports-color
- typescript
dev: false
file:projects/calendar-resources.tgz_096c09b0b673a57c275d9767a12070b1:
resolution: {integrity: sha512-UZIMmHCGB+XoLDkyP1A/+ot4oc0POAPYCVOtiPI3vQtVWuTIPquMM4pPX3HxkvfpWqblXburuJx3j18rfA5Mgw==, tarball: file:projects/calendar-resources.tgz}
id: file:projects/calendar-resources.tgz
name: '@rush-temp/calendar-resources'
version: 0.0.0
dependencies:
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
eslint: 7.32.0
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
eslint-plugin-import: 2.25.3_eslint@7.32.0
eslint-plugin-node: 11.1.0_eslint@7.32.0
eslint-plugin-promise: 5.2.0_eslint@7.32.0
eslint-plugin-svelte3: 3.2.1_eslint@7.32.0+svelte@3.44.3
prettier: 2.5.1
prettier-plugin-svelte: 2.5.1_prettier@2.5.1+svelte@3.44.3
sass: 1.45.0
svelte: 3.44.3
svelte-check: 2.3.0_4374c622c67ed7479ff0e44c29d09bce
svelte-loader: 3.1.2_svelte@3.44.3
svelte-preprocess: 4.10.3_14d64cad431e31f100de7363af24a44f
typescript: 4.5.4
transitivePeerDependencies:
- '@babel/core'
- coffeescript
- less
- node-sass
- postcss
- postcss-load-config
- pug
- stylus
- sugarss
- supports-color
dev: false
file:projects/calendar.tgz:
resolution: {integrity: sha512-mN4GfaHpyGzuLV59eFw3c3R6p2r4ewEumsIhzyifgeXaXSD9Bnm06LG5LvFgF5G5TmDquUFcIq88w3Zzd20cQA==, tarball: file:projects/calendar.tgz}
name: '@rush-temp/calendar'
version: 0.0.0
dependencies:
'@rushstack/heft': 0.41.8
'@types/heft-jest': 1.0.2
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
eslint: 7.32.0
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
eslint-plugin-import: 2.25.3_eslint@7.32.0
eslint-plugin-node: 11.1.0_eslint@7.32.0
eslint-plugin-promise: 5.2.0_eslint@7.32.0
prettier: 2.5.1
typescript: 4.5.4
transitivePeerDependencies:
- supports-color
dev: false
file:projects/chunter-assets.tgz_typescript@4.5.4: file:projects/chunter-assets.tgz_typescript@4.5.4:
resolution: {integrity: sha512-2EQgdQ62vXKYpX9iOtOaCRt1GZliFiJpPPBZbOMHjaeWLye+/zYXlffkBbjVUSfR894cBlrtZBVRapjJU2T3uA==, tarball: file:projects/chunter-assets.tgz} resolution: {integrity: sha512-2EQgdQ62vXKYpX9iOtOaCRt1GZliFiJpPPBZbOMHjaeWLye+/zYXlffkBbjVUSfR894cBlrtZBVRapjJU2T3uA==, tarball: file:projects/chunter-assets.tgz}
id: file:projects/chunter-assets.tgz id: file:projects/chunter-assets.tgz
@ -11713,9 +11826,8 @@ packages:
- supports-color - supports-color
dev: false dev: false
file:projects/dev-server.tgz_@types+node@16.11.14: file:projects/dev-server.tgz:
resolution: {integrity: sha512-pv0DpcojPAreq3xXTnbl3Rj2UwEy97zRPmXU2lJsM0pKc6dVpmicD+/Bq4laIWx5qhvzfLurbwjBV4/0mLeuYw==, tarball: file:projects/dev-server.tgz} resolution: {integrity: sha512-pv0DpcojPAreq3xXTnbl3Rj2UwEy97zRPmXU2lJsM0pKc6dVpmicD+/Bq4laIWx5qhvzfLurbwjBV4/0mLeuYw==, tarball: file:projects/dev-server.tgz}
id: file:projects/dev-server.tgz
name: '@rush-temp/dev-server' name: '@rush-temp/dev-server'
version: 0.0.0 version: 0.0.0
dependencies: dependencies:
@ -11730,7 +11842,7 @@ packages:
eslint-plugin-promise: 5.2.0_eslint@7.32.0 eslint-plugin-promise: 5.2.0_eslint@7.32.0
jwt-simple: 0.5.6 jwt-simple: 0.5.6
prettier: 2.5.1 prettier: 2.5.1
ts-node: 10.5.0_5d12c2add188ff0e728b4ade3dacd39b ts-node: 10.5.0_typescript@4.5.4
typescript: 4.5.4 typescript: 4.5.4
transitivePeerDependencies: transitivePeerDependencies:
- '@swc/core' - '@swc/core'
@ -11815,9 +11927,8 @@ packages:
- typescript - typescript
dev: false dev: false
file:projects/elastic.tgz_@types+node@16.11.14: file:projects/elastic.tgz:
resolution: {integrity: sha512-CF8tie/n08Slnvw40pFZnaS4j/2JPQzs2xAxakNqpokb5x3ci9e0z2gzXNB6otQElk1NxW69QDHqNme+jHqWPA==, tarball: file:projects/elastic.tgz} resolution: {integrity: sha512-CF8tie/n08Slnvw40pFZnaS4j/2JPQzs2xAxakNqpokb5x3ci9e0z2gzXNB6otQElk1NxW69QDHqNme+jHqWPA==, tarball: file:projects/elastic.tgz}
id: file:projects/elastic.tgz
name: '@rush-temp/elastic' name: '@rush-temp/elastic'
version: 0.0.0 version: 0.0.0
dependencies: dependencies:
@ -11832,7 +11943,7 @@ packages:
eslint-plugin-node: 11.1.0_eslint@7.32.0 eslint-plugin-node: 11.1.0_eslint@7.32.0
eslint-plugin-promise: 5.2.0_eslint@7.32.0 eslint-plugin-promise: 5.2.0_eslint@7.32.0
prettier: 2.5.1 prettier: 2.5.1
ts-node: 10.5.0_5d12c2add188ff0e728b4ade3dacd39b ts-node: 10.5.0_typescript@4.5.4
typescript: 4.5.4 typescript: 4.5.4
transitivePeerDependencies: transitivePeerDependencies:
- '@swc/core' - '@swc/core'
@ -12305,7 +12416,7 @@ packages:
dev: false dev: false
file:projects/model-all.tgz_typescript@4.5.4: file:projects/model-all.tgz_typescript@4.5.4:
resolution: {integrity: sha512-Jll+Wp4p6/OjdvtbTqAGKbMUkOoAai5Se2+CSIOteRB6dDTgtlwy95fzQv2o5RHIBBiQXLQ2Csb8KgAluzTJ6w==, tarball: file:projects/model-all.tgz} resolution: {integrity: sha512-LprNHSPgySvlj/zl9bkeSZ7Uh1wWyPPkQBW6knjBBD3cizpFpxPGBrSQnhDe/RP0uj6azKRYWWUcAPFOYe4TvQ==, 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
@ -12350,6 +12461,27 @@ packages:
- typescript - typescript
dev: false dev: false
file:projects/model-calendar.tgz_typescript@4.5.4:
resolution: {integrity: sha512-sI97CjbxdgK1qWGDrBViWm65NY+vwFzt1s94cGheRJP8CL/GqJNuaSyrJv0pqjfZgK6Z3IkcUq2ZUSSHwD+1gQ==, tarball: file:projects/model-calendar.tgz}
id: file:projects/model-calendar.tgz
name: '@rush-temp/model-calendar'
version: 0.0.0
dependencies:
'@rushstack/heft': 0.41.8
'@types/heft-jest': 1.0.2
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
eslint: 7.32.0
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
eslint-plugin-import: 2.25.3_eslint@7.32.0
eslint-plugin-node: 11.1.0_eslint@7.32.0
eslint-plugin-promise: 5.2.0_eslint@7.32.0
prettier: 2.5.1
transitivePeerDependencies:
- supports-color
- typescript
dev: false
file:projects/model-chunter.tgz_typescript@4.5.4: file:projects/model-chunter.tgz_typescript@4.5.4:
resolution: {integrity: sha512-bbKuovYnIkpRkU0YhE2x6UEx2gvwKM/OkCj7SH4rQhVR3vjdWisBd0/P71NsNdfcdR7Hl4fhHSNVZfW6KNNPog==, tarball: file:projects/model-chunter.tgz} resolution: {integrity: sha512-bbKuovYnIkpRkU0YhE2x6UEx2gvwKM/OkCj7SH4rQhVR3vjdWisBd0/P71NsNdfcdR7Hl4fhHSNVZfW6KNNPog==, tarball: file:projects/model-chunter.tgz}
id: file:projects/model-chunter.tgz id: file:projects/model-chunter.tgz
@ -12540,7 +12672,7 @@ packages:
dev: false dev: false
file:projects/model-recruit.tgz_typescript@4.5.4: file:projects/model-recruit.tgz_typescript@4.5.4:
resolution: {integrity: sha512-t2mSCyFnB+YfsxFuAu0J8hNVhM+GP5SjiVevMe8tft+dCyyLeMpAJxTdEKkvRx6jV6yyxyPUmqZMAQCmm/pvuA==, tarball: file:projects/model-recruit.tgz} resolution: {integrity: sha512-TtzOJVyFh5KY3RmQUTUUqeemhDDo/14R1RRrbQ7FoYun9yl+lv5b00OUuIbZ4rtYSJQLhiJaXFbZbxHl6pr3mA==, tarball: file:projects/model-recruit.tgz}
id: file:projects/model-recruit.tgz id: file:projects/model-recruit.tgz
name: '@rush-temp/model-recruit' name: '@rush-temp/model-recruit'
version: 0.0.0 version: 0.0.0
@ -13235,7 +13367,7 @@ packages:
dev: false dev: false
file:projects/prod.tgz_a07ec81d4d975778878ca12202ea119e: file:projects/prod.tgz_a07ec81d4d975778878ca12202ea119e:
resolution: {integrity: sha512-R0Y2btm9DPFvRRGYAGC6ilCIcE5t+p2trcdARVBxyi+tH4XwcXZSt0NJJe+57vpiwkCV8hYPa2fI+Eul1wdrpQ==, tarball: file:projects/prod.tgz} resolution: {integrity: sha512-mxkGGtPp108JHMkmnEgKKPNVrihu3xVv1wZqY/HTWhZ+v8Ct5RG92+Yg6NBhEsLmqCBBPTAHY/JapGi1oF9bWg==, 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
@ -13329,7 +13461,7 @@ packages:
dev: false dev: false
file:projects/recruit-resources.tgz_096c09b0b673a57c275d9767a12070b1: file:projects/recruit-resources.tgz_096c09b0b673a57c275d9767a12070b1:
resolution: {integrity: sha512-EexKsB2ZRQ/VxlTABy5b5g3MLR51+549IMWW4g6+Xb/ZR6MZKCT6ocWPRgULW64EO3CTOJ37nIlXijj6Z/fPKw==, tarball: file:projects/recruit-resources.tgz} resolution: {integrity: sha512-nvxkMBBQ9fPzCtIIQbUOlLaV+4wulVAKLg2iBOR5RuEiKZK/kwItpbzv0u6mS5Vb7/yFRp0GsRM+G7rv1tiGfg==, tarball: file:projects/recruit-resources.tgz}
id: file:projects/recruit-resources.tgz id: file:projects/recruit-resources.tgz
name: '@rush-temp/recruit-resources' name: '@rush-temp/recruit-resources'
version: 0.0.0 version: 0.0.0
@ -13366,7 +13498,7 @@ packages:
dev: false dev: false
file:projects/recruit.tgz: file:projects/recruit.tgz:
resolution: {integrity: sha512-wAgHbMEy+KVBNhYTiPdcBnN0O7BAsfxLMxK/gcjM1pkqeLbCzJ/SpRNMFpC2K0/TU+qsPqCzK3zx0VSvFXH05A==, tarball: file:projects/recruit.tgz} resolution: {integrity: sha512-VAcfan3RLB0bq8DYryIS1waf0EiSSjbd1mdyq0IqMvlp9b8OBUGf0mOCfHDhkno9C4nf9fN8kCUDk9y82LfjZg==, tarball: file:projects/recruit.tgz}
name: '@rush-temp/recruit' name: '@rush-temp/recruit'
version: 0.0.0 version: 0.0.0
dependencies: dependencies:

View File

@ -82,7 +82,7 @@ services:
- mongodb - mongodb
- elastic - elastic
- minio - minio
- apm-server # - apm-server
ports: ports:
- 3333:3333 - 3333:3333
environment: environment:
@ -94,41 +94,41 @@ services:
- MINIO_ENDPOINT=minio - MINIO_ENDPOINT=minio
- MINIO_ACCESS_KEY=minioadmin - MINIO_ACCESS_KEY=minioadmin
- MINIO_SECRET_KEY=minioadmin - MINIO_SECRET_KEY=minioadmin
- APM_SERVER_URL=http://apm-server:8200 # - APM_SERVER_URL=http://apm-server:8200
apm-server: # apm-server:
image: docker.elastic.co/apm/apm-server:7.14.2 # image: docker.elastic.co/apm/apm-server:7.14.2
depends_on: # depends_on:
- "elastic" # - "elastic"
- "kibana" # - "kibana"
cap_add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"] # cap_add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"]
cap_drop: ["ALL"] # cap_drop: ["ALL"]
ports: # ports:
- 8200:8200 # - 8200:8200
command: | # command: |
apm-server -e # apm-server -e
-E apm-server.rum.enabled=true # -E apm-server.rum.enabled=true
-E setup.kibana.host=kibana:5601 # -E setup.kibana.host=kibana:5601
-E setup.template.settings.index.number_of_replicas=0 # -E setup.template.settings.index.number_of_replicas=0
-E apm-server.kibana.enabled=true # -E apm-server.kibana.enabled=true
-E apm-server.kibana.host=kibana:5601 # -E apm-server.kibana.host=kibana:5601
-E output.elasticsearch.hosts=["elastic:9200"] # -E output.elasticsearch.hosts=["elastic:9200"]
healthcheck: # healthcheck:
interval: 10s # interval: 10s
retries: 12 # retries: 12
test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:8200/ # test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:8200/
kibana: # kibana:
image: docker.elastic.co/kibana/kibana:7.14.2 # image: docker.elastic.co/kibana/kibana:7.14.2
depends_on: # depends_on:
- "elastic" # - "elastic"
environment: # environment:
ELASTICSEARCH_URL: http://elastic:9200 # ELASTICSEARCH_URL: http://elastic:9200
ELASTICSEARCH_HOSTS: http://elastic:9200 # ELASTICSEARCH_HOSTS: http://elastic:9200
ports: # ports:
- 5601:5601 # - 5601:5601
healthcheck: # healthcheck:
interval: 10s # interval: 10s
retries: 20 # retries: 20
test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:5601/api/status # test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:5601/api/status
volumes: volumes:
db: db:

View File

@ -119,6 +119,9 @@
"@anticrm/server-recruit": "~0.6.0", "@anticrm/server-recruit": "~0.6.0",
"@anticrm/server-recruit-resources": "~0.6.0", "@anticrm/server-recruit-resources": "~0.6.0",
"@anticrm/server-task": "~0.6.0", "@anticrm/server-task": "~0.6.0",
"@anticrm/server-task-resources": "~0.6.0" "@anticrm/server-task-resources": "~0.6.0",
"@anticrm/calendar": "~0.6.0",
"@anticrm/calendar-assets": "~0.6.0",
"@anticrm/calendar-resources": "~0.6.0"
} }
} }

View File

@ -35,6 +35,7 @@ import { inventoryId } from '@anticrm/inventory'
import { templatesId } from '@anticrm/templates' import { templatesId } from '@anticrm/templates'
import { notificationId } from '@anticrm/notification' import { notificationId } from '@anticrm/notification'
import { tagsId } from '@anticrm/tags' import { tagsId } from '@anticrm/tags'
import { calendarId } from '@anticrm/calendar'
import rekoni from '@anticrm/rekoni' import rekoni from '@anticrm/rekoni'
import '@anticrm/login-assets' import '@anticrm/login-assets'
@ -54,6 +55,7 @@ import '@anticrm/inventory-assets'
import '@anticrm/templates-assets' import '@anticrm/templates-assets'
import '@anticrm/notification-assets' import '@anticrm/notification-assets'
import '@anticrm/tags-assets' import '@anticrm/tags-assets'
import '@anticrm/calendar-assets'
import { setMetadata } from '@anticrm/platform' import { setMetadata } from '@anticrm/platform'
export async function configurePlatform() { export async function configurePlatform() {
@ -95,4 +97,5 @@ export async function configurePlatform() {
addLocation(templatesId, () => import(/* webpackChunkName: "templates" */ '@anticrm/templates-resources')) addLocation(templatesId, () => import(/* webpackChunkName: "templates" */ '@anticrm/templates-resources'))
addLocation(notificationId, () => import(/* webpackChunkName: "notification" */ '@anticrm/notification-resources')) addLocation(notificationId, () => import(/* webpackChunkName: "notification" */ '@anticrm/notification-resources'))
addLocation(tagsId, () => import(/* webpackChunkName: "tags" */ '@anticrm/tags-resources')) addLocation(tagsId, () => import(/* webpackChunkName: "tags" */ '@anticrm/tags-resources'))
addLocation(calendarId, () => import(/* webpackChunkName: "calendar" */ '@anticrm/calendar-resources'))
} }

View File

@ -61,6 +61,7 @@
"@anticrm/model-notification": "~0.6.0", "@anticrm/model-notification": "~0.6.0",
"@anticrm/model-text-editor": "~0.6.0", "@anticrm/model-text-editor": "~0.6.0",
"@anticrm/core": "~0.6.16", "@anticrm/core": "~0.6.16",
"@anticrm/model-tags": "~0.6.0" "@anticrm/model-tags": "~0.6.0",
"@anticrm/model-calendar": "~0.6.0"
} }
} }

View File

@ -47,6 +47,7 @@ import { createModel as viewModel } from '@anticrm/model-view'
import { createModel as workbenchModel } from '@anticrm/model-workbench' import { createModel as workbenchModel } from '@anticrm/model-workbench'
import { createModel as notificationModel } from '@anticrm/model-notification' import { createModel as notificationModel } from '@anticrm/model-notification'
import { createModel as tagsModel } from '@anticrm/model-tags' import { createModel as tagsModel } from '@anticrm/model-tags'
import { createModel as calendarModel } from '@anticrm/model-calendar'
export const version: Data<Version> = jsonVersion as Data<Version> export const version: Data<Version> = jsonVersion as Data<Version>
@ -77,6 +78,7 @@ const builders = [
serverNotificationModel, serverNotificationModel,
serveSettingModel, serveSettingModel,
tagsModel, tagsModel,
calendarModel,
serverChunterModel, serverChunterModel,
serverInventoryModel, serverInventoryModel,
serverLeadModel, serverLeadModel,

View File

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

View File

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

View File

@ -0,0 +1,18 @@
// The "rig.json" file directs tools to look for their config files in an external package.
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
/**
* (Required) The name of the rig package to inherit from.
* It should be an NPM package name with the "-rig" suffix.
*/
"rigPackageName": "@anticrm/model-rig"
/**
* (Optional) Selects a config profile from the rig package. The name must consist of
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
* If omitted, then the "default" profile will be used."
*/
// "rigProfile": "your-profile-name"
}

View File

@ -0,0 +1,46 @@
{
"name": "@anticrm/model-calendar",
"version": "0.6.0",
"main": "lib/index.js",
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "heft build",
"build:watch": "tsc",
"lint:fix": "eslint --fix src",
"lint": "eslint src",
"format": "prettier --write src && eslint --fix src"
},
"devDependencies": {
"@anticrm/model-rig": "~0.6.0",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-node": "^11.1.0",
"eslint": "^7.32.0",
"@types/heft-jest": "^1.0.2",
"@typescript-eslint/parser": "^5.4.0",
"eslint-config-standard-with-typescript": "^21.0.1",
"prettier": "^2.4.1",
"@rushstack/heft": "^0.41.1"
},
"dependencies": {
"@anticrm/core": "~0.6.11",
"@anticrm/model": "~0.6.0",
"@anticrm/ui": "~0.6.0",
"@anticrm/view": "~0.6.0",
"@anticrm/model-attachment": "~0.6.0",
"@anticrm/model-task": "~0.6.0",
"@anticrm/calendar": "~0.6.0",
"@anticrm/calendar-resources": "~0.6.0",
"@anticrm/platform": "~0.6.5",
"@anticrm/model-core": "~0.6.0",
"@anticrm/model-view": "~0.6.0",
"@anticrm/model-workbench": "~0.6.1",
"@anticrm/activity": "~0.6.0",
"@anticrm/workbench": "~0.6.1",
"@anticrm/model-chunter": "~0.6.0",
"@anticrm/model-contact": "~0.6.1",
"@anticrm/contact": "~0.6.5"
}
}

View File

@ -0,0 +1,90 @@
//
// Copyright © 2020, 2021 Anticrm Platform Contributors.
//
// 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 { Calendar, Event } from '@anticrm/calendar'
import { Employee } from '@anticrm/contact'
import type { Domain, Markup, Ref, Timestamp } from '@anticrm/core'
import { IndexKind } from '@anticrm/core'
import { Builder, Collection, Index, Model, Prop, TypeDate, TypeMarkup, TypeString, UX } from '@anticrm/model'
import attachment from '@anticrm/model-attachment'
import chunter from '@anticrm/model-chunter'
import contact from '@anticrm/model-contact'
import core, { TAttachedDoc } from '@anticrm/model-core'
import { TSpaceWithStates } from '@anticrm/model-task'
import workbench from '@anticrm/model-workbench'
import calendar from './plugin'
export * from '@anticrm/calendar'
export const DOMAIN_CALENDAR = 'calendar' as Domain
@Model(calendar.class.Calendar, core.class.Space)
@UX(calendar.string.Calendar, calendar.icon.Calendar)
export class TCalendar extends TSpaceWithStates implements Calendar {}
@Model(calendar.class.Event, core.class.AttachedDoc, DOMAIN_CALENDAR)
export class TEvent extends TAttachedDoc implements Event {
@Prop(TypeString(), calendar.string.Title)
@Index(IndexKind.FullText)
title!: string
@Prop(TypeString(), calendar.string.EventNumber)
number!: number
@Prop(TypeMarkup(), calendar.string.Description)
@Index(IndexKind.FullText)
description!: Markup
@Prop(TypeString(), calendar.string.Location, calendar.icon.Location)
@Index(IndexKind.FullText)
location?: string
@Prop(TypeDate(true), calendar.string.Date)
date!: Timestamp
@Prop(TypeDate(true), calendar.string.DueTo)
dueDate!: Timestamp
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number
@Prop(Collection(contact.class.Employee), calendar.string.Participants)
participants!: Ref<Employee>[]
}
export function createModel (builder: Builder): void {
builder.createModel(TCalendar, TEvent)
builder.createDoc(workbench.class.Application, core.space.Model, {
label: calendar.string.ApplicationLabelCalendar,
icon: calendar.icon.Calendar,
hidden: true,
navigatorModel: {
spaces: [
{
label: calendar.string.Calendars,
spaceClass: calendar.class.Calendar,
addSpaceLabel: calendar.string.CreateCalendar,
createComponent: calendar.component.CreateCalendar
}
]
}
}, calendar.app.Calendar)
}
export default calendar

View File

@ -0,0 +1,31 @@
//
// Copyright © 2020 Anticrm Platform Contributors.
//
// 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 { calendarId } from '@anticrm/calendar'
import calendar from '@anticrm/calendar-resources/src/plugin'
import type { IntlString } from '@anticrm/platform'
import { mergeIds } from '@anticrm/platform'
import { AnyComponent } from '@anticrm/ui'
export default mergeIds(calendarId, calendar, {
component: {
CreateCalendar: '' as AnyComponent
},
string: {
ApplicationLabelCalendar: '' as IntlString
},
space: {
}
})

View File

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

View File

@ -25,6 +25,7 @@ export default mergeIds(coreId, core, {
Name: '' as IntlString, Name: '' as IntlString,
Description: '' as IntlString, Description: '' as IntlString,
Private: '' as IntlString, Private: '' as IntlString,
Archived: '' as IntlString Archived: '' as IntlString,
ClassLabel: '' as IntlString
} }
}) })

View File

@ -24,7 +24,7 @@ import {
Timestamp, Timestamp,
Type, Version Type, Version
} from '@anticrm/core' } from '@anticrm/core'
import { Index, Model, Prop, TypeRef, TypeString, TypeTimestamp } from '@anticrm/model' import { Index, Model, Prop, TypeIntlString, TypeRef, TypeString, TypeTimestamp } from '@anticrm/model'
import type { IntlString } from '@anticrm/platform' import type { IntlString } from '@anticrm/platform'
import core from './component' import core from './component'
@ -70,7 +70,10 @@ export class TAttachedDoc extends TDoc implements AttachedDoc {
@Model(core.class.Class, core.class.Doc, DOMAIN_MODEL) @Model(core.class.Class, core.class.Doc, DOMAIN_MODEL)
export class TClass extends TDoc implements Class<Obj> { export class TClass extends TDoc implements Class<Obj> {
kind!: ClassifierKind kind!: ClassifierKind
@Prop(TypeIntlString(), core.string.ClassLabel)
label!: IntlString label!: IntlString
extends!: Ref<Class<Obj>> extends!: Ref<Class<Obj>>
domain!: Domain domain!: Domain
} }
@ -101,6 +104,9 @@ export class TType extends TObj implements Type<any> {
@Model(core.class.TypeString, core.class.Type) @Model(core.class.TypeString, core.class.Type)
export class TTypeString extends TType {} export class TTypeString extends TType {}
@Model(core.class.TypeIntlString, core.class.Type)
export class TTypeIntlString extends TType {}
@Model(core.class.TypeNumber, core.class.Type) @Model(core.class.TypeNumber, core.class.Type)
export class TTypeNumber extends TType {} export class TTypeNumber extends TType {}

View File

@ -28,7 +28,7 @@ import {
TRefTo, TRefTo,
TType, TType,
TTypeBoolean, TTypeBoolean,
TTypeDate, TTypeMarkup, TTypeNumber, TTypeString, TTypeTimestamp, TTypeDate, TTypeIntlString, TTypeMarkup, TTypeNumber, TTypeString, TTypeTimestamp,
TVersion TVersion
} from './core' } from './core'
import { TAccount, TSpace } from './security' import { TAccount, TSpace } from './security'
@ -80,6 +80,7 @@ export function createModel (builder: Builder): void {
TTypeDate, TTypeDate,
TArrOf, TArrOf,
TVersion, TVersion,
TTypeNumber TTypeNumber,
TTypeIntlString
) )
} }

View File

@ -44,6 +44,7 @@
"@anticrm/model-task": "~0.6.0", "@anticrm/model-task": "~0.6.0",
"@anticrm/workbench": "~0.6.1", "@anticrm/workbench": "~0.6.1",
"@anticrm/model-presentation": "~0.6.0", "@anticrm/model-presentation": "~0.6.0",
"@anticrm/model-calendar": "~0.6.0",
"@anticrm/model-tags": "~0.6.0", "@anticrm/model-tags": "~0.6.0",
"@anticrm/skillset": "^0.6.0" "@anticrm/skillset": "^0.6.0"
} }

View File

@ -303,6 +303,10 @@ export function createModel (builder: Builder): void {
presenter: recruit.component.VacancyPresenter presenter: recruit.component.VacancyPresenter
}) })
builder.mixin(recruit.class.ReviewCategory, core.class.Class, view.mixin.AttributePresenter, {
presenter: recruit.component.ReviewCategoryPresenter
})
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.ObjectValidator, { builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.ObjectValidator, {
validator: recruit.validator.ApplicantValidator validator: recruit.validator.ApplicantValidator
}) })
@ -366,6 +370,14 @@ export function createModel (builder: Builder): void {
} }
}) })
builder.createDoc(view.class.ActionTarget, core.space.Model, {
target: recruit.class.ReviewCategory,
action: task.action.UnarchiveSpace,
query: {
archived: true
}
})
builder.createDoc( builder.createDoc(
view.class.Action, view.class.Action,
core.space.Model, core.space.Model,

View File

@ -17,6 +17,7 @@ import { Person } from '@anticrm/contact'
import core, { AttachedDoc, Class, Doc, DocumentQuery, DOMAIN_TX, MixinData, Ref, TxCollectionCUD, TxCreateDoc, TxMixin, TxOperations, TxUpdateDoc } from '@anticrm/core' import core, { AttachedDoc, Class, Doc, DocumentQuery, DOMAIN_TX, MixinData, Ref, TxCollectionCUD, TxCreateDoc, TxMixin, TxOperations, TxUpdateDoc } from '@anticrm/core'
import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model' import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
import { DOMAIN_ATTACHMENT } from '@anticrm/model-attachment' import { DOMAIN_ATTACHMENT } from '@anticrm/model-attachment'
import { DOMAIN_CALENDAR } from '@anticrm/model-calendar'
import { DOMAIN_COMMENT } from '@anticrm/model-chunter' import { DOMAIN_COMMENT } from '@anticrm/model-chunter'
import contact, { DOMAIN_CONTACT } from '@anticrm/model-contact' import contact, { DOMAIN_CONTACT } from '@anticrm/model-contact'
import tags, { DOMAIN_TAGS, TagCategory, TagElement } from '@anticrm/model-tags' import tags, { DOMAIN_TAGS, TagCategory, TagElement } from '@anticrm/model-tags'
@ -157,6 +158,25 @@ export const recruitOperation: MigrateOperation = {
}) })
} }
} }
// Migrate reviews
await client.update(DOMAIN_TASK, {
_class: recruit.class.Review
}, {
$rename: {
startDate: 'date'
}
})
await client.update(DOMAIN_TASK, {
_class: recruit.class.Review,
title: { $exists: false }
}, {
title: ''
})
await client.move(DOMAIN_TASK, { _class: recruit.class.Review }, DOMAIN_CALENDAR)
}, },
async upgrade (client: MigrationUpgradeClient): Promise<void> { async upgrade (client: MigrationUpgradeClient): Promise<void> {
const tx = new TxOperations(client, core.account.System) const tx = new TxOperations(client, core.account.System)

View File

@ -61,6 +61,7 @@ export default mergeIds(recruitId, recruit, {
ApplicationPresenter: '' as AnyComponent, ApplicationPresenter: '' as AnyComponent,
ApplicationsPresenter: '' as AnyComponent, ApplicationsPresenter: '' as AnyComponent,
VacancyPresenter: '' as AnyComponent, VacancyPresenter: '' as AnyComponent,
ReviewCategoryPresenter: '' as AnyComponent,
EditApplication: '' as AnyComponent, EditApplication: '' as AnyComponent,
TemplatesIcon: '' as AnyComponent, TemplatesIcon: '' as AnyComponent,
Applications: '' as AnyComponent, Applications: '' as AnyComponent,

View File

@ -1,67 +1,38 @@
import { Employee, Organization } from '@anticrm/contact' import { Organization } from '@anticrm/contact'
import { Domain, IndexKind, Ref, Timestamp } from '@anticrm/core' import { Domain, IndexKind, Ref } from '@anticrm/core'
import { Collection, Index, Model, Prop, TypeDate, TypeMarkup, TypeRef, TypeString, UX } from '@anticrm/model' import { Collection, Index, Model, Prop, TypeMarkup, TypeRef, TypeString, UX } from '@anticrm/model'
import attachment from '@anticrm/model-attachment' import attachment from '@anticrm/model-attachment'
import calendar, { TEvent } from '@anticrm/model-calendar'
import chunter from '@anticrm/model-chunter' import chunter from '@anticrm/model-chunter'
import contact from '@anticrm/model-contact' import contact from '@anticrm/model-contact'
import core, { TAttachedDoc } from '@anticrm/model-core' import core, { TAttachedDoc, TSpace } from '@anticrm/model-core'
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task' import task from '@anticrm/model-task'
import { Candidate, Opinion, Review, ReviewCategory } from '@anticrm/recruit' import { Candidate, Opinion, Review, ReviewCategory } from '@anticrm/recruit'
import recruit from './plugin' import recruit from './plugin'
@Model(recruit.class.ReviewCategory, task.class.SpaceWithStates) @Model(recruit.class.ReviewCategory, core.class.Space)
@UX(recruit.string.ReviewCategory, recruit.icon.Review) @UX(recruit.string.ReviewCategory, recruit.icon.Review)
export class TReviewCategory extends TSpaceWithStates implements ReviewCategory { export class TReviewCategory extends TSpace implements ReviewCategory {
@Prop(TypeString(), recruit.string.FullDescription) @Prop(TypeString(), recruit.string.FullDescription)
fullDescription?: string fullDescription?: string
}
@Model(recruit.class.Review, calendar.class.Event)
@UX(recruit.string.Review, recruit.icon.Review, recruit.string.ReviewShortLabel, 'number')
export class TReview extends TEvent implements Review {
// We need to declare, to provide property with label
@Prop(TypeRef(recruit.mixin.Candidate), recruit.string.Candidate)
declare attachedTo: Ref<Candidate>
@Prop(TypeString(), recruit.string.Verdict)
@Index(IndexKind.FullText)
verdict!: string
@Prop(TypeRef(contact.class.Organization), recruit.string.Company, contact.icon.Company) @Prop(TypeRef(contact.class.Organization), recruit.string.Company, contact.icon.Company)
company?: Ref<Organization> company?: Ref<Organization>
}
@Model(recruit.class.Review, task.class.Task)
@UX(recruit.string.Review, recruit.icon.Review, recruit.string.ReviewShortLabel, 'number')
export class TReview extends TTask implements Review {
// We need to declare, to provide property with label
@Prop(TypeRef(recruit.class.Applicant), recruit.string.Candidate)
declare attachedTo: Ref<Candidate>
@Prop(TypeRef(contact.class.Employee), recruit.string.AssignedRecruiter)
declare assignee: Ref<Employee> | null
@Prop(TypeMarkup(), recruit.string.Description)
@Index(IndexKind.FullText)
description!: string
@Index(IndexKind.FullText)
@Prop(TypeMarkup(), recruit.string.Verdict)
verdict!: string
@Index(IndexKind.FullText)
@Prop(TypeString(), recruit.string.Location, recruit.icon.Location)
location?: string
@Index(IndexKind.FullText)
@Prop(TypeString(), recruit.string.Company, contact.icon.Company)
company?: string
@Prop(TypeDate(), recruit.string.StartDate)
startDate!: Timestamp | null
@Prop(TypeDate(), recruit.string.DueDate)
dueDate!: Timestamp | null
@Prop(Collection(recruit.class.Opinion), recruit.string.Opinions) @Prop(Collection(recruit.class.Opinion), recruit.string.Opinions)
opinions?: number opinions?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number
@Prop(Collection(contact.class.Employee), recruit.string.Participants)
participants!: Ref<Employee>[]
} }
@Model(recruit.class.Opinion, core.class.AttachedDoc, 'recruit' as Domain) @Model(recruit.class.Opinion, core.class.AttachedDoc, 'recruit' as Domain)

View File

@ -6,6 +6,7 @@ import task from '@anticrm/model-task'
import view from '@anticrm/model-view' import view from '@anticrm/model-view'
import workbench from '@anticrm/model-workbench' import workbench from '@anticrm/model-workbench'
import recruit from './plugin' import recruit from './plugin'
import calendar from '@anticrm/model-calendar'
export function createReviewModel (builder: Builder): void { export function createReviewModel (builder: Builder): void {
builder.mixin(recruit.class.ReviewCategory, core.class.Class, workbench.mixin.SpaceView, { builder.mixin(recruit.class.ReviewCategory, core.class.Class, workbench.mixin.SpaceView, {
@ -21,11 +22,21 @@ export function createReviewModel (builder: Builder): void {
}) })
createTableViewlet(builder) createTableViewlet(builder)
createKanbanViewlet(builder)
createStatusTableViewlet(builder)
builder.mixin(recruit.class.Review, core.class.Class, task.mixin.KanbanCard, { builder.createDoc(
card: recruit.component.KanbanReviewCard view.class.Action,
core.space.Model,
{
label: recruit.string.CreateOpinion,
icon: recruit.icon.Create,
action: recruit.actionImpl.CreateOpinion
},
recruit.action.CreateOpinion
)
builder.createDoc(view.class.ActionTarget, core.space.Model, {
target: recruit.class.Review,
action: recruit.action.CreateOpinion
}) })
builder.mixin(recruit.class.Review, core.class.Class, view.mixin.ObjectEditor, { builder.mixin(recruit.class.Review, core.class.Class, view.mixin.ObjectEditor, {
@ -63,17 +74,6 @@ export function createReviewModel (builder: Builder): void {
action: recruit.action.CreateReview action: recruit.action.CreateReview
}) })
builder.createDoc(
task.class.KanbanTemplateSpace,
core.space.Model,
{
name: recruit.string.ReviewCategory,
description: task.string.ManageStatusesWithin,
icon: recruit.component.TemplatesIcon
},
recruit.space.ReviewTemplates
)
builder.createDoc(view.class.ActionTarget, core.space.Model, { builder.createDoc(view.class.ActionTarget, core.space.Model, {
target: recruit.class.ReviewCategory, target: recruit.class.ReviewCategory,
action: task.action.ArchiveSpace, action: task.action.ArchiveSpace,
@ -82,69 +82,6 @@ export function createReviewModel (builder: Builder): void {
} }
}) })
} }
function createStatusTableViewlet (builder: Builder): void {
builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: recruit.class.Review,
descriptor: task.viewlet.StatusTable,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
options: {
lookup: {
attachedTo: recruit.mixin.Candidate,
state: task.class.State,
assignee: contact.class.Employee,
doneState: task.class.DoneState,
participants: contact.class.Employee
}
} as FindOptions<Doc>,
config: [
'',
'$lookup.attachedTo',
{ key: '$lookup.participants', presenter: recruit.component.PersonsPresenter, label: recruit.string.Participants, sortingKey: '$lookup.participants' },
// 'location',
'company',
'dueDate',
{ key: '', presenter: recruit.component.OpinionsPresenter, label: recruit.string.Opinions, sortingKey: 'opinions' },
'$lookup.state',
'$lookup.doneState',
// { presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' },
// { presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' },
'modifiedOn'
]
})
builder.createDoc(
view.class.Action,
core.space.Model,
{
label: recruit.string.CreateOpinion,
icon: recruit.icon.Create,
action: recruit.actionImpl.CreateOpinion
},
recruit.action.CreateOpinion
)
builder.createDoc(view.class.ActionTarget, core.space.Model, {
target: recruit.class.Review,
action: recruit.action.CreateOpinion
})
}
function createKanbanViewlet (builder: Builder): void {
builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: recruit.class.Review,
descriptor: task.viewlet.Kanban,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
options: {
lookup: {
attachedTo: recruit.mixin.Candidate,
state: task.class.State,
assignee: contact.class.Employee,
participants: contact.class.Employee
}
} as FindOptions<Doc>,
config: ['$lookup.attachedTo', '$lookup.state', '$lookup.participants', '$lookup.assignee']
})
}
function createTableViewlet (builder: Builder): void { function createTableViewlet (builder: Builder): void {
builder.createDoc(view.class.Viewlet, core.space.Model, { builder.createDoc(view.class.Viewlet, core.space.Model, {
@ -154,22 +91,20 @@ function createTableViewlet (builder: Builder): void {
options: { options: {
lookup: { lookup: {
attachedTo: recruit.mixin.Candidate, attachedTo: recruit.mixin.Candidate,
state: task.class.State, participants: contact.class.Employee,
assignee: contact.class.Employee, company: contact.class.Organization
doneState: task.class.DoneState,
participants: contact.class.Employee
} }
} as FindOptions<Doc>, } as FindOptions<Doc>,
config: [ config: [
'', '',
'title',
'$lookup.attachedTo', '$lookup.attachedTo',
{ key: '$lookup.participants', presenter: recruit.component.PersonsPresenter, label: recruit.string.Participants, sortingKey: '$lookup.participants' }, 'verdict',
// 'location',
'company',
'dueDate',
{ key: '', presenter: recruit.component.OpinionsPresenter, label: recruit.string.Opinions, sortingKey: 'opinions' }, { key: '', presenter: recruit.component.OpinionsPresenter, label: recruit.string.Opinions, sortingKey: 'opinions' },
'$lookup.state', { key: '$lookup.participants', presenter: calendar.component.PersonsPresenter, label: calendar.string.Participants, sortingKey: '$lookup.participants' },
'$lookup.doneState', '$lookup.company',
'date',
'dueDate',
// { presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' }, // { presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' },
// { presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' }, // { presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' },
'modifiedOn' 'modifiedOn'

View File

@ -142,6 +142,10 @@ export function createModel (builder: Builder): void {
presenter: view.component.StringPresenter presenter: view.component.StringPresenter
}) })
builder.mixin(core.class.TypeIntlString, core.class.Class, view.mixin.AttributePresenter, {
presenter: view.component.IntlStringPresenter
})
builder.mixin(core.class.TypeNumber, core.class.Class, view.mixin.AttributeEditor, { builder.mixin(core.class.TypeNumber, core.class.Class, view.mixin.AttributeEditor, {
editor: view.component.NumberEditor editor: view.component.NumberEditor
}) })

View File

@ -31,6 +31,7 @@ export default mergeIds(viewId, view, {
component: { component: {
StringEditor: '' as AnyComponent, StringEditor: '' as AnyComponent,
StringPresenter: '' as AnyComponent, StringPresenter: '' as AnyComponent,
IntlStringPresenter: '' as AnyComponent,
NumberEditor: '' as AnyComponent, NumberEditor: '' as AnyComponent,
NumberPresenter: '' as AnyComponent, NumberPresenter: '' as AnyComponent,
HTMLPresenter: '' as AnyComponent, HTMLPresenter: '' as AnyComponent,

View File

@ -10,6 +10,7 @@
"Name": "Name", "Name": "Name",
"Description": "Description", "Description": "Description",
"Private": "Private", "Private": "Private",
"Archived": "Archived" "Archived": "Archived",
"ClassLabel": "Label"
} }
} }

View File

@ -10,6 +10,7 @@
"Name": "Название", "Name": "Название",
"Description": "Описание", "Description": "Описание",
"Private": "Личный", "Private": "Личный",
"Archived": "Архивный" "Archived": "Архивный",
"ClassLabel": "Тип"
} }
} }

View File

@ -31,6 +31,11 @@ export type PrimitiveType = number | string | boolean | undefined | Ref<Doc>
*/ */
export type Timestamp = number export type Timestamp = number
/**
* @public
*/
export type Markup = string
/** /**
* @public * @public
*/ */
@ -160,6 +165,15 @@ export type AttachedData<T extends AttachedDoc> = Omit<T, keyof AttachedDoc>
// T Y P E S // T Y P E S
/**
* @public
*/
export interface TypeDate extends Type<Date> {
// If not set to true, will be false
withTime?: boolean
}
/** /**
* @public * @public
*/ */

View File

@ -44,6 +44,7 @@ export default plugin(coreId, {
Space: '' as Ref<Class<Space>>, Space: '' as Ref<Class<Space>>,
Account: '' as Ref<Class<Account>>, Account: '' as Ref<Class<Account>>,
TypeString: '' as Ref<Class<Type<string>>>, TypeString: '' as Ref<Class<Type<string>>>,
TypeIntlString: '' as Ref<Class<Type<IntlString>>>,
TypeNumber: '' as Ref<Class<Type<string>>>, TypeNumber: '' as Ref<Class<Type<string>>>,
TypeMarkup: '' as Ref<Class<Type<string>>>, TypeMarkup: '' as Ref<Class<Type<string>>>,
TypeBoolean: '' as Ref<Class<Type<boolean>>>, TypeBoolean: '' as Ref<Class<Type<boolean>>>,

View File

@ -14,7 +14,7 @@
// //
import core, { import core, {
Account, ArrOf as TypeArrOf, AttachedDoc, Attribute, Class, Classifier, ClassifierKind, Collection as TypeCollection, Data, Doc, Domain, generateId, IndexKind, Interface, Mixin as IMixin, MixinUpdate, Obj, PropertyType, Ref, RefTo, Space, Tx, TxCreateDoc, TxFactory, TxProcessor, Type Account, ArrOf as TypeArrOf, AttachedDoc, Attribute, Class, Classifier, ClassifierKind, Collection as TypeCollection, Data, Doc, Domain, generateId, IndexKind, Interface, Markup, Mixin as IMixin, MixinUpdate, Obj, PropertyType, Ref, RefTo, Space, Timestamp, Tx, TxCreateDoc, TxFactory, TxProcessor, Type, TypeDate as TypeDateType
} from '@anticrm/core' } from '@anticrm/core'
import type { Asset, IntlString } from '@anticrm/platform' import type { Asset, IntlString } from '@anticrm/platform'
import toposort from 'toposort' import toposort from 'toposort'
@ -331,36 +331,43 @@ export function TypeString (): Type<string> {
/** /**
* @public * @public
*/ */
export function TypeNumber (): Type<string> { export function TypeNumber (): Type<number> {
return { _class: core.class.TypeNumber, label: 'TypeNumber' as IntlString } return { _class: core.class.TypeNumber, label: 'TypeNumber' as IntlString }
} }
/** /**
* @public * @public
*/ */
export function TypeMarkup (): Type<string> { export function TypeMarkup (): Type<Markup> {
return { _class: core.class.TypeMarkup, label: 'TypeMarkup' as IntlString } return { _class: core.class.TypeMarkup, label: 'TypeMarkup' as IntlString }
} }
/** /**
* @public * @public
*/ */
export function TypeBoolean (): Type<string> { export function TypeIntlString (): Type<IntlString> {
return { _class: core.class.TypeIntlString, label: 'TypeIntlString' as IntlString }
}
/**
* @public
*/
export function TypeBoolean (): Type<boolean> {
return { _class: core.class.TypeBoolean, label: 'TypeBoolean' as IntlString } return { _class: core.class.TypeBoolean, label: 'TypeBoolean' as IntlString }
} }
/** /**
* @public * @public
*/ */
export function TypeTimestamp (): Type<string> { export function TypeTimestamp (): Type<Timestamp> {
return { _class: core.class.TypeTimestamp, label: 'TypeTimestamp' as IntlString } return { _class: core.class.TypeTimestamp, label: 'TypeTimestamp' as IntlString }
} }
/** /**
* @public * @public
*/ */
export function TypeDate (): Type<string> { export function TypeDate (withTime?: boolean): TypeDateType {
return { _class: core.class.TypeDate, label: 'TypeDate' as IntlString } return { _class: core.class.TypeDate, label: 'TypeDate' as IntlString, withTime }
} }
/** /**

View File

@ -76,6 +76,7 @@
placeholder={attribute?.label} placeholder={attribute?.label}
{maxWidth} {maxWidth}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })} value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
attributeType={attribute.type}
space={object.space} space={object.space}
{onChange} {onChange}
{focus} {focus}
@ -94,6 +95,7 @@
placeholder={attribute?.label} placeholder={attribute?.label}
{maxWidth} {maxWidth}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })} value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
attributeType={attribute.type}
space={object.space} space={object.space}
{onChange} {onChange}
{focus} {focus}
@ -105,6 +107,7 @@
this={instance} this={instance}
{maxWidth} {maxWidth}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })} value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
attributeType={attribute.type}
space={object.space} space={object.space}
{onChange} {onChange}
{focus} {focus}

View File

@ -33,6 +33,7 @@
export let labelProps: any | undefined = undefined export let labelProps: any | undefined = undefined
export let okAction: () => void export let okAction: () => void
export let canSave: boolean = false export let canSave: boolean = false
export let size: 'small'| 'medium' = 'small'
export let okLabel: IntlString = presentation.string.Create export let okLabel: IntlString = presentation.string.Create
export let cancelLabel: IntlString = presentation.string.Cancel export let cancelLabel: IntlString = presentation.string.Cancel
@ -40,7 +41,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
</script> </script>
<form class="antiCard" on:submit|preventDefault={ () => {} }> <form class="antiCard" class:w-2125rem={size === 'small'} class:w-4125rem={size === 'medium'} on:submit|preventDefault={ () => {} }>
<div class="antiCard-header"> <div class="antiCard-header">
<div class="antiCard-header__title"><Label {label} params={labelProps ?? {}} /></div> <div class="antiCard-header__title"><Label {label} params={labelProps ?? {}} /></div>
{#if $$slots.error} {#if $$slots.error}

View File

@ -35,6 +35,7 @@
export let show: boolean = false export let show: boolean = false
export let allowDeselect = false export let allowDeselect = false
export let titleDeselect: IntlString | undefined = undefined export let titleDeselect: IntlString | undefined = undefined
export let readonly = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -69,7 +70,7 @@
<div class="antiSelect" bind:this={container} <div class="antiSelect" bind:this={container}
on:click|preventDefault={() => { on:click|preventDefault={() => {
btn.focus() btn.focus()
if (!opened) { if (!opened && !readonly) {
opened = true opened = true
showPopup(UsersPopup, { _class, title, caption, allowDeselect, selected: value, titleDeselect }, container, (result) => { showPopup(UsersPopup, { _class, title, caption, allowDeselect, selected: value, titleDeselect }, container, (result) => {
if (result === null) { if (result === null) {

View File

@ -78,7 +78,7 @@
</div> </div>
{/if} {/if}
{#each persons as person} {#each persons as person}
<div class="antiComponentBox flex-center"> <div class="antiComponentBox flex-center margin_025 antiComponentBoxFocused">
<UserInfo value={person} size={'medium'} /> <UserInfo value={person} size={'medium'} />
<div class="ml-1"> <div class="ml-1">
<ActionIcon icon={IconClose} size={'small'} action={() => removePerson(person)} /> <ActionIcon icon={IconClose} size={'small'} action={() => removePerson(person)} />
@ -104,4 +104,7 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
.margin_025 {
margin: 0.25rem;
}
</style> </style>

View File

@ -1,13 +1,19 @@
<script lang="ts"> <script lang="ts">
import { IntlString } from '@anticrm/platform' import { IntlString } from '@anticrm/platform'
import presentation, { MessageViewer } from '@anticrm/presentation' import presentation, { MessageViewer } from '@anticrm/presentation'
import { ActionIcon, IconCheck, IconClose, IconEdit } from '@anticrm/ui' import { ActionIcon, IconCheck, IconClose, IconEdit, Label } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import textEditorPlugin from '../plugin' import textEditorPlugin from '../plugin'
import StyledTextEditor from './StyledTextEditor.svelte' import StyledTextEditor from './StyledTextEditor.svelte'
export let label: IntlString | undefined = undefined
export let content: string export let content: string
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
export let emphasized = false
export let alwaysEdit = false
export let showButtons = true
let rawValue: string let rawValue: string
const Mode = { const Mode = {
@ -22,18 +28,41 @@
textEditor.submit() textEditor.submit()
} }
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let focused = false
let needFocus = false
$: if (textEditor && needFocus) {
textEditor.focus()
needFocus = false
}
</script> </script>
<div class="antiComponent styled-box"> <div class="antiComponent styled-box" class:emphasized class:emphasized-focus={(mode === Mode.Edit || alwaysEdit) && focused} on:click={() => {
{#if mode !== Mode.View} if (alwaysEdit && focused) {
textEditor?.focus()
}
}}>
{#if label}
<div class="label"><Label {label} /></div>
{/if}
{#if mode !== Mode.View || alwaysEdit}
<StyledTextEditor <StyledTextEditor
{placeholder} {placeholder}
{showButtons}
bind:content={rawValue} bind:content={rawValue}
bind:this={textEditor} bind:this={textEditor}
on:focus={() => {
focused = true
}}
on:blur={() => {
focused = false
}}
on:value={(evt) => { on:value={(evt) => {
rawValue = evt.detail rawValue = evt.detail
}} }}
> >
{#if !alwaysEdit}
<div class="flex flex-reverse flex-grow"> <div class="flex flex-reverse flex-grow">
<div class="ml-2"> <div class="ml-2">
<!-- disabled={rawValue.trim().length === 0} --> <!-- disabled={rawValue.trim().length === 0} -->
@ -59,6 +88,7 @@
}} }}
/> />
</div> </div>
{/if}
</StyledTextEditor> </StyledTextEditor>
{:else} {:else}
<div class="text"> <div class="text">
@ -66,28 +96,58 @@
<MessageViewer message={content} /> <MessageViewer message={content} />
{/if} {/if}
</div> </div>
<div class="flex flex-reverse"> {#if !alwaysEdit}
<ActionIcon <div class="flex flex-reverse">
size={'medium'} <ActionIcon
icon={IconEdit} size={'medium'}
direction={'top'} icon={IconEdit}
label={textEditorPlugin.string.Edit} direction={'top'}
action={() => { label={textEditorPlugin.string.Edit}
rawValue = content ?? '' action={() => {
mode = Mode.Edit rawValue = content ?? ''
}} needFocus = true
/> mode = Mode.Edit
</div> }}
/>
</div>
{/if}
{/if} {/if}
</div> </div>
<style lang="scss"> <style lang="scss">
.styled-box { .styled-box {
flex-grow: 1; flex-grow: 1;
.label {
padding-bottom: 0.25rem;
font-size: 0.75rem;
color: var(--theme-caption-color);
opacity: 0.3;
transition: top 200ms;
pointer-events: none;
user-select: none;
}
.emphasized .emphasized-focus + .label {
top: 0.5rem;
}
}
.emphasized {
padding: 1rem;
background-color: var(--theme-bg-accent-color);
border: 1px solid var(--theme-bg-accent-hover);
border-radius: 0.75rem;
&.emphasized-focus {
background-color: var(--theme-bg-focused-color);
border-color: var(--theme-bg-focused-border);
}
} }
.text { .text {
overflow: auto; overflow: auto;
flex-grow: 1; flex-grow: 1;
line-height: 150%; line-height: 150%;
.nolabel {
padding-top: 0;
}
} }
</style> </style>

View File

@ -27,12 +27,16 @@
export let content: string = '' export let content: string = ''
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
export let showButtons = true
let textEditor: TextEditor let textEditor: TextEditor
export function submit (): void { export function submit (): void {
textEditor.submit() textEditor.submit()
} }
export function focus (): void {
textEditor.focus()
}
</script> </script>
<div class="ref-container"> <div class="ref-container">
@ -50,15 +54,16 @@
textEditor.clear() textEditor.clear()
}} }}
on:blur on:blur
on:focus
supportSubmit={false} supportSubmit={false}
/> />
</ScrollBox> </ScrollBox>
</div> </div>
</div> </div>
<div class="buttons"> <div class="buttons" class:shown={showButtons}>
<div class="tool"><TextStyle size={'large'} /></div> <div class="tool"><TextStyle size={'large'} /></div>
<div class="tool"><Emoji size={'large'}/></div> <div class="tool"><Emoji size={'large'} /></div>
<div class="tool"><GIF size={'large'}/></div> <div class="tool"><GIF size={'large'} /></div>
<div class="flex-grow"> <div class="flex-grow">
<slot /> <slot />
</div> </div>
@ -72,6 +77,14 @@
flex-direction: column; flex-direction: column;
min-height: 4.5rem; min-height: 4.5rem;
.buttons {
visibility: hidden;
}
.shown {
visibility: visible;
}
.textInput { .textInput {
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;

View File

@ -53,6 +53,15 @@
export function insertText (text: string): void { export function insertText (text: string): void {
editor.commands.insertContent(text as HTMLContent) editor.commands.insertContent(text as HTMLContent)
} }
let needFocus = false
export function focus (): void {
needFocus = true
}
$: if (editor && needFocus) {
editor.commands.focus()
needFocus = false
}
const Handle = Extension.create({ const Handle = Extension.create({
addKeyboardShortcuts () { addKeyboardShortcuts () {
@ -98,6 +107,9 @@
onBlur: () => { onBlur: () => {
dispatch('blur', editor.getHTML()) dispatch('blur', editor.getHTML())
}, },
onFocus: () => {
dispatch('focus', editor.getHTML())
},
onUpdate: () => { onUpdate: () => {
content = editor.getHTML() content = editor.getHTML()
dispatch('value', content) dispatch('value', content)

View File

@ -417,12 +417,14 @@
// Basic component view. // Basic component view.
.antiComponentBox { .antiComponentBox {
margin: 0.25rem;
padding: 0.5rem; padding: 0.5rem;
background-color: var(--theme-button-bg-focused); background-color: var(--theme-bg-accent-color);
border: 1px solid var(--theme-button-border-enabled); border: 1px solid var(--theme-button-border-enabled);
border-radius: .75rem; border-radius: .75rem;
box-shadow: 0px 3px 3px rgba(0, 0, 0, .2);
&.antiComponentBoxFocused {
background-color: var(--theme-button-bg-focused);
}
} }
/* Select */ /* Select */

View File

@ -70,13 +70,11 @@
} }
/* Cards */ /* Cards */
.antiCard { .antiCard {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 21.25rem;
min-width: 21.25rem;
max-width: 21.25rem;
background-color: var(--theme-card-bg); background-color: var(--theme-card-bg);
border-radius: 1.25rem; border-radius: 1.25rem;
box-shadow: var(--theme-card-shadow); box-shadow: var(--theme-card-shadow);

View File

@ -28,12 +28,12 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const getNow = (): Date => { const getNow = (): Date => {
let tempDate = new Date(Date.now()) const tempDate = new Date(Date.now())
return new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate()) return new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())
} }
let today: Date = getNow() const today: Date = getNow()
let todayString: string let todayString: string
async function todayStr() { async function todayStr () {
todayString = await translate(ui.string.Today, {}) todayString = await translate(ui.string.Today, {})
} }
todayStr() todayStr()

View File

@ -22,7 +22,7 @@
export let withTime: boolean = false export let withTime: boolean = false
const { currentLanguage } = getContext('lang') const { currentLanguage } = getContext('lang')
let inter: boolean = (currentLanguage === 'ru') ?? false const inter: boolean = (currentLanguage === 'ru') ?? false
const zeroLead = (n: number): string => { const zeroLead = (n: number): string => {
if (n < 10) return '0' + n.toString() if (n < 10) return '0' + n.toString()

View File

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

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="calendar" viewBox="0 0 24 24">
<path d="M19.5,5h-2.1V4.5c0-0.3-0.2-0.5-0.5-0.5s-0.5,0.2-0.5,0.5V5H8.1V4.5C8.1,4.2,7.9,4,7.6,4S7.1,4.2,7.1,4.5V5H5 C4.2,5,3.5,5.7,3.5,6.5V19c0,0.8,0.7,1.5,1.5,1.5h14.5c0.8,0,1.5-0.7,1.5-1.5V6.5C21,5.7,20.3,5,19.5,5z M5,6h2.1v0.5 c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5V6h8.3v0.5c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5V6h2.1C19.7,6,20,6.3,20,6.5v3.1H4.5V6.5 C4.5,6.3,4.7,6,5,6z M19.5,19.5H5c-0.3,0-0.5-0.2-0.5-0.5v-8.3H20V19C20,19.2,19.7,19.5,19.5,19.5z" />
</symbol>
<symbol id="location" viewBox="0 0 16 16">
<path d="M8,4.6c-1.5,0-2.6,1.2-2.6,2.6S6.5,9.9,8,9.9s2.6-1.2,2.6-2.6S9.5,4.6,8,4.6z M8,8.9c-0.9,0-1.6-0.7-1.6-1.6 c0-0.9,0.7-1.6,1.6-1.6s1.6,0.7,1.6,1.6C9.6,8.2,8.9,8.9,8,8.9z"/>
<path d="M8,1.8c-3,0-5.5,2.5-5.5,5.5c0,1.9,0.9,3.4,2,4.5c1.1,1.1,2.3,1.8,2.9,2.1c0.4,0.2,0.9,0.2,1.3,0c0.6-0.3,1.8-1,2.9-2.1 c1.1-1.1,2-2.6,2-4.5C13.5,4.3,11,1.8,8,1.8z M10.8,11.1c-1,1-2.1,1.7-2.6,2c-0.1,0.1-0.2,0.1-0.3,0c-0.6-0.3-1.7-1-2.6-2 c-1-1-1.7-2.3-1.7-3.8c0-2.5,2-4.5,4.5-4.5s4.5,2,4.5,4.5C12.5,8.8,11.7,10.1,10.8,11.1z"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,18 @@
// The "rig.json" file directs tools to look for their config files in an external package.
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
/**
* (Required) The name of the rig package to inherit from.
* It should be an NPM package name with the "-rig" suffix.
*/
"rigPackageName": "@anticrm/platform-rig"
/**
* (Optional) Selects a config profile from the rig package. The name must consist of
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
* If omitted, then the "default" profile will be used."
*/
// "rigProfile": "your-profile-name"
}

View File

@ -0,0 +1,17 @@
{
"string": {
"ApplicationLabelCalendar": "Calendar",
"Calendars": "Calendars",
"Participants": "Participants",
"NoParticipants": "No participants added",
"PersonsLabel": "{name}",
"AddDescription": "Add description",
"Date": "Date",
"DueTo": "Due date",
"Description": "Description",
"Title": "Title",
"Location": "Location",
"Company": "Company",
"CreateCalendar": "Create Calendar"
}
}

View File

@ -0,0 +1,17 @@
{
"string": {
"ApplicationLabelCalendar": "Календарь",
"Calendars": "Календари",
"Participants": "Участники",
"NoParticipants": "Участники не добавлены",
"PersonsLabel": "{name}",
"AddDescription": "Добавить описание",
"Date": "Дата",
"DueTo": "Дата конца",
"Description": "Описание",
"Title": "Название",
"Location": "Местоположение",
"Company": "Компания",
"CreateCalendar": "Новый Калеедарь"
}
}

View File

@ -0,0 +1,33 @@
{
"name": "@anticrm/calendar-assets",
"version": "0.6.0",
"main": "lib/index.js",
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "heft build",
"build:docs": "",
"lint": "eslint src",
"lint:fix": "eslint --fix src",
"format": "prettier --write src && eslint --fix src",
"build:watch": "tsc"
},
"devDependencies": {
"@anticrm/platform-rig": "~0.6.0",
"@types/heft-jest": "^1.0.2",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"eslint-config-standard-with-typescript": "^21.0.1",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1",
"eslint": "^7.32.0",
"prettier": "^2.4.1",
"@rushstack/heft": "^0.41.1",
"@types/node": "^16.4.10"
},
"dependencies": {
"@anticrm/platform": "~0.6.5",
"@anticrm/calendar": "~0.6.0"
}
}

View File

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

View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"moduleResolution": "node",
"target": "esnext",
"module": "esnext",
"declaration": true,
"outDir": "./lib",
"strict": true,
"esModuleInterop": true,
"lib": [
"esnext",
"dom"
]
}
}

View File

@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@anticrm/platform-rig/profiles/ui/config/eslint.config.json'],
parserOptions: { tsconfigRootDir: __dirname },
settings: {
'svelte3/ignore-styles': () => true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,47 @@
{
"name": "@anticrm/calendar-resources",
"version": "0.6.0",
"main": "src/index.ts",
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "echo 'no build for ui'",
"build:docs": "api-extractor run --local",
"lint": "svelte-check && eslint",
"lint:fix": "eslint --fix src",
"format": "prettier --write --plugin-search-dir=. src && eslint --fix src",
"svelte-check": "svelte-check"
},
"devDependencies": {
"svelte-loader": "^3.1.2",
"sass": "^1.37.5",
"svelte-preprocess": "^4.10.3",
"@anticrm/platform-rig": "~0.6.0",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"eslint-config-standard-with-typescript": "^21.0.1",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-svelte3": "~3.2.1",
"prettier-plugin-svelte": "^2.2.0",
"eslint": "^7.32.0",
"prettier": "^2.4.1",
"svelte-check": "^2.2.10",
"typescript": "^4.3.5"
},
"dependencies": {
"@anticrm/core": "~0.6.11",
"@anticrm/platform": "~0.6.5",
"@anticrm/ui": "~0.6.0",
"@anticrm/presentation": "~0.6.2",
"@anticrm/calendar": "~0.6.0",
"svelte": "^3.37.0",
"@anticrm/text-editor": "~0.6.0",
"@anticrm/contact": "~0.6.2",
"@anticrm/contact-resources": "~0.6.0",
"@anticrm/view-resources": "~0.6.0",
"@anticrm/view": "~0.6.0",
"@anticrm/workbench": "~0.6.1"
}
}

View File

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

View File

@ -0,0 +1,56 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { formatName, Person } from '@anticrm/contact'
import { Hierarchy } from '@anticrm/core'
import { Avatar } from '@anticrm/presentation'
import calendar from '../plugin'
import { showPanel, Tooltip } from '@anticrm/ui'
import view from '@anticrm/view'
export let value: Person | Person[]
export let inline: boolean = false
let persons: Person[] = []
$: persons = Array.isArray(value) ? value : [value]
async function onClick (p: Person) {
showPanel(view.component.EditDoc, p._id, Hierarchy.mixinOrClass(p), 'full')
}
</script>
{#if value}
<div class='flex persons'>
{#each persons as p}
<Tooltip label={calendar.string.PersonsLabel} props={{ name: formatName(p.name) }}>
<div class="flex-presenter" class:inline-presenter={inline} on:click={() => onClick(p)}>
<div class="icon">
<Avatar size={'x-small'} avatar={p.avatar} />
</div>
</div>
</Tooltip>
{/each}
</div>
{/if}
<style lang="scss">
.persons {
display: grid;
grid-template-columns: repeat(4, min-content);
.icon {
margin: 0.25rem;
}
}
</style>

View File

@ -0,0 +1,24 @@
//
// Copyright © 2020 Anticrm Platform Contributors.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { Resources } from '@anticrm/platform'
import PersonsPresenter from './components/PersonsPresenter.svelte'
export default async (): Promise<Resources> => ({
component: {
PersonsPresenter
}
})

View File

@ -0,0 +1,24 @@
//
// Copyright © 2020 Anticrm Platform Contributors.
//
// 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 calendar, { calendarId } from '@anticrm/calendar'
import { mergeIds } from '@anticrm/platform'
export default mergeIds(calendarId, calendar, {
component: {
},
string: {
}
})

View File

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

View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"moduleResolution": "node",
"target": "esnext",
"module": "esnext",
"declaration": true,
"outDir": "./lib",
"strict": true,
"esModuleInterop": true,
"lib": [
"esnext",
"dom"
]
}
}

View File

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

View File

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

View File

@ -0,0 +1,18 @@
// The "rig.json" file directs tools to look for their config files in an external package.
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
/**
* (Required) The name of the rig package to inherit from.
* It should be an NPM package name with the "-rig" suffix.
*/
"rigPackageName": "@anticrm/platform-rig"
/**
* (Optional) Selects a config profile from the rig package. The name must consist of
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
* If omitted, then the "default" profile will be used."
*/
// "rigProfile": "your-profile-name"
}

View File

@ -0,0 +1,34 @@
{
"name": "@anticrm/calendar",
"version": "0.6.0",
"main": "lib/index.js",
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "heft build",
"build:watch": "tsc",
"lint:fix": "eslint --fix src",
"lint": "eslint src",
"format": "prettier --write src && eslint --fix src"
},
"devDependencies": {
"@anticrm/platform-rig": "~0.6.0",
"@types/heft-jest": "^1.0.2",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-node": "^11.1.0",
"eslint": "^7.32.0",
"@typescript-eslint/parser": "^5.4.0",
"eslint-config-standard-with-typescript": "^21.0.1",
"prettier": "^2.4.1",
"@rushstack/heft": "^0.41.1",
"typescript": "^4.3.5"
},
"dependencies": {
"@anticrm/platform": "~0.6.5",
"@anticrm/ui": "~0.6.0",
"@anticrm/core": "~0.6.11",
"@anticrm/contact": "~0.6.5"
}
}

View File

@ -0,0 +1,90 @@
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
import { Employee } from '@anticrm/contact'
import type { AttachedDoc, Class, Doc, Markup, Ref, Space, Timestamp } from '@anticrm/core'
import type { Asset, IntlString, Plugin } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import { AnyComponent } from '@anticrm/ui'
/**
* @public
*/
export interface Calendar extends Space {}
/**
* @public
*/
export interface Event extends AttachedDoc {
title: string
number: number
description: Markup
location?: string
// Event scheduled date
date: Timestamp
// Event due date for long events.
dueDate?: Timestamp
attachments?: number
comments?: number
participants?: Ref<Employee>[]
}
/**
* @public
*/
export const calendarId = 'calendar' as Plugin
/**
* @public
*/
const calendarPlugin = plugin(calendarId, {
class: {
Calendar: '' as Ref<Class<Calendar>>,
Event: '' as Ref<Class<Event>>
},
icon: {
Calendar: '' as Asset,
Location: '' as Asset
},
space: {
// Space for all personal events.
PersonalEvents: '' as Ref<Space>
},
app: {
Calendar: '' as Ref<Doc>
},
component: {
PersonsPresenter: '' as AnyComponent
},
string: {
Title: '' as IntlString,
Calendar: '' as IntlString,
Description: '' as IntlString,
Date: '' as IntlString,
DueTo: '' as IntlString,
Calendars: '' as IntlString,
CreateCalendar: '' as IntlString,
Location: '' as IntlString,
Participants: '' as IntlString,
NoParticipants: '' as IntlString,
PersonsLabel: '' as IntlString,
EventNumber: '' as IntlString
}
})
export default calendarPlugin

View File

@ -0,0 +1,9 @@
{
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"lib": ["esnext", "dom"]
}
}

View File

@ -20,6 +20,7 @@
import { createQuery } from '@anticrm/presentation' import { createQuery } from '@anticrm/presentation'
import { Dropdown } from '@anticrm/ui' import { Dropdown } from '@anticrm/ui'
import { ListItem } from '@anticrm/ui/src/types' import { ListItem } from '@anticrm/ui/src/types'
import { createEventDispatcher } from 'svelte'
import contact from '../plugin' import contact from '../plugin'
import Company from './icons/Company.svelte' import Company from './icons/Company.svelte'
@ -27,6 +28,7 @@
export let label: IntlString = contact.string.Organization export let label: IntlString = contact.string.Organization
const query = createQuery() const query = createQuery()
const dispatch = createEventDispatcher()
query.query(contact.class.Organization, {}, (res) => { query.query(contact.class.Organization, {}, (res) => {
items = res.map((org) => { items = res.map((org) => {
@ -52,6 +54,7 @@
} else { } else {
value = selected._id as Ref<Organization> value = selected._id as Ref<Organization>
} }
dispatch('change', value)
} }
</script> </script>

View File

@ -62,7 +62,7 @@
"ReviewCategory": "Reviews", "ReviewCategory": "Reviews",
"ReviewCategoryDescription": "Description", "ReviewCategoryDescription": "Description",
"ThisReviewCategoryIsPrivate": "This category is private", "ThisReviewCategoryIsPrivate": "This category is private",
"CreateReview": "Create review", "CreateReview": "Schedule {label}",
"SelectReviewCategory": "select category", "SelectReviewCategory": "select category",
"Reviews": "Reviews", "Reviews": "Reviews",
"Review": "Review", "Review": "Review",
@ -84,7 +84,8 @@
"OpinionValuePlaceholder": "10/10", "OpinionValuePlaceholder": "10/10",
"Participants": "Participants", "Participants": "Participants",
"NoParticipants": "No participants added", "NoParticipants": "No participants added",
"PersonsLabel": "{name}" "PersonsLabel": "{name}",
"AddDescription": "Add description"
}, },
"status": { "status": {
"CandidateRequired": "Please select candidate", "CandidateRequired": "Please select candidate",

View File

@ -63,7 +63,7 @@
"ReviewCategory": "Оценки", "ReviewCategory": "Оценки",
"ReviewCategoryDescription": "Описание", "ReviewCategoryDescription": "Описание",
"ThisReviewCategoryIsPrivate": "Эта категория личная", "ThisReviewCategoryIsPrivate": "Эта категория личная",
"CreateReview": "Запланировать оценку", "CreateReview": "Запланировать {label}",
"SelectReviewCategory": "выбрать категорию", "SelectReviewCategory": "выбрать категорию",
"Reviews": "Оценки", "Reviews": "Оценки",
"Review": "Оценка", "Review": "Оценка",
@ -85,7 +85,8 @@
"OpinionValuePlaceholder": "10/10", "OpinionValuePlaceholder": "10/10",
"Participants": "Участники", "Participants": "Участники",
"NoParticipants": "Участники не добавлены", "NoParticipants": "Участники не добавлены",
"PersonsLabel": "{name}" "PersonsLabel": "{name}",
"AddDescription": "Add description"
}, },
"status": { "status": {
"CandidateRequired": "Пожалуйста выберите кандидата", "CandidateRequired": "Пожалуйста выберите кандидата",

View File

@ -55,6 +55,7 @@
"@anticrm/contact-resources": "~0.6.0", "@anticrm/contact-resources": "~0.6.0",
"@anticrm/rekoni": "~0.6.0", "@anticrm/rekoni": "~0.6.0",
"@anticrm/notification": "~0.6.0", "@anticrm/notification": "~0.6.0",
"@anticrm/tags": "~0.6.0" "@anticrm/tags": "~0.6.0",
"@anticrm/calendar": "~0.6.0"
} }
} }

View File

@ -13,36 +13,36 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import type { Contact, Employee, Person } from '@anticrm/contact' import type { Contact, Organization, Person } from '@anticrm/contact'
import contact from '@anticrm/contact' import contact from '@anticrm/contact'
import { Account, Class, Client, Doc, generateId, Ref, SortingOrder } from '@anticrm/core' import { OrganizationSelector } from '@anticrm/contact-resources'
import { Account, Class, Client, Doc, generateId, Ref } from '@anticrm/core'
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform' import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
import { Card, getClient, UserBox } from '@anticrm/presentation' import { Card, getClient, UserBox } from '@anticrm/presentation'
import type { Candidate, Review } from '@anticrm/recruit' import type { Candidate, Review } from '@anticrm/recruit'
import task, { calcRank, SpaceWithStates, State } from '@anticrm/task' import task, { SpaceWithStates } from '@anticrm/task'
import { Grid, Status as StatusControl } from '@anticrm/ui' import { StyledTextBox } from '@anticrm/text-editor'
import {DatePicker} from '@anticrm/ui' import { DatePicker, Grid, Status as StatusControl, StylishEdit } from '@anticrm/ui'
import view from '@anticrm/view' import view from '@anticrm/view'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import recruit from '../../plugin' import recruit from '../../plugin'
export let space: Ref<SpaceWithStates> export let space: Ref<SpaceWithStates>
export let candidate: Ref<Person> export let candidate: Ref<Person>
export let assignee: Ref<Employee>
export let preserveCandidate = false export let preserveCandidate = false
let status: Status = OK let status: Status = OK
let title: string = ''
let description: string = ''
let startDate: Date = new Date() let startDate: Date = new Date()
let dueDate: Date = new Date() let dueDate: Date = new Date()
let location: string = ''
let company: Ref<Organization> | undefined = undefined
const doc: Review = { const doc: Review = {
state: '' as Ref<State>,
doneState: null,
number: 0, number: 0,
assignee: assignee,
rank: '',
attachedTo: candidate, attachedTo: candidate,
attachedToClass: recruit.mixin.Candidate, attachedToClass: recruit.mixin.Candidate,
_class: recruit.class.Review, _class: recruit.class.Review,
@ -51,10 +51,12 @@
collection: 'reviews', collection: 'reviews',
modifiedOn: Date.now(), modifiedOn: Date.now(),
modifiedBy: '' as Ref<Account>, modifiedBy: '' as Ref<Account>,
startDate: null, date: 0,
dueDate: null, dueDate: undefined,
description: '', description,
verdict: '' company,
verdict: '',
title
} }
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -62,9 +64,17 @@
const hierarchy = client.getHierarchy() const hierarchy = client.getHierarchy()
export function canClose (): boolean { export function canClose (): boolean {
return candidate === undefined && assignee === undefined return candidate === undefined
} }
let spaceLabel: string = ''
$: client.findOne(recruit.class.ReviewCategory, { _id: doc.space }).then((res) => {
if (res !== undefined) {
spaceLabel = res.name
}
})
async function createReview () { async function createReview () {
const state = await client.findOne(task.class.State, { space: doc.space }) const state = await client.findOne(task.class.State, { space: doc.space })
if (state === undefined) { if (state === undefined) {
@ -75,11 +85,6 @@
throw new Error('sequence object not found') throw new Error('sequence object not found')
} }
const lastOne = await client.findOne(
recruit.class.Review,
{ state: state._id },
{ sort: { rank: SortingOrder.Descending } }
)
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true) const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
const candidateInstance = await client.findOne(contact.class.Person, { _id: doc.attachedTo as Ref<Person> }) const candidateInstance = await client.findOne(contact.class.Person, { _id: doc.attachedTo as Ref<Person> })
@ -87,24 +92,25 @@
throw new Error('contact not found') throw new Error('contact not found')
} }
if (!client.getHierarchy().hasMixin(candidateInstance, recruit.mixin.Candidate)) { if (!client.getHierarchy().hasMixin(candidateInstance, recruit.mixin.Candidate)) {
await client.createMixin<Contact, Candidate>(candidateInstance._id, candidateInstance._class, candidateInstance.space, recruit.mixin.Candidate, {}) await client.createMixin<Contact, Candidate>(
candidateInstance._id,
candidateInstance._class,
candidateInstance.space,
recruit.mixin.Candidate,
{}
)
} }
await client.addCollection( await client.addCollection(recruit.class.Review, doc.space, doc.attachedTo, doc.attachedToClass, 'reviews', {
recruit.class.Review, number: (incResult as any).object.sequence,
doc.space, doc.attachedTo, doc.attachedToClass, 'reviews', date: startDate?.getTime() ?? null,
{ dueDate: dueDate?.getTime() ?? null,
state: state._id, description,
doneState: null, verdict: '',
number: (incResult as any).object.sequence, title,
assignee: doc.assignee, company,
rank: calcRank(lastOne, undefined), location
startDate: startDate?.getTime() ?? null, })
dueDate: dueDate?.getTime() ?? null,
description: '',
verdict: ''
}
)
} }
async function invokeValidate ( async function invokeValidate (
@ -130,9 +136,11 @@
</script> </script>
<Card <Card
size={'medium'}
label={recruit.string.CreateReview} label={recruit.string.CreateReview}
labelProps={{ label: spaceLabel }}
okAction={createReview} okAction={createReview}
canSave={status.severity === Severity.OK} canSave={status.severity === Severity.OK && title.trim().length > 0}
spaceClass={recruit.class.ReviewCategory} spaceClass={recruit.class.ReviewCategory}
spaceQuery={{ archived: false }} spaceQuery={{ archived: false }}
spaceLabel={recruit.string.ReviewCategory} spaceLabel={recruit.string.ReviewCategory}
@ -143,19 +151,38 @@
}} }}
> >
<StatusControl slot="error" {status} /> <StatusControl slot="error" {status} />
<Grid column={1} rowGap={1.75}> <Grid column={1} rowGap={1.75}>
{#if !preserveCandidate} <Grid column={!preserveCandidate ? 2 : 1}>
<UserBox _class={contact.class.Person} title={recruit.string.Candidate} caption={recruit.string.Candidates} bind:value={doc.attachedTo} /> <StylishEdit bind:value={title} label={recruit.string.Title} />
{/if} {#if !preserveCandidate}
<UserBox <div class="antiComponentBox">
_class={contact.class.Employee} <UserBox
title={recruit.string.AssignRecruiter} _class={contact.class.Person}
caption={recruit.string.Recruiters} title={recruit.string.Candidate}
bind:value={doc.assignee} caption={recruit.string.Candidates}
allowDeselect bind:value={doc.attachedTo}
titleDeselect={recruit.string.UnAssignRecruiter} />
</div>
{/if}
</Grid>
<StyledTextBox
emphasized
showButtons={false}
bind:content={description}
label={recruit.string.Description}
alwaysEdit
placeholder={recruit.string.AddDescription}
/> />
<DatePicker title={recruit.string.StartDate} bind:value={startDate} withTime /> <Grid column={2}>
<DatePicker title={recruit.string.DueDate} bind:value={dueDate} withTime /> <StylishEdit bind:value={location} label={recruit.string.Location} />
<OrganizationSelector bind:value={company} label={recruit.string.Company} />
</Grid>
<div class="antiComponentBox">
<DatePicker title={recruit.string.StartDate} bind:value={startDate} withTime />
</div>
<div class="antiComponentBox">
<DatePicker title={recruit.string.DueDate} bind:value={dueDate} withTime />
</div>
</Grid> </Grid>
</Card> </Card>

View File

@ -14,44 +14,32 @@
--> -->
<script lang="ts"> <script lang="ts">
import core, { Ref } from '@anticrm/core' import core from '@anticrm/core'
import { getClient,SpaceCreateCard } from '@anticrm/presentation' import { getClient, SpaceCreateCard } from '@anticrm/presentation'
import task,{ createKanban,KanbanTemplate } from '@anticrm/task' import { EditBox, Grid } from '@anticrm/ui'
import { Component,EditBox,Grid } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import recruit from '../../plugin' import recruit from '../../plugin'
import Review from '../icons/Review.svelte' import Review from '../icons/Review.svelte'
import { Organization } from '@anticrm/contact'
import { OrganizationSelector } from '@anticrm/contact-resources'
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let name: string = '' let name: string = ''
const description: string = '' const description: string = ''
let templateId: Ref<KanbanTemplate> | undefined
let company: Ref<Organization> | undefined
export function canClose (): boolean { export function canClose (): boolean {
return name === '' && templateId !== undefined return name === ''
} }
const client = getClient() const client = getClient()
async function createReviewCategory () { async function createReviewCategory () {
if (templateId !== undefined && await client.findOne(task.class.KanbanTemplate, { _id: templateId }) === undefined) { await client.createDoc(recruit.class.ReviewCategory, core.space.Space, {
throw Error(`Failed to find target kanban template: ${templateId}`)
}
const id = await client.createDoc(recruit.class.ReviewCategory, core.space.Space, {
name, name,
description, description,
private: false, private: false,
archived: false, archived: false,
company,
members: [] members: []
}) })
await createKanban(client, id, templateId)
} }
</script> </script>
@ -63,13 +51,5 @@
> >
<Grid column={1} rowGap={1.5}> <Grid column={1} rowGap={1.5}>
<EditBox label={recruit.string.ReviewCategoryName} bind:value={name} icon={Review} placeholder={recruit.string.ReviewCategoryPlaceholder} maxWidth={'16rem'} focus/> <EditBox label={recruit.string.ReviewCategoryName} bind:value={name} icon={Review} placeholder={recruit.string.ReviewCategoryPlaceholder} maxWidth={'16rem'} focus/>
<OrganizationSelector bind:value={company} label={recruit.string.Company} />
<Component is={task.component.KanbanTemplateSelector} props={{
folders: [recruit.space.ReviewTemplates],
template: templateId
}} on:change={(evt) => {
templateId = evt.detail
}}/>
</Grid> </Grid>
</SpaceCreateCard> </SpaceCreateCard>

View File

@ -14,117 +14,111 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import contact from '@anticrm/contact' import calendar from '@anticrm/calendar'
import { createQuery, getClient, UserBoxList } from '@anticrm/presentation' import contact, { Contact } from '@anticrm/contact'
import type { Candidate, Review, ReviewCategory } from '@anticrm/recruit' import { OrganizationSelector } from '@anticrm/contact-resources'
import { getClient, UserBox, UserBoxList, UserInfo } from '@anticrm/presentation'
import type { Review } from '@anticrm/recruit'
import { StyledTextBox } from '@anticrm/text-editor' import { StyledTextBox } from '@anticrm/text-editor'
import { EditBox, Grid, Label } from '@anticrm/ui' import { Grid, Label, showPanel, StylishEdit } from '@anticrm/ui'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import recruit from '../../plugin' import recruit from '../../plugin'
import CandidateCard from '../CandidateCard.svelte' import view from '@anticrm/view'
import ExpandRightDouble from '../icons/ExpandRightDouble.svelte'
import ReviewCategoryCard from './ReviewCategoryCard.svelte'
export let object: Review export let object: Review
let candidate: Candidate
let reviewCategory: ReviewCategory
const candidateQuery = createQuery()
$: if (object !== undefined) {
candidateQuery.query(recruit.mixin.Candidate, { _id: object.attachedTo }, (result) => {
candidate = result[0]
})
}
const reviewCategoryQuery = createQuery()
$: if (candidate !== undefined) {
reviewCategoryQuery.query(recruit.class.ReviewCategory, { _id: object.space }, (result) => {
reviewCategory = result[0]
})
}
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const client = getClient() const client = getClient()
onMount(() => { onMount(() => {
dispatch('open', { dispatch('open', {
ignoreKeys: ['location', 'company', 'number', 'comments', 'startDate', 'description', 'verdict'] ignoreKeys: ['number', 'comments', 'title', 'description', 'verdict']
}) })
}) })
let candidate: Contact | undefined = undefined
async function updateSelected (object: Review) {
candidate = await client.findOne<Contact>(object.attachedToClass, { _id: object.attachedTo })
}
$: updateSelected(object)
</script> </script>
{#if object !== undefined && candidate !== undefined} {#if object !== undefined}
<div class="flex-between"> <div class="mb-2">
<div class="card"><CandidateCard {candidate} /></div> <div class="mb-2">
<div class="arrows"><ExpandRightDouble /></div> <Grid column={2}>
<div class="card"><ReviewCategoryCard category={reviewCategory} /></div> <StylishEdit
</div> label={calendar.string.Title}
bind:value={object.title}
<div class="mt-6 mb-2"> on:change={() => client.update(object, { title: object.title })}
<Grid column={2}> />
<EditBox <div class="antiComponentBox over-underline" on:click={() => {
label={recruit.string.Company} if (candidate !== undefined) {
bind:value={object.company} showPanel(view.component.EditDoc, candidate._id, candidate._class, 'full')
icon={contact.icon.Company} }
placeholder={recruit.string.Company} }}>
maxWidth="39rem" <UserBox
focus readonly
on:change={() => client.update(object, { company: object.company })} _class={contact.class.Person}
title={recruit.string.Candidate}
caption={recruit.string.Candidates}
value={object.attachedTo}
/>
</div>
</Grid>
</div>
<div class="mt-2 mb-2">
<StyledTextBox
label={recruit.string.Description}
emphasized
content={object.description}
on:value={(evt) => {
console.log(evt.detail)
client.update(object, { description: evt.detail })
}}
/> />
<EditBox </div>
label={recruit.string.Location}
<Grid column={2}>
<StylishEdit
label={calendar.string.Location}
bind:value={object.location} bind:value={object.location}
icon={recruit.icon.Location}
placeholder={recruit.string.Location}
maxWidth="39rem"
focus
on:change={() => client.update(object, { location: object.location })} on:change={() => client.update(object, { location: object.location })}
/> />
<div class="antiComponentBox">
<OrganizationSelector
bind:value={object.company}
label={recruit.string.Company}
on:change={() => client.update(object, { company: object.company })}
/>
</div>
</Grid> </Grid>
<div class="flex-row"> <div class="flex-row">
<div class="mt-4 mb-2"> <div class="mt-4 mb-2">
<Label label={recruit.string.Participants} /> <Label label={calendar.string.Participants} />
</div> </div>
<UserBoxList <UserBoxList
_class={contact.class.Employee} _class={contact.class.Employee}
items={object.participants} items={object.participants}
title={recruit.string.Participants} title={calendar.string.Participants}
on:open={(evt) => { on:open={(evt) => {
client.update(object, { $push: { participants: evt.detail._id } }) client.update(object, { $push: { participants: evt.detail._id } })
}} }}
on:delete={(evt) => { on:delete={(evt) => {
client.update(object, { $pull: { participants: evt.detail._id } }) client.update(object, { $pull: { participants: evt.detail._id } })
}} }}
noItems={recruit.string.NoParticipants} noItems={calendar.string.NoParticipants}
/> />
</div> </div>
</div> </div>
<div class="mt-4 mb-1"> <StylishEdit
<Label label={recruit.string.Description} /> label={recruit.string.Verdict}
</div> bind:value={object.verdict}
<div class="description flex"> on:change={() => client.update(object, { verdict: object.verdict })}
<StyledTextBox />
content={object.description}
on:value={(evt) => {
console.log(evt.detail)
client.update(object, { description: evt.detail })
}}
/>
</div>
<div class="mt-4 mb-1">
<Label label={recruit.string.Verdict} />
</div>
<div class="description flex">
<StyledTextBox
content={object.verdict}
on:value={(evt) => {
client.update(object, { verdict: evt.detail })
}}
/>
</div>
{/if} {/if}
<style lang="scss"> <style lang="scss">
@ -138,8 +132,6 @@
.description { .description {
height: 10rem; height: 10rem;
padding: 1rem; margin-bottom: 1rem;
border: 1px solid var(--theme-menu-divider);
border-radius: 8px;
} }
</style> </style>

View File

@ -1,90 +0,0 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
//
// 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 { AttachmentsPresenter } from '@anticrm/attachment-resources'
import { CommentsPresenter } from '@anticrm/chunter-resources'
import { Employee, formatName, Person } from '@anticrm/contact'
import type { WithLookup } from '@anticrm/core'
import { Avatar } from '@anticrm/presentation'
import type { Review } from '@anticrm/recruit'
import { ActionIcon, IconMoreH, showPanel } from '@anticrm/ui'
import view from '@anticrm/view'
import PersonsPresenter from './PersonsPresenter.svelte'
import ReviewPresenter from './ReviewPresenter.svelte'
export let object: WithLookup<Review>
export let draggable: boolean
function showCandidate () {
showPanel(view.component.EditDoc, object.attachedTo, object.attachedToClass, 'full')
}
function getPersons (object: WithLookup<Review>): Person[] {
const r = (object.$lookup?.participants as unknown as Employee[] ?? [])
const assignee = object.$lookup?.assignee as Employee
if (assignee != null && r.findIndex(it => it._id === assignee._id) === -1) {
return [...r, assignee]
}
return r
}
</script>
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend>
<div class="flex-between mb-3">
<Avatar avatar={object.$lookup?.attachedTo?.avatar} size={'medium'} />
<div class="flex-grow flex-col min-w-0 ml-2">
<div class="fs-title over-underline lines-limit-2" on:click={showCandidate}>
{#if object.$lookup?.attachedTo}
{formatName(object.$lookup?.attachedTo?.name)}
{/if}
</div>
<div class="small-text lines-limit-2">{object.$lookup?.attachedTo?.title ?? ''}</div>
</div>
<div class="tool"><ActionIcon label={undefined} icon={IconMoreH} size={'small'} /></div>
</div>
<div class="flex-between">
<div class="flex-row-center">
<div class="sm-tool-icon step-lr75">
<ReviewPresenter value={object} />
</div>
{#if (object.attachments ?? 0) > 0}
<div class="step-lr75"><AttachmentsPresenter value={object} /></div>
{/if}
{#if (object.comments ?? 0) > 0}
<div class="step-lr75"><CommentsPresenter value={object} /></div>
{/if}
</div>
{#if object.$lookup?.participants || object.$lookup?.assignee}
<PersonsPresenter value={getPersons(object)}></PersonsPresenter>
{/if}
</div>
</div>
<style lang="scss">
.card-container {
display: flex;
flex-direction: column;
padding: 1rem 1.25rem;
background-color: rgba(222, 222, 240, 0.06);
border-radius: 0.75rem;
user-select: none;
&.draggable {
cursor: grab;
}
}
.tool {
align-self: start;
}
</style>

View File

@ -0,0 +1,37 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021, 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import type { ReviewCategory } from '@anticrm/recruit'
import { Icon } from '@anticrm/ui'
import { showPanel } from '@anticrm/ui/src/panelup'
import recruit from '../../plugin'
export let value: ReviewCategory
export let inline: boolean = false
function show () {
// showPanel(recruit.component.EditVacancy, value._id, value._class, 'right')
}
</script>
{#if value}
<div class="flex-presenter" class:inline-presenter={inline} on:click={show}>
<div class="icon">
<Icon icon={recruit.icon.Vacancy} size={'small'} />
</div>
<span class="label">{value.name}</span>
</div>
{/if}

View File

@ -37,7 +37,7 @@
function show () { function show () {
closeTooltip() closeTooltip()
showPanel(view.component.EditDoc, value._id, value._class, 'full') showPanel(view.component.EditDoc, value._id, value._class, 'right')
} }
</script> </script>

View File

@ -43,21 +43,19 @@
config={[ config={[
'', '',
{ key: '$lookup.space.name', label: recruit.string.ReviewCategoryTitle }, { key: '$lookup.space.name', label: recruit.string.ReviewCategoryTitle },
'dueDate', 'verdict',
{ {
key: '', key: '',
presenter: recruit.component.OpinionsPresenter, presenter: recruit.component.OpinionsPresenter,
label: recruit.string.Opinions, label: recruit.string.Opinions,
sortingKey: 'opinions' sortingKey: 'opinions'
}, },
'$lookup.state', 'date',
'$lookup.doneState' 'dueDate'
]} ]}
options={{ options={{
lookup: { lookup: {
state: task.class.State, space: core.class.Space
space: core.class.Space,
doneState: task.class.DoneState
} }
}} }}
query={{ attachedTo: objectId }} query={{ attachedTo: objectId }}

View File

@ -35,7 +35,6 @@ import CreateOpinion from './components/review/CreateOpinion.svelte'
import CreateReviewCategory from './components/review/CreateReviewCategory.svelte' import CreateReviewCategory from './components/review/CreateReviewCategory.svelte'
import EditReview from './components/review/EditReview.svelte' import EditReview from './components/review/EditReview.svelte'
import EditReviewCategory from './components/review/EditReviewCategory.svelte' import EditReviewCategory from './components/review/EditReviewCategory.svelte'
import KanbanReviewCard from './components/review/KanbanReviewCard.svelte'
import OpinionPresenter from './components/review/OpinionPresenter.svelte' import OpinionPresenter from './components/review/OpinionPresenter.svelte'
import OpinionsPresenter from './components/review/OpinionsPresenter.svelte' import OpinionsPresenter from './components/review/OpinionsPresenter.svelte'
import Opinions from './components/review/Opinions.svelte' import Opinions from './components/review/Opinions.svelte'
@ -48,8 +47,8 @@ import VacancyItemPresenter from './components/VacancyItemPresenter.svelte'
import VacancyPresenter from './components/VacancyPresenter.svelte' import VacancyPresenter from './components/VacancyPresenter.svelte'
import VacancyCountPresenter from './components/VacancyCountPresenter.svelte' import VacancyCountPresenter from './components/VacancyCountPresenter.svelte'
import VacancyModifiedPresenter from './components/VacancyModifiedPresenter.svelte' import VacancyModifiedPresenter from './components/VacancyModifiedPresenter.svelte'
import ReviewCategoryPresenter from './components/review/ReviewCategoryPresenter.svelte'
import recruit from './plugin' import recruit from './plugin'
import PersonsPresenter from './components/review/PersonsPresenter.svelte'
async function createApplication (object: Doc): Promise<void> { async function createApplication (object: Doc): Promise<void> {
showPopup(CreateApplication, { candidate: object._id, preserveCandidate: true }) showPopup(CreateApplication, { candidate: object._id, preserveCandidate: true })
@ -163,12 +162,11 @@ export default async (): Promise<Resources> => ({
CreateReview, CreateReview,
ReviewPresenter, ReviewPresenter,
EditReview, EditReview,
KanbanReviewCard,
Reviews, Reviews,
Opinions, Opinions,
OpinionPresenter, OpinionPresenter,
OpinionsPresenter, OpinionsPresenter,
PersonsPresenter ReviewCategoryPresenter
}, },
completion: { completion: {
ApplicationQuery: async (client: Client, query: string) => await queryApplication(client, query) ApplicationQuery: async (client: Client, query: string) => await queryApplication(client, query)

View File

@ -101,9 +101,7 @@ export default mergeIds(recruitId, recruit, {
StartDate: '' as IntlString, StartDate: '' as IntlString,
DueDate: '' as IntlString, DueDate: '' as IntlString,
CandidateReviews: '' as IntlString, CandidateReviews: '' as IntlString,
Participants: '' as IntlString, AddDescription: '' as IntlString
NoParticipants: '' as IntlString,
PersonsLabel: '' as IntlString
}, },
space: { space: {
CandidatesPublic: '' as Ref<Space> CandidatesPublic: '' as Ref<Space>
@ -116,7 +114,6 @@ export default mergeIds(recruitId, recruit, {
VacancyItemPresenter: '' as AnyComponent, VacancyItemPresenter: '' as AnyComponent,
VacancyCountPresenter: '' as AnyComponent, VacancyCountPresenter: '' as AnyComponent,
OpinionsPresenter: '' as AnyComponent, OpinionsPresenter: '' as AnyComponent,
PersonsPresenter: '' as AnyComponent,
VacancyModifiedPresenter: '' as AnyComponent VacancyModifiedPresenter: '' as AnyComponent
} }
}) })

View File

@ -31,6 +31,7 @@
"@anticrm/contact": "~0.6.5", "@anticrm/contact": "~0.6.5",
"@anticrm/chunter": "~0.6.1", "@anticrm/chunter": "~0.6.1",
"@anticrm/task": "~0.6.0", "@anticrm/task": "~0.6.0",
"@anticrm/calendar": "~0.6.0",
"@anticrm/ui": "~0.6.0" "@anticrm/ui": "~0.6.0"
} }
} }

View File

@ -13,7 +13,8 @@
// limitations under the License. // limitations under the License.
// //
import type { Employee, Organization, Person } from '@anticrm/contact' import { Calendar, Event } from '@anticrm/calendar'
import type { Organization, Person } from '@anticrm/contact'
import type { AttachedDoc, Class, Doc, Mixin, Ref, Space, Timestamp } from '@anticrm/core' import type { AttachedDoc, Class, Doc, Mixin, Ref, Space, Timestamp } from '@anticrm/core'
import type { Asset, Plugin } from '@anticrm/platform' import type { Asset, Plugin } from '@anticrm/platform'
import { plugin } from '@anticrm/platform' import { plugin } from '@anticrm/platform'
@ -34,10 +35,10 @@ export interface Vacancy extends SpaceWithStates {
/** /**
* @public * @public
*/ */
export interface ReviewCategory extends SpaceWithStates { export interface ReviewCategory extends Calendar {
fullDescription?: string fullDescription?: string
attachments?: number attachments?: number
company?: Ref<Organization> comments?: number
} }
/** /**
@ -70,22 +71,14 @@ export interface Applicant extends Task {
/** /**
* @public * @public
*/ */
export interface Review extends Task { export interface Review extends Event {
attachedTo: Ref<Candidate> attachedTo: Ref<Candidate>
attachments?: number
comments?: number
description: string
verdict: string verdict: string
location?: string company?: Ref<Organization>
company?: string
startDate: Timestamp | null
dueDate: Timestamp | null
opinions?: number opinions?: number
participants?: Ref<Employee>[]
} }
/** /**

View File

@ -15,6 +15,7 @@
--> -->
<script lang="ts"> <script lang="ts">
import { TypeDate } from '@anticrm/core'
import { IntlString } from '@anticrm/platform' import { IntlString } from '@anticrm/platform'
import { DatePopup, showPopup } from '@anticrm/ui' import { DatePopup, showPopup } from '@anticrm/ui'
import DatePresenter from './DatePresenter.svelte' import DatePresenter from './DatePresenter.svelte'
@ -22,6 +23,9 @@
export let value: number | Date | undefined export let value: number | Date | undefined
export let label: IntlString export let label: IntlString
export let onChange: (value: any) => void export let onChange: (value: any) => void
export let attributeType: TypeDate | undefined
$: date = value ? new Date(value) : new Date() $: date = value ? new Date(value) : new Date()
let container: HTMLElement let container: HTMLElement
let opened: boolean = false let opened: boolean = false
@ -31,7 +35,7 @@
on:click|preventDefault={() => { on:click|preventDefault={() => {
if (!opened) { if (!opened) {
opened = true opened = true
showPopup(DatePopup, { selected: date, title: label }, container, (result) => { showPopup(DatePopup, { value: date, title: label, withTime: attributeType?.withTime ?? false }, container, (result) => {
if (result) { if (result) {
value = result.getTime() value = result.getTime()
onChange(value) onChange(value)
@ -40,5 +44,5 @@
}) })
} }
}} > }} >
<DatePresenter {value} /> <DatePresenter {value} {attributeType} />
</div> </div>

View File

@ -15,16 +15,18 @@
--> -->
<script lang="ts"> <script lang="ts">
import { TypeDate } from '@anticrm/core'
import { DatePresenter } from '@anticrm/ui' import { DatePresenter } from '@anticrm/ui'
export let value: number | Date | undefined export let value: number | Date | undefined
export let attributeType: TypeDate | undefined
$: date = value ? new Date(value) : undefined $: date = value ? new Date(value) : undefined
</script> </script>
<div class="antiSelect"> <div class="antiSelect">
{#if date} {#if date}
<DatePresenter value={date} /> <DatePresenter value={date} withTime={attributeType?.withTime ?? false} />
{:else} {:else}
No date No date
{/if} {/if}

View File

@ -0,0 +1,26 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { IntlString } from '@anticrm/platform'
import { Label } from '@anticrm/ui'
export let value: IntlString
</script>
<span class="lines-limit-2">
<Label label={value}/>
</span>

View File

@ -23,6 +23,7 @@ import DateEditor from './components/DateEditor.svelte'
import DatePresenter from './components/DatePresenter.svelte' import DatePresenter from './components/DatePresenter.svelte'
import StringEditor from './components/StringEditor.svelte' import StringEditor from './components/StringEditor.svelte'
import StringPresenter from './components/StringPresenter.svelte' import StringPresenter from './components/StringPresenter.svelte'
import IntlStringPresenter from './components/IntlStringPresenter.svelte'
import NumberEditor from './components/NumberEditor.svelte' import NumberEditor from './components/NumberEditor.svelte'
import NumberPresenter from './components/NumberPresenter.svelte' import NumberPresenter from './components/NumberPresenter.svelte'
import Table from './components/Table.svelte' import Table from './components/Table.svelte'
@ -80,6 +81,7 @@ export default async (): Promise<Resources> => ({
RolePresenter, RolePresenter,
ObjectPresenter, ObjectPresenter,
EditDoc, EditDoc,
HTMLPresenter HTMLPresenter,
IntlStringPresenter
} }
}) })

View File

@ -98,12 +98,14 @@ async function getAttributePresenter (
const resultKey = preserveKey.sortingKey ?? preserveKey.key const resultKey = preserveKey.sortingKey ?? preserveKey.key
const sortingKey = attribute.type._class === core.class.ArrOf ? resultKey + '.length' : resultKey const sortingKey = attribute.type._class === core.class.ArrOf ? resultKey + '.length' : resultKey
const presenter = await getResource(presenterMixin.presenter) const presenter = await getResource(presenterMixin.presenter)
return { return {
key: preserveKey.key, key: preserveKey.key,
sortingKey, sortingKey,
_class: attrClass, _class: attrClass,
label: preserveKey.label ?? attribute.label, label: preserveKey.label ?? attribute.label,
presenter, presenter,
props: { attributeType: attribute.type },
icon: presenterMixin.icon, icon: presenterMixin.icon,
attribute attribute
} }
@ -215,7 +217,7 @@ export async function getActions (
export async function deleteObject (client: TxOperations, object: Doc): Promise<void> { export async function deleteObject (client: TxOperations, object: Doc): Promise<void> {
const hierarchy = client.getHierarchy() const hierarchy = client.getHierarchy()
const promises: Promise<any>[] = [] const promises: Array<Promise<any>> = []
if (client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) { if (client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) {
const adoc = object as AttachedDoc const adoc = object as AttachedDoc
promises.push(client.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection).catch(err => console.error(err))) promises.push(client.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection).catch(err => console.error(err)))
@ -261,7 +263,7 @@ function getParentClass (hierarchy: Hierarchy, _class: Ref<Class<Doc>>): Ref<Cla
return result return result
} }
function getMixins (hierarchy: Hierarchy, _class: Ref<Class<Doc>>, object: Doc): Ref<Mixin<Doc>>[] { function getMixins (hierarchy: Hierarchy, _class: Ref<Class<Doc>>, object: Doc): Array<Ref<Mixin<Doc>>> {
const parentClass = getParentClass(hierarchy, _class) const parentClass = getParentClass(hierarchy, _class)
const descendants = hierarchy.getDescendants(parentClass) const descendants = hierarchy.getDescendants(parentClass)
return descendants.filter( return descendants.filter(

View File

@ -65,8 +65,12 @@
{#if spaceSample !== undefined && model} {#if spaceSample !== undefined && model}
<Table <Table
_class={spaceSample._class} _class={spaceSample._class}
config={['', 'company', 'location', 'modifiedOn']} config={['', '$lookup._class.label', 'modifiedOn']}
options={{}} options={{
lookup: {
_class: core.class.Class
}
}}
showNotification showNotification
baseMenuClass={core.class.Space} baseMenuClass={core.class.Space}
query={{ query={{

View File

@ -1131,5 +1131,25 @@
"projectFolder": "server-plugins/setting-resources", "projectFolder": "server-plugins/setting-resources",
"shouldPublish": true "shouldPublish": true
}, },
{
"packageName": "@anticrm/calendar",
"projectFolder": "plugins/calendar",
"shouldPublish": true
},
{
"packageName": "@anticrm/calendar-assets",
"projectFolder": "plugins/calendar-assets",
"shouldPublish": true
},
{
"packageName": "@anticrm/calendar-resources",
"projectFolder": "plugins/calendar-resources",
"shouldPublish": true
},
{
"packageName": "@anticrm/model-calendar",
"projectFolder": "models/calendar",
"shouldPublish": true
},
] ]
} }