diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 20a44b1b27..4c9d2810d5 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -13,6 +13,9 @@ specifiers: '@rush-temp/attachment': file:./projects/attachment.tgz '@rush-temp/attachment-assets': file:./projects/attachment-assets.tgz '@rush-temp/attachment-resources': file:./projects/attachment-resources.tgz + '@rush-temp/automation': file:./projects/automation.tgz + '@rush-temp/automation-assets': file:./projects/automation-assets.tgz + '@rush-temp/automation-resources': file:./projects/automation-resources.tgz '@rush-temp/board': file:./projects/board.tgz '@rush-temp/board-assets': file:./projects/board-assets.tgz '@rush-temp/board-resources': file:./projects/board-resources.tgz @@ -61,6 +64,7 @@ specifiers: '@rush-temp/model-activity': file:./projects/model-activity.tgz '@rush-temp/model-all': file:./projects/model-all.tgz '@rush-temp/model-attachment': file:./projects/model-attachment.tgz + '@rush-temp/model-automation': file:./projects/model-automation.tgz '@rush-temp/model-board': file:./projects/model-board.tgz '@rush-temp/model-calendar': file:./projects/model-calendar.tgz '@rush-temp/model-chunter': file:./projects/model-chunter.tgz @@ -314,6 +318,9 @@ dependencies: '@rush-temp/attachment': file:projects/attachment.tgz '@rush-temp/attachment-assets': file:projects/attachment-assets.tgz_typescript@4.7.2 '@rush-temp/attachment-resources': file:projects/attachment-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c + '@rush-temp/automation': file:projects/automation.tgz + '@rush-temp/automation-assets': file:projects/automation-assets.tgz_typescript@4.7.2 + '@rush-temp/automation-resources': file:projects/automation-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c '@rush-temp/board': file:projects/board.tgz '@rush-temp/board-assets': file:projects/board-assets.tgz_typescript@4.7.2 '@rush-temp/board-resources': file:projects/board-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c @@ -362,6 +369,7 @@ dependencies: '@rush-temp/model-activity': file:projects/model-activity.tgz_typescript@4.7.2 '@rush-temp/model-all': file:projects/model-all.tgz_typescript@4.7.2 '@rush-temp/model-attachment': file:projects/model-attachment.tgz_typescript@4.7.2 + '@rush-temp/model-automation': file:projects/model-automation.tgz_typescript@4.7.2 '@rush-temp/model-board': file:projects/model-board.tgz_typescript@4.7.2 '@rush-temp/model-calendar': file:projects/model-calendar.tgz_typescript@4.7.2 '@rush-temp/model-chunter': file:projects/model-chunter.tgz_typescript@4.7.2 @@ -10202,6 +10210,90 @@ packages: - supports-color dev: false + file:projects/automation-assets.tgz_typescript@4.7.2: + resolution: {integrity: sha512-IwQs0Nc95dL+Xacvp+ETxvlN9rlWdGXk24FqpHjQi/wRvH2/VucZZAhqCpB1o9H5THFNpOiPHXSQ4RspNVzwBA==, tarball: file:projects/automation-assets.tgz} + id: file:projects/automation-assets.tgz + name: '@rush-temp/automation-assets' + version: 0.0.0 + dependencies: + '@rushstack/heft': 0.45.5 + '@types/heft-jest': 1.0.2 + '@types/node': 16.11.38 + '@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5 + '@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2 + eslint: 7.32.0 + eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a + eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c + eslint-plugin-node: 11.1.0_eslint@7.32.0 + eslint-plugin-promise: 5.2.0_eslint@7.32.0 + prettier: 2.6.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - typescript + dev: false + + file:projects/automation-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c: + resolution: {integrity: sha512-Ee3vgtig0qsuUIGOvSOXAinzXLSHF0MUTGqbMCqkDt6H5CZhwrqZHfPpgaBozXHOIV3HVnZ1gxQam4cMo2dTNw==, tarball: file:projects/automation-resources.tgz} + id: file:projects/automation-resources.tgz + name: '@rush-temp/automation-resources' + version: 0.0.0 + dependencies: + '@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5 + '@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2 + eslint: 7.32.0 + eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a + eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c + eslint-plugin-node: 11.1.0_eslint@7.32.0 + eslint-plugin-promise: 5.2.0_eslint@7.32.0 + eslint-plugin-svelte3: 4.0.0_eslint@7.32.0+svelte@3.48.0 + prettier: 2.6.2 + prettier-plugin-svelte: 2.7.0_prettier@2.6.2+svelte@3.48.0 + sass: 1.52.2 + svelte: 3.48.0 + svelte-check: 2.7.2_c1788f0bf13b393830d6c30602bd01af + svelte-loader: 3.1.3_svelte@3.48.0 + svelte-preprocess: 4.10.6_0757fe126296bf9639251bcd13609b29 + typescript: 4.7.2 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - less + - node-sass + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + dev: false + + file:projects/automation.tgz: + resolution: {integrity: sha512-qUBX9l8mso6djseOkGoMs1P791OVqOOKKMEV+Mi2U4h4uVQKJKekfMmfZfK6ArRM0yXiB3AGlfu9tdTiNy7QHg==, tarball: file:projects/automation.tgz} + name: '@rush-temp/automation' + version: 0.0.0 + dependencies: + '@rushstack/heft': 0.45.5 + '@types/heft-jest': 1.0.2 + '@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5 + '@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2 + eslint: 7.32.0 + eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a + eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c + eslint-plugin-node: 11.1.0_eslint@7.32.0 + eslint-plugin-promise: 5.2.0_eslint@7.32.0 + prettier: 2.6.2 + simplytyped: 3.3.0_typescript@4.7.2 + typescript: 4.7.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: false + file:projects/board-assets.tgz_typescript@4.7.2: resolution: {integrity: sha512-cnaqyRiwKUSLi9RgFQpYqIN4PyW85s77OzOovNq1cT0cJqHycMJN1CaXIDIBrlTrFS0TCOeTpUyMamq0b0rBOQ==, tarball: file:projects/board-assets.tgz} id: file:projects/board-assets.tgz @@ -11466,7 +11558,7 @@ packages: dev: false file:projects/model-all.tgz_typescript@4.7.2: - resolution: {integrity: sha512-VrR3QpQKo2qizq7xi/uS9Jj+OyrB/mPDLnAc8/pvwEx8jF5PwySSsP63uSNGcO3fwv7ZjZvAiuvEnaIHUZ5Ivg==, tarball: file:projects/model-all.tgz} + resolution: {integrity: sha512-cqTW87WKHFYNzT4PSJJesrbiXrMIUP7byOo6OubKKnXsRltLG/aUWj+v24tg4gOX0vLk+/HsZ/pLS0fqXJ5RuQ==, tarball: file:projects/model-all.tgz} id: file:projects/model-all.tgz name: '@rush-temp/model-all' version: 0.0.0 @@ -11515,8 +11607,31 @@ packages: - typescript dev: false + file:projects/model-automation.tgz_typescript@4.7.2: + resolution: {integrity: sha512-V/knMbpqayHquNsaGCVgZDR6RVCglOkJFlko9UswT+4t8LeDTN5aBFjupz+XDenoXt3BVNrqyIoPE95u87fYWw==, tarball: file:projects/model-automation.tgz} + id: file:projects/model-automation.tgz + name: '@rush-temp/model-automation' + version: 0.0.0 + dependencies: + '@rushstack/heft': 0.45.5 + '@types/heft-jest': 1.0.2 + '@typescript-eslint/eslint-plugin': 5.27.0_738fa17fa57f8a69ace69c90e5cfa1d5 + '@typescript-eslint/parser': 5.27.0_eslint@7.32.0+typescript@4.7.2 + eslint: 7.32.0 + eslint-config-standard-with-typescript: 21.0.1_99a5fe2f2ae1dc64d6b59974c931eb2a + eslint-plugin-import: 2.26.0_c21022bc9feaeb7b200d3d631eeae46c + eslint-plugin-node: 11.1.0_eslint@7.32.0 + eslint-plugin-promise: 5.2.0_eslint@7.32.0 + prettier: 2.6.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - typescript + dev: false + file:projects/model-board.tgz_typescript@4.7.2: - resolution: {integrity: sha512-BDGUx3OP3/eowFfmRWMZnFcAj9Oa6orfKBNrYh6F2lqKhBeDwBGvdSYurtm2n4/ISaK0bZaSgM0reRKn63oDsA==, tarball: file:projects/model-board.tgz} + resolution: {integrity: sha512-SChn62gAEcqTy7E0oX/2zY2kMaLuE+GzeUHahQMC+Ev1Z2uj5SdkjL3P1wBnKk9+RnN/v54UuFhmw1feVBwRCw==, tarball: file:projects/model-board.tgz} id: file:projects/model-board.tgz name: '@rush-temp/model-board' version: 0.0.0 @@ -12873,7 +12988,7 @@ packages: dev: false file:projects/prod.tgz_d1c3762ecb2c185353d3f02936f6ec22: - resolution: {integrity: sha512-Ruj8noHdrzO1bGVEYxLSgza5VdloFea3VbtIutq8UnZThqIzyBmJRsEo1lLmN5WAKBW50oEBKsKDZxuXB/ZuYQ==, tarball: file:projects/prod.tgz} + resolution: {integrity: sha512-lLcDC0hOQAwp7ZoxN75OI6FScb/VuSQTMzGyBTxk1VP46pQnGe3U6vjcNK18oc7Sesuk6kcb7Uot65tWgB01eg==, tarball: file:projects/prod.tgz} id: file:projects/prod.tgz name: '@rush-temp/prod' version: 0.0.0 diff --git a/dev/prod/package.json b/dev/prod/package.json index 72fa30ab6c..7039f97600 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -71,6 +71,9 @@ "@anticrm/activity": "~0.6.0", "@anticrm/activity-assets": "~0.6.0", "@anticrm/activity-resources": "~0.6.0", + "@anticrm/automation": "~0.6.0", + "@anticrm/automation-assets": "~0.6.0", + "@anticrm/automation-resources": "~0.6.0", "@anticrm/telegram": "~0.6.2", "@anticrm/telegram-assets": "~0.6.0", "@anticrm/telegram-resources": "~0.6.0", diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 207a722277..7c7cc507d9 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -24,6 +24,7 @@ import { contactId } from '@anticrm/contact' import { chunterId } from '@anticrm/chunter' import { recruitId } from '@anticrm/recruit' import { activityId } from '@anticrm/activity' +import { automationId } from '@anticrm/automation' import { settingId } from '@anticrm/setting' import { telegramId } from '@anticrm/telegram' import { attachmentId } from '@anticrm/attachment' @@ -50,6 +51,7 @@ import '@anticrm/attachment-assets' import '@anticrm/contact-assets' import '@anticrm/recruit-assets' import '@anticrm/activity-assets' +import '@anticrm/automation-assets' import '@anticrm/setting-assets' import '@anticrm/telegram-assets' import '@anticrm/lead-assets' @@ -117,6 +119,7 @@ export async function configurePlatform() { addLocation(trackerId, () => import(/* webpackChunkName: "tracker" */ '@anticrm/tracker-resources')) addLocation(boardId, () => import(/* webpackChunkName: "board" */ '@anticrm/board-resources')) + addLocation(automationId, () => import(/* webpackChunkName: "automation" */ '@anticrm/automation-resources')) addLocation(hrId, () => import(/* webpackChunkName: "hr" */ '@anticrm/hr-resources')) setMetadata(workbench.metadata.PlatformTitle, 'Platform') diff --git a/models/all/package.json b/models/all/package.json index 4edbef3314..eb9f7e8b0a 100644 --- a/models/all/package.json +++ b/models/all/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@anticrm/model": "~0.6.0", + "@anticrm/model-automation": "~0.6.0", "@anticrm/model-core": "~0.6.0", "@anticrm/model-view": "~0.6.0", "@anticrm/model-workbench": "~0.6.1", diff --git a/models/all/src/index.ts b/models/all/src/index.ts index 752856c83d..9665e63172 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -19,6 +19,7 @@ import jsonVersion from './version.json' import { Builder } from '@anticrm/model' import { createModel as activityModel } from '@anticrm/model-activity' import { createModel as attachmentModel } from '@anticrm/model-attachment' +import { createModel as automationModel } from '@anticrm/model-automation' import { createModel as chunterModel } from '@anticrm/model-chunter' import { createModel as contactModel } from '@anticrm/model-contact' import { createModel as coreModel } from '@anticrm/model-core' @@ -105,7 +106,8 @@ const builders: [(b: Builder) => void, string][] = [ [serverHrModel, 'server-hr'], [trackerModel, 'tracker'], [boardModel, 'board'], - [calendarModel, 'calendar'] + [calendarModel, 'calendar'], + [automationModel, 'automation'] ] for (const [b, id] of builders) { diff --git a/models/all/src/migration.ts b/models/all/src/migration.ts index 87647317ed..1e0398adbc 100644 --- a/models/all/src/migration.ts +++ b/models/all/src/migration.ts @@ -16,6 +16,7 @@ // Import migrate operations. import { MigrateOperation } from '@anticrm/model' import { attachmentOperation } from '@anticrm/model-attachment' +import { automationOperation } from '@anticrm/model-automation' import { chunterOperation } from '@anticrm/model-chunter' import { contactOperation } from '@anticrm/model-contact' import { coreOperation } from '@anticrm/model-core' @@ -43,6 +44,7 @@ export const migrateOperations: MigrateOperation[] = [ telegramOperation, taskOperation, attachmentOperation, + automationOperation, leadOperation, recruitOperation, viewOperation, diff --git a/models/automation/.eslintrc.js b/models/automation/.eslintrc.js new file mode 100644 index 0000000000..c3c2c49417 --- /dev/null +++ b/models/automation/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@anticrm/model-rig/profiles/default/config/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/automation/config/rig.json b/models/automation/config/rig.json new file mode 100644 index 0000000000..e9a9ee9add --- /dev/null +++ b/models/automation/config/rig.json @@ -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" +} diff --git a/models/automation/package.json b/models/automation/package.json new file mode 100644 index 0000000000..ee2678efe7 --- /dev/null +++ b/models/automation/package.json @@ -0,0 +1,38 @@ +{ + "name": "@anticrm/model-automation", + "version": "0.6.0", + "main": "lib/index.js", + "author": "Anticrm Platform Contributors", + "license": "EPL-2.0", + "scripts": { + "build": "heft build", + "build:watch": "tsc", + "lint:fix": "eslint --fix src", + "lint": "eslint src", + "format": "prettier --write src && eslint --fix src" + }, + "devDependencies": { + "@anticrm/model-rig": "~0.6.0", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-promise": "^5.1.1", + "eslint-plugin-node": "^11.1.0", + "eslint": "^7.32.0", + "@types/heft-jest": "^1.0.2", + "@typescript-eslint/parser": "^5.4.0", + "eslint-config-standard-with-typescript": "^21.0.1", + "prettier": "^2.4.1", + "@rushstack/heft": "^0.45.5" + }, + "dependencies": { + "@anticrm/core": "~0.6.16", + "@anticrm/automation": "~0.6.0", + "@anticrm/model": "~0.6.0", + "@anticrm/model-core": "~0.6.0", + "@anticrm/view": "~0.6.0", + "@anticrm/setting": "~0.6.1", + "@anticrm/platform": "~0.6.6", + "@anticrm/automation-resources": "~0.6.0" + } +} + diff --git a/models/automation/src/index.ts b/models/automation/src/index.ts new file mode 100644 index 0000000000..bd060d4960 --- /dev/null +++ b/models/automation/src/index.ts @@ -0,0 +1,93 @@ +// +// 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. +// + +// To help typescript locate view plugin properly +import automation, { + Automation, + AutomationSupport, + AttributeAutomationSupport, + AutomationSortSupport, + AutomationTriggerSupport, + Command, + TriggerType +} from '@anticrm/automation' +import { Class, Doc, Domain, Ref } from '@anticrm/core' +import { Builder, Mixin, Model, Prop, TypeString, UX } from '@anticrm/model' +import core, { TAttachedDoc, TClass } from '@anticrm/model-core' +import setting from '@anticrm/setting' +import view from '@anticrm/view' + +import plugin from './plugin' + +export const DOMAIN_AUTOMATION = 'automation' as Domain + +@Model(automation.class.Automation, core.class.AttachedDoc, DOMAIN_AUTOMATION) +@UX(automation.string.Automation) +export class TAutomation extends TAttachedDoc implements Automation { + @Prop(TypeString(), core.string.Name) + name!: string + + @Prop(TypeString(), core.string.Description) + description!: string | null + + targetClass!: Ref> | null + declare trigger: { + type: TriggerType + } + + declare commands: Command[] +} + +@Mixin(automation.mixin.AutomationSupport, core.class.Class) +export class TAutomationSupport extends TClass implements AutomationSupport { + declare attributes: AttributeAutomationSupport[] + declare trigger: AutomationTriggerSupport + sort?: AutomationSortSupport +} + +export function createModel (builder: Builder): void { + builder.createModel(TAutomation, TAutomationSupport) + builder.createDoc( + core.class.Space, + core.space.Model, + { + archived: false, + description: 'Automation space', + members: [], + name: 'Automation space', + private: true + }, + automation.space.Automation + ) + builder.createDoc( + setting.class.SettingsCategory, + core.space.Model, + { + name: 'automation', + label: automation.string.Automation, + icon: automation.icon.Automation, // TODO: update icon + component: plugin.component.AutomationSettingsElement, + order: 3600 + }, + plugin.ids.Automation + ) + + // TODO: Enable when server triggers are added + builder.mixin(automation.class.Automation, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete] + }) +} + +export { automationOperation } from './migration' diff --git a/models/automation/src/migration.ts b/models/automation/src/migration.ts new file mode 100644 index 0000000000..5d9daa0f5b --- /dev/null +++ b/models/automation/src/migration.ts @@ -0,0 +1,21 @@ +// +// Copyright © 2022 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model' + +export const automationOperation: MigrateOperation = { + async migrate (client: MigrationClient): Promise {}, + async upgrade (client: MigrationUpgradeClient): Promise {} +} diff --git a/models/automation/src/plugin.ts b/models/automation/src/plugin.ts new file mode 100644 index 0000000000..24204bc267 --- /dev/null +++ b/models/automation/src/plugin.ts @@ -0,0 +1,29 @@ +// +// Copyright © 2022 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { Ref } from '@anticrm/core' +import { automationId } from '@anticrm/automation' +import automation, { PluginType } from '@anticrm/automation-resources/src/plugin' +import { mergeIds } from '@anticrm/platform' +import { SettingsCategory } from '@anticrm/setting' + +const pluginData = { + ids: { + Automation: '' as Ref + } +} +const automations: PluginType & typeof pluginData = mergeIds(automationId, automation, pluginData) + +export default automations diff --git a/models/automation/tsconfig.json b/models/automation/tsconfig.json new file mode 100644 index 0000000000..1d60db76b4 --- /dev/null +++ b/models/automation/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./node_modules/@anticrm/model-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + } +} \ No newline at end of file diff --git a/models/board/package.json b/models/board/package.json index 44c48c3f2a..c0eba6ec33 100644 --- a/models/board/package.json +++ b/models/board/package.json @@ -36,6 +36,7 @@ "@anticrm/model-contact": "~0.6.1", "@anticrm/model-chunter": "~0.6.0", "@anticrm/model-attachment": "~0.6.0", + "@anticrm/automation": "~0.6.0", "@anticrm/board": "~0.6.0", "@anticrm/board-resources": "~0.6.0", "@anticrm/view": "~0.6.0", diff --git a/models/board/src/index.ts b/models/board/src/index.ts index 5174279256..67dde4348b 100644 --- a/models/board/src/index.ts +++ b/models/board/src/index.ts @@ -14,9 +14,10 @@ // // To help typescript locate view plugin properly +import automation, { AutomationSupport } from '@anticrm/automation' import type { Board, Card, MenuPage, CommonBoardPreference, CardCover } from '@anticrm/board' import type { Employee } from '@anticrm/contact' -import { DOMAIN_MODEL, IndexKind, Markup, Ref, Type } from '@anticrm/core' +import { Class, DOMAIN_MODEL, IndexKind, Markup, Ref, Type } from '@anticrm/core' import { ArrOf, Builder, @@ -443,6 +444,56 @@ export function createModel (builder: Builder): void { builder.mixin(board.class.Card, core.class.Class, view.mixin.IgnoreActions, { actions: [view.action.Delete, task.action.Move] }) + builder.mixin, AutomationSupport>( + board.class.Card, + core.class.Class, + automation.mixin.AutomationSupport, + { + attributes: [ + { + name: 'isArchived' + }, + { + name: 'title' + }, + { + name: 'description' + } + ], + trigger: { + action: { + mode: ['context', 'editor'] + } + } + } + ) + + builder.mixin, AutomationSupport>( + board.class.Board, + core.class.Class, + automation.mixin.AutomationSupport, + { + attributes: [ + { + name: 'name' + }, + { + name: 'description' + }, + { + name: 'private' + }, + { + name: 'archived' + } + ], + trigger: { + action: { + mode: ['context'] + } + } + } + ) // TODO: update query when nested query is available createAction( diff --git a/models/core/src/component.ts b/models/core/src/component.ts index efd2b3ff10..6b4567baeb 100644 --- a/models/core/src/component.ts +++ b/models/core/src/component.ts @@ -18,7 +18,6 @@ import { IntlString, mergeIds } from '@anticrm/platform' export default mergeIds(coreId, core, { string: { - Description: '' as IntlString, Private: '' as IntlString, Archived: '' as IntlString, ClassLabel: '' as IntlString, diff --git a/models/view/src/plugin.ts b/models/view/src/plugin.ts index 76a9230f20..43d99fadf3 100644 --- a/models/view/src/plugin.ts +++ b/models/view/src/plugin.ts @@ -13,33 +13,13 @@ // limitations under the License. // -import { ObjQueryType, Ref } from '@anticrm/core' +import { ObjQueryType } from '@anticrm/core' import { IntlString, mergeIds, Resource } from '@anticrm/platform' import type { AnyComponent } from '@anticrm/ui' -import { Action, Filter, ViewAction, viewId } from '@anticrm/view' +import { Filter, ViewAction, viewId } from '@anticrm/view' import view from '@anticrm/view-resources/src/plugin' export default mergeIds(viewId, view, { - action: { - Delete: '' as Ref, - Move: '' as Ref, - MoveLeft: '' as Ref, - MoveRight: '' as Ref, - MoveUp: '' as Ref, - MoveDown: '' as Ref, - - SelectItem: '' as Ref, - SelectItemAll: '' as Ref, - SelectItemNone: '' as Ref, - SelectUp: '' as Ref, - SelectDown: '' as Ref, - - ShowPreview: '' as Ref, - ShowActions: '' as Ref, - - // Edit document - Open: '' as Ref - }, actionImpl: { Delete: '' as ViewAction, Move: '' as ViewAction, diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 57c52e892a..2c5c0b29d8 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -133,6 +133,7 @@ export default plugin(coreId, { Array: '' as IntlString, Bag: '' as IntlString, Name: '' as IntlString, + Description: '' as IntlString, Enum: '' as IntlString } }) diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 43fc241917..42c0023a34 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -17,7 +17,7 @@ import { addLocation, addStringsLoader } from '@anticrm/platform' import { SvelteComponent } from 'svelte' import { readable } from 'svelte/store' import Root from './components/internal/Root.svelte' -import { uiId } from './plugin' +import { uiId, uis } from './plugin' export type { AnyComponent, @@ -171,4 +171,4 @@ addStringsLoader(uiId, async (lang: string) => { addLocation(uiId, async () => ({ default: async () => ({}) })) -export { default } from './plugin' +export default uis diff --git a/packages/ui/src/plugin.ts b/packages/ui/src/plugin.ts index 2688d8819f..f695f2c84b 100644 --- a/packages/ui/src/plugin.ts +++ b/packages/ui/src/plugin.ts @@ -23,7 +23,7 @@ import { AnyComponent } from './types' */ export const uiId = 'ui' as Plugin -export default plugin(uiId, { +export const uis = plugin(uiId, { string: { EditBoxPlaceholder: '' as IntlString, Ok: '' as IntlString, @@ -69,3 +69,5 @@ export default plugin(uiId, { DefaultApplication: '' as Metadata } }) + +export default uis diff --git a/plugins/automation-assets/.eslintrc.js b/plugins/automation-assets/.eslintrc.js new file mode 100644 index 0000000000..426e4c33f7 --- /dev/null +++ b/plugins/automation-assets/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@anticrm/platform-rig/profiles/assets/config/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/plugins/automation-assets/assets/icons.svg b/plugins/automation-assets/assets/icons.svg new file mode 100644 index 0000000000..6f151a452c --- /dev/null +++ b/plugins/automation-assets/assets/icons.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/automation-assets/config/rig.json b/plugins/automation-assets/config/rig.json new file mode 100644 index 0000000000..46f6e60c92 --- /dev/null +++ b/plugins/automation-assets/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@anticrm/platform-rig", + "rigProfile": "assets" +} diff --git a/plugins/automation-assets/lang/en.json b/plugins/automation-assets/lang/en.json new file mode 100644 index 0000000000..08b618cf26 --- /dev/null +++ b/plugins/automation-assets/lang/en.json @@ -0,0 +1,20 @@ +{ + "string": { + "Automation": "Automation", + "Actions": "Actions", + "Chat": "Chat", + "Content": "Content", + "Dates": "Dates", + "Tracker": "Tracker", + "Trigger": "Trigger", + "Set": "Set", + "To": "to", + "Mode": "Mode", + "AddMenu": "Add menu", + "Menu": "Menu", + "Icon": "Icon", + "SelectClass": "Select class", + "In": "in", + "Update": "Update" + } +} diff --git a/plugins/automation-assets/lang/ru.json b/plugins/automation-assets/lang/ru.json new file mode 100644 index 0000000000..d6c40ce47f --- /dev/null +++ b/plugins/automation-assets/lang/ru.json @@ -0,0 +1,20 @@ +{ + "string": { + "Automation": "Автоматизация", + "Actions": "Действия", + "Chat": "Чат", + "Content": "Контент", + "Dates": "Даты", + "Tracker": "Трекер", + "Trigger": "Триггер", + "Set": "Присвоить", + "To": "значение", + "Mode": "Режим", + "AddMenu": "Добавить меню", + "Menu": "Menu", + "Icon": "Изображение", + "SelectClass": "Выберите класс", + "In": "в", + "Update": "Обновить" + } +} \ No newline at end of file diff --git a/plugins/automation-assets/package.json b/plugins/automation-assets/package.json new file mode 100644 index 0000000000..f38df097cb --- /dev/null +++ b/plugins/automation-assets/package.json @@ -0,0 +1,34 @@ +{ + "name": "@anticrm/automation-assets", + "version": "0.6.0", + "main": "lib/index.js", + "author": "Anticrm Platform Contributors", + "template": "@anticrm/assets-package", + "license": "EPL-2.0", + "scripts": { + "build": "heft build", + "build:docs": "", + "lint": "eslint src", + "lint:fix": "eslint --fix src", + "format": "prettier --write src && eslint --fix src", + "build:watch": "tsc" + }, + "devDependencies": { + "@types/heft-jest": "^1.0.2", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.4.0", + "eslint-config-standard-with-typescript": "^21.0.1", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.1", + "eslint": "^7.32.0", + "prettier": "^2.4.1", + "@rushstack/heft": "^0.45.5", + "@types/node": "~16.11.12", + "@anticrm/platform-rig": "~0.6.0" + }, + "dependencies": { + "@anticrm/platform": "~0.6.6", + "@anticrm/automation": "~0.6.0" + } +} diff --git a/plugins/automation-assets/src/index.ts b/plugins/automation-assets/src/index.ts new file mode 100644 index 0000000000..b4ef834377 --- /dev/null +++ b/plugins/automation-assets/src/index.ts @@ -0,0 +1,24 @@ +// +// Copyright © 2022 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 automation, { automationId } from '@anticrm/automation' + +const icons = require('../assets/icons.svg') as string // eslint-disable-line +loadMetadata(automation.icon, { + Automation: `${icons}#automation` +}) + +addStringsLoader(automationId, async (lang: string) => await import(`../lang/${lang}.json`)) diff --git a/plugins/automation-assets/tsconfig.json b/plugins/automation-assets/tsconfig.json new file mode 100644 index 0000000000..0980a1b8b4 --- /dev/null +++ b/plugins/automation-assets/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "target": "esnext", + "module": "esnext", + "declaration": true, + "outDir": "./lib", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "lib": [ + "esnext", + "dom" + ] + } +} \ No newline at end of file diff --git a/plugins/automation-resources/.eslintrc.js b/plugins/automation-resources/.eslintrc.js new file mode 100644 index 0000000000..a327214967 --- /dev/null +++ b/plugins/automation-resources/.eslintrc.js @@ -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 + } +} diff --git a/plugins/automation-resources/config/rig.json b/plugins/automation-resources/config/rig.json new file mode 100644 index 0000000000..e12b462b19 --- /dev/null +++ b/plugins/automation-resources/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@anticrm/platform-rig", + "rigProfile": "ui" +} diff --git a/plugins/automation-resources/package.json b/plugins/automation-resources/package.json new file mode 100644 index 0000000000..91a8c49229 --- /dev/null +++ b/plugins/automation-resources/package.json @@ -0,0 +1,42 @@ +{ + "name": "@anticrm/automation-resources", + "version": "0.6.0", + "main": "src/index.ts", + "author": "Anticrm Platform Contributors", + "license": "EPL-2.0", + "scripts": { + "build": "tsc --noEmit", + "build:docs": "api-extractor run --local", + "lint": "svelte-check && eslint", + "lint:fix": "eslint --fix src", + "format": "prettier --write --plugin-search-dir=. src && eslint --fix src" + }, + "devDependencies": { + "@anticrm/platform-rig": "~0.6.0", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.4.0", + "eslint": "^7.32.0", + "eslint-config-standard-with-typescript": "^21.0.1", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.1", + "eslint-plugin-svelte3": "^4.0.0", + "prettier": "^2.4.1", + "prettier-plugin-svelte": "^2.7.0", + "sass": "^1.37.5", + "svelte-check": "^2.7.0", + "svelte-loader": "^3.1.2", + "svelte-preprocess": "^4.10.5", + "typescript": "^4.3.5" + }, + "dependencies": { + "@anticrm/automation": "~0.6.0", + "@anticrm/core": "~0.6.16", + "@anticrm/platform": "~0.6.6", + "@anticrm/presentation": "~0.6.2", + "svelte": "^3.47", + "@anticrm/ui": "~0.6.0", + "@anticrm/view": "~0.6.0", + "@anticrm/view-resources": "~0.6.0" + } +} diff --git a/plugins/automation-resources/postcss.config.js b/plugins/automation-resources/postcss.config.js new file mode 100644 index 0000000000..88752c6cb0 --- /dev/null +++ b/plugins/automation-resources/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: [ + require('autoprefixer') + ] +} diff --git a/plugins/automation-resources/src/components/AddActionTrigger.svelte b/plugins/automation-resources/src/components/AddActionTrigger.svelte new file mode 100644 index 0000000000..1a9a9f4eac --- /dev/null +++ b/plugins/automation-resources/src/components/AddActionTrigger.svelte @@ -0,0 +1,101 @@ + + +
+
diff --git a/plugins/automation-resources/src/components/AddAutomation.svelte b/plugins/automation-resources/src/components/AddAutomation.svelte new file mode 100644 index 0000000000..45cd5b23ee --- /dev/null +++ b/plugins/automation-resources/src/components/AddAutomation.svelte @@ -0,0 +1,61 @@ + + +
+
+
+
+
+
+ { + trigger = e.detail + }} + on:targetClass={(e) => { + targetClass = e.detail + }} + /> +
+ {#if trigger} +
+ +
+ {/if} + +
diff --git a/plugins/automation-resources/src/components/AutomationActions.svelte b/plugins/automation-resources/src/components/AutomationActions.svelte new file mode 100644 index 0000000000..a629515c53 --- /dev/null +++ b/plugins/automation-resources/src/components/AutomationActions.svelte @@ -0,0 +1,130 @@ + + +
+
+
+ {#each commands as command} +
+ +
+ {/each} +
+ {#if contentAttributes.length > 0} +
+
+ {#if currentTab === ActionTab.Content && targetClass} + {#each contentAttributes as attr} + + {/each} + + {/if} +
+
diff --git a/plugins/automation-resources/src/components/AutomationSettingsElement.svelte b/plugins/automation-resources/src/components/AutomationSettingsElement.svelte new file mode 100644 index 0000000000..f53a246604 --- /dev/null +++ b/plugins/automation-resources/src/components/AutomationSettingsElement.svelte @@ -0,0 +1,36 @@ + + +
+
+
+
+ +
+
+ {#if isAdding} +
+ { + isAdding = false + }} + /> +
+ {:else} + + {/if} +
diff --git a/plugins/automation-resources/src/components/AutomationTrigger.svelte b/plugins/automation-resources/src/components/AutomationTrigger.svelte new file mode 100644 index 0000000000..b583781ec8 --- /dev/null +++ b/plugins/automation-resources/src/components/AutomationTrigger.svelte @@ -0,0 +1,30 @@ + + +
+
+
+ {#if trigger} +
+ +
+ {:else} + + {/if} +
diff --git a/plugins/automation-resources/src/components/Automations.svelte b/plugins/automation-resources/src/components/Automations.svelte new file mode 100644 index 0000000000..4a4ff0f355 --- /dev/null +++ b/plugins/automation-resources/src/components/Automations.svelte @@ -0,0 +1,6 @@ + + + diff --git a/plugins/automation-resources/src/components/actions/ContentActionCreate.svelte b/plugins/automation-resources/src/components/actions/ContentActionCreate.svelte new file mode 100644 index 0000000000..2ea225b579 --- /dev/null +++ b/plugins/automation-resources/src/components/actions/ContentActionCreate.svelte @@ -0,0 +1,57 @@ + + +{#if attribute && automationSupport} +
+
+
+
+{/if} diff --git a/plugins/automation-resources/src/components/presenters/CommandPresenter.svelte b/plugins/automation-resources/src/components/presenters/CommandPresenter.svelte new file mode 100644 index 0000000000..39ee031c8e --- /dev/null +++ b/plugins/automation-resources/src/components/presenters/CommandPresenter.svelte @@ -0,0 +1,43 @@ + + +{#if isUpdateDocCommand(value)} + {@const updateCommand = toUpdateDocCommand(value)} + {@const targetClass = hierarchy.getClass(updateCommand.targetClass)} + {#each Object.keys(updateCommand.update) as attr} + {@const attribute = hierarchy.getAttribute(updateCommand.targetClass, attr)} +
+
+ {/each} +{/if} diff --git a/plugins/automation-resources/src/components/presenters/TriggerPresenter.svelte b/plugins/automation-resources/src/components/presenters/TriggerPresenter.svelte new file mode 100644 index 0000000000..96cc939957 --- /dev/null +++ b/plugins/automation-resources/src/components/presenters/TriggerPresenter.svelte @@ -0,0 +1,29 @@ + + +{#if value.action} + {@const targetClass = hierarchy.getClass(value.action.target)} +
+ + + {#if value.action.icon} + + {/if} + {value.action.label} +
+{/if} diff --git a/plugins/automation-resources/src/components/selectors/ClassSelector.svelte b/plugins/automation-resources/src/components/selectors/ClassSelector.svelte new file mode 100644 index 0000000000..140391c77d --- /dev/null +++ b/plugins/automation-resources/src/components/selectors/ClassSelector.svelte @@ -0,0 +1,22 @@ + + +{#await getClassItems() then classItems} + +{/await} diff --git a/plugins/automation-resources/src/components/selectors/IconChooser.svelte b/plugins/automation-resources/src/components/selectors/IconChooser.svelte new file mode 100644 index 0000000000..f94d6c2368 --- /dev/null +++ b/plugins/automation-resources/src/components/selectors/IconChooser.svelte @@ -0,0 +1,57 @@ + + + { + dispatch('close') + }} +> +
+ {#each icons as obj} +
+
+ {/each} +
+
diff --git a/plugins/automation-resources/src/index.ts b/plugins/automation-resources/src/index.ts new file mode 100644 index 0000000000..f649aecaae --- /dev/null +++ b/plugins/automation-resources/src/index.ts @@ -0,0 +1,27 @@ +// +// Copyright © 2020, 2021 Anticrm Platform Contributors. +// Copyright © 2021 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import { Resources } from '@anticrm/platform' +import AutomationSettingsElement from './components/AutomationSettingsElement.svelte' +import { performAutomation } from './utils' + +export default async (): Promise => ({ + component: { + AutomationSettingsElement + }, + action: { + PerformAutomation: performAutomation + } +}) diff --git a/plugins/automation-resources/src/models.ts b/plugins/automation-resources/src/models.ts new file mode 100644 index 0000000000..0775be95ca --- /dev/null +++ b/plugins/automation-resources/src/models.ts @@ -0,0 +1,21 @@ +import { Class, Doc, Ref } from '@anticrm/core' +import { Asset } from '@anticrm/platform' + +export enum ActionTab { + Add = 'Add', + Chat = 'Chat', + Content = 'Content', + Dates = 'Dates', + Move = 'Move', + Sort = 'Sort', + Tracker = 'Tracker' +} + +export interface Trigger { + action?: { + context: 'context' | 'editor' + target: Ref> + label: string + icon?: Asset + } +} diff --git a/plugins/automation-resources/src/plugin.ts b/plugins/automation-resources/src/plugin.ts new file mode 100644 index 0000000000..e7a65a0d79 --- /dev/null +++ b/plugins/automation-resources/src/plugin.ts @@ -0,0 +1,28 @@ +// +// 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 automation, { automationId } from '@anticrm/automation' +import { mergeIds } from '@anticrm/platform' +import type { AnyComponent } from '@anticrm/ui' + +const automations = mergeIds(automationId, automation, { + component: { + AutomationSettingsElement: '' as AnyComponent + } +}) + +export type PluginType = typeof automations + +export default automations diff --git a/plugins/automation-resources/src/utils.ts b/plugins/automation-resources/src/utils.ts new file mode 100644 index 0000000000..2697a49903 --- /dev/null +++ b/plugins/automation-resources/src/utils.ts @@ -0,0 +1,120 @@ +import automation, { + Automation, + Command, + isUpdateDocCommand, + PerformAutomationProps, + TriggerType +} from '@anticrm/automation' +import core, { Class, Doc, Ref, Space, TxOperations } from '@anticrm/core' +import { IntlString } from '@anticrm/platform' +import { getClient } from '@anticrm/presentation' +import view, { Action } from '@anticrm/view' +import { Trigger } from './models' + +export async function createAutomation ( + client: TxOperations, + name: string, + trigger: Trigger, + commands: Array>, + props: { targetClass?: Ref>, attachedTo?: Doc, description?: string } = {} +): Promise>> { + let space: Ref = automation.space.Automation + let attachedTo: Ref = automation.space.Automation + let attachedToClass: Ref> = core.class.Space + const collection = 'automations' + if (props.attachedTo !== undefined) { + space = props.attachedTo.space + attachedTo = props.attachedTo._id + attachedToClass = props.attachedTo._class + } + const automationId = await client.addCollection( + automation.class.Automation, + space, + attachedTo, + attachedToClass, + collection, + { + name, + description: null, + targetClass: props.targetClass ?? null, + trigger: { + type: getTriggerType(trigger) + }, + commands + } + ) + await createTrigger(client, trigger, space, automationId) + return automationId +} + +export function getTriggerType (trigger: Trigger): TriggerType { + if (trigger.action !== undefined) { + return TriggerType.Action + } else { + throw new Error('Unknown automation trigger') + } +} + +export async function createTrigger ( + client: TxOperations, + trigger: Trigger, + space: Ref, + automationId: Ref> +): Promise { + if (trigger.action !== undefined) { + const triggerId = await client.createDoc>(view.class.Action, core.space.Model, { + action: automation.action.PerformAutomation, + actionProps: { + automationId, + automationClass: automation.class.Automation + }, + category: automation.category.Automation, + context: { mode: trigger.action.context }, + icon: trigger.action.icon, + input: 'any', + label: trigger.action.label as IntlString, + target: trigger.action.target + }) + return await client.findOne>(view.class.Action, { _id: triggerId }) + } else { + throw new Error('Unknown automation trigger') + } +} + +export async function performAutomation ( + doc: Doc | Doc[] | undefined, + evt: Event, + props: PerformAutomationProps | undefined +): Promise { + if (doc === undefined || props?.automationId === undefined) { + throw new Error('Unknown automation action') + } + const client = getClient() + const automationObj = await client.findOne(props.automationClass ?? automation.class.Automation, { + _id: props.automationId + }) + if (automationObj === undefined) { + return + } + + let objects = [] + if (Array.isArray(doc)) { + objects = doc + } else { + objects = [doc] + } + + await doPerformAutomation(client, objects, automationObj) +} + +async function doPerformAutomation (client: TxOperations, docs: Doc[], automationObj: Automation): Promise { + // TODO: move to automation server + const hierarchy = client.getHierarchy() + for (const doc of docs) { + for (const command of automationObj.commands) { + if (isUpdateDocCommand(command) && hierarchy.isDerived(doc._class, command.targetClass)) { + await client.update(doc, command.update) + } + } + } +} diff --git a/plugins/automation-resources/svelte.config.js b/plugins/automation-resources/svelte.config.js new file mode 100644 index 0000000000..944a06f73e --- /dev/null +++ b/plugins/automation-resources/svelte.config.js @@ -0,0 +1,5 @@ +const sveltePreprocess = require('svelte-preprocess') + +module.exports = { + preprocess: sveltePreprocess() +}; \ No newline at end of file diff --git a/plugins/automation-resources/tsconfig.json b/plugins/automation-resources/tsconfig.json new file mode 100644 index 0000000000..0980a1b8b4 --- /dev/null +++ b/plugins/automation-resources/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "target": "esnext", + "module": "esnext", + "declaration": true, + "outDir": "./lib", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "lib": [ + "esnext", + "dom" + ] + } +} \ No newline at end of file diff --git a/plugins/automation/.eslintrc.js b/plugins/automation/.eslintrc.js new file mode 100644 index 0000000000..5da5872d4a --- /dev/null +++ b/plugins/automation/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/plugins/automation/.npmignore b/plugins/automation/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/plugins/automation/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/plugins/automation/config/rig.json b/plugins/automation/config/rig.json new file mode 100644 index 0000000000..af1257a896 --- /dev/null +++ b/plugins/automation/config/rig.json @@ -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" +} diff --git a/plugins/automation/package.json b/plugins/automation/package.json new file mode 100644 index 0000000000..851687b689 --- /dev/null +++ b/plugins/automation/package.json @@ -0,0 +1,35 @@ +{ + "name": "@anticrm/automation", + "version": "0.6.0", + "main": "lib/index.js", + "author": "Anticrm Platform Contributors", + "license": "EPL-2.0", + "scripts": { + "build": "heft build", + "build:watch": "tsc", + "test": "heft test", + "lint:fix": "eslint --fix src", + "lint": "eslint src", + "format": "prettier --write src && eslint --fix src" + }, + "devDependencies": { + "@anticrm/platform-rig": "~0.6.0", + "@types/heft-jest": "^1.0.2", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-promise": "^5.1.1", + "eslint-plugin-node": "^11.1.0", + "eslint": "^7.32.0", + "simplytyped": "^3.3.0", + "@rushstack/heft": "^0.45.5", + "@typescript-eslint/parser": "^5.4.0", + "eslint-config-standard-with-typescript": "^21.0.1", + "prettier": "^2.4.1", + "typescript": "^4.3.5" + }, + "dependencies": { + "@anticrm/platform": "~0.6.6", + "@anticrm/core": "~0.6.16", + "@anticrm/view": "~0.6.0" + } +} diff --git a/plugins/automation/src/index.ts b/plugins/automation/src/index.ts new file mode 100644 index 0000000000..864112de13 --- /dev/null +++ b/plugins/automation/src/index.ts @@ -0,0 +1,148 @@ +import { AttachedDoc, Class, Doc, DocumentQuery, DocumentUpdate, Mixin, Ref, Space } from '@anticrm/core' +import { Asset, IntlString, Plugin, plugin } from '@anticrm/platform' +import { ActionCategory, ViewAction } from '@anticrm/view' + +/** + * @public + */ +export const automationId = 'automation' as Plugin + +/** + * @public + */ +export enum CommandType { + UpdateDoc = 'UPDATE_DOC' +} + +/** + * @public + */ +export enum TriggerType { + Action = 'ACTION', + Trigger = 'TRIGGER' +} + +/** + * @public + */ +export interface AttributeAutomationSupport { + name: keyof T + sort?: { + groupBy?: DocumentQuery + } +} + +/** + * @public + */ +export interface AttributeAutomationTriggerSupport { + name: keyof T +} + +/** + * @public + */ +export interface AutomationTriggerSupport { + action?: { + mode: ('editor' | 'context')[] + } + attributes?: AttributeAutomationTriggerSupport[] +} +/** + * @public + */ +export interface AutomationSortSupport { + groupBy?: DocumentQuery +} +/** + * @public + */ +export interface AutomationSupport extends Class { + attributes: AttributeAutomationSupport[] + trigger: AutomationTriggerSupport + sort?: AutomationSortSupport +} + +/** + * @public + */ +export interface Command { + fetch?: DocumentQuery + type: CommandType +} + +/** + * @public + */ +export interface UpdateDocCommand extends Command { + type: CommandType.UpdateDoc + targetClass: Ref> + update: DocumentUpdate +} + +/** + * @public + */ +export function isUpdateDocCommand (command: Command): command is UpdateDocCommand { + return command.type === CommandType.UpdateDoc +} + +/** + * @public + */ +export interface Automation extends AttachedDoc { + name: string + description: string | null + targetClass: Ref> | null + trigger: { + type: TriggerType + } + commands: Command[] +} + +/** + * @public + */ +export interface PerformAutomationProps { + automationId: Ref> + automationClass: Ref>> +} + +export default plugin(automationId, { + class: { + Automation: '' as Ref>> + }, + action: { + PerformAutomation: '' as ViewAction + }, + mixin: { + AutomationSupport: '' as Ref>> + }, + category: { + Automation: '' as Ref + }, + string: { + Automation: '' as IntlString, + Actions: '' as IntlString, + Chat: '' as IntlString, + Content: '' as IntlString, + Dates: '' as IntlString, + Tracker: '' as IntlString, + Trigger: '' as IntlString, + Set: '' as IntlString, + To: '' as IntlString, + AddMenu: '' as IntlString, + Menu: '' as IntlString, + Mode: '' as IntlString, + Icon: '' as IntlString, + SelectClass: '' as IntlString, + In: '' as IntlString, + Update: '' as IntlString + }, + icon: { + Automation: '' as Asset + }, + space: { + Automation: '' as Ref + } +}) diff --git a/plugins/automation/tsconfig.json b/plugins/automation/tsconfig.json new file mode 100644 index 0000000000..32045300ce --- /dev/null +++ b/plugins/automation/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "lib": ["esnext", "dom"] + } +} \ No newline at end of file diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts index e9b1fe6855..be1f56bda6 100644 --- a/plugins/view-resources/src/index.ts +++ b/plugins/view-resources/src/index.ts @@ -99,7 +99,12 @@ export { UpDownNavigator, ViewletSettingButton, FilterBar, - ClassAttributeBar + ClassAttributeBar, + ClassPresenter, + BooleanEditor, + BooleanPresenter, + NumberEditor, + NumberPresenter } export default async (): Promise => ({ diff --git a/plugins/view-resources/src/plugin.ts b/plugins/view-resources/src/plugin.ts index fb12f8a4b6..2713cbbc82 100644 --- a/plugins/view-resources/src/plugin.ts +++ b/plugins/view-resources/src/plugin.ts @@ -33,7 +33,6 @@ export default mergeIds(viewId, view, { Cancel: '' as IntlString, LabelYes: '' as IntlString, LabelNo: '' as IntlString, - LabelNA: '' as IntlString, ChooseAColor: '' as IntlString, DeleteObject: '' as IntlString, DeleteObjectConfirm: '' as IntlString, diff --git a/plugins/view/src/index.ts b/plugins/view/src/index.ts index 339be5484f..28700cba13 100644 --- a/plugins/view/src/index.ts +++ b/plugins/view/src/index.ts @@ -400,6 +400,26 @@ const view = plugin(viewId, { LinkPresenter: '' as Ref>, FilterMode: '' as Ref> }, + action: { + Delete: '' as Ref, + Move: '' as Ref, + MoveLeft: '' as Ref, + MoveRight: '' as Ref, + MoveUp: '' as Ref, + MoveDown: '' as Ref, + + SelectItem: '' as Ref, + SelectItemAll: '' as Ref, + SelectItemNone: '' as Ref, + SelectUp: '' as Ref, + SelectDown: '' as Ref, + + ShowPreview: '' as Ref, + ShowActions: '' as Ref, + + // Edit document + Open: '' as Ref + }, viewlet: { Table: '' as Ref }, @@ -410,7 +430,8 @@ const view = plugin(viewId, { BooleanTruePresenter: '' as AnyComponent }, string: { - CustomizeView: '' as IntlString + CustomizeView: '' as IntlString, + LabelNA: '' as IntlString }, icon: { Table: '' as Asset, diff --git a/rush.json b/rush.json index 98408a434d..7aeb8f6728 100644 --- a/rush.json +++ b/rush.json @@ -1328,6 +1328,26 @@ "projectFolder": "pods/backup", "shouldPublish": false }, + { + "packageName": "@anticrm/automation", + "projectFolder": "plugins/automation", + "shouldPublish": true + }, + { + "packageName": "@anticrm/automation-resources", + "projectFolder": "plugins/automation-resources", + "shouldPublish": true + }, + { + "packageName": "@anticrm/automation-assets", + "projectFolder": "plugins/automation-assets", + "shouldPublish": true + }, + { + "packageName": "@anticrm/model-automation", + "projectFolder": "models/automation", + "shouldPublish": true + }, { "packageName": "@anticrm/hr", "projectFolder": "plugins/hr",