mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
Automation: Initial support (#2134)
This commit is contained in:
parent
14de8334ba
commit
3c037e4506
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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')
|
||||
|
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
7
models/automation/.eslintrc.js
Normal file
7
models/automation/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/model-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
18
models/automation/config/rig.json
Normal file
18
models/automation/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/model-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
38
models/automation/package.json
Normal file
38
models/automation/package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
|
93
models/automation/src/index.ts
Normal file
93
models/automation/src/index.ts
Normal file
@ -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<Doc> {
|
||||
@Prop(TypeString(), core.string.Name)
|
||||
name!: string
|
||||
|
||||
@Prop(TypeString(), core.string.Description)
|
||||
description!: string | null
|
||||
|
||||
targetClass!: Ref<Class<Doc>> | null
|
||||
declare trigger: {
|
||||
type: TriggerType
|
||||
}
|
||||
|
||||
declare commands: Command<Doc>[]
|
||||
}
|
||||
|
||||
@Mixin(automation.mixin.AutomationSupport, core.class.Class)
|
||||
export class TAutomationSupport extends TClass implements AutomationSupport<Doc> {
|
||||
declare attributes: AttributeAutomationSupport<Doc>[]
|
||||
declare trigger: AutomationTriggerSupport<Doc>
|
||||
sort?: AutomationSortSupport<Doc>
|
||||
}
|
||||
|
||||
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'
|
21
models/automation/src/migration.ts
Normal file
21
models/automation/src/migration.ts
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||
|
||||
export const automationOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {}
|
||||
}
|
29
models/automation/src/plugin.ts
Normal file
29
models/automation/src/plugin.ts
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { 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<SettingsCategory>
|
||||
}
|
||||
}
|
||||
const automations: PluginType & typeof pluginData = mergeIds(automationId, automation, pluginData)
|
||||
|
||||
export default automations
|
8
models/automation/tsconfig.json
Normal file
8
models/automation/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/model-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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<Class<Card>, AutomationSupport<Card>>(
|
||||
board.class.Card,
|
||||
core.class.Class,
|
||||
automation.mixin.AutomationSupport,
|
||||
{
|
||||
attributes: [
|
||||
{
|
||||
name: 'isArchived'
|
||||
},
|
||||
{
|
||||
name: 'title'
|
||||
},
|
||||
{
|
||||
name: 'description'
|
||||
}
|
||||
],
|
||||
trigger: {
|
||||
action: {
|
||||
mode: ['context', 'editor']
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
builder.mixin<Class<Board>, AutomationSupport<Board>>(
|
||||
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(
|
||||
|
@ -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,
|
||||
|
@ -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<Action>,
|
||||
Move: '' as Ref<Action>,
|
||||
MoveLeft: '' as Ref<Action>,
|
||||
MoveRight: '' as Ref<Action>,
|
||||
MoveUp: '' as Ref<Action>,
|
||||
MoveDown: '' as Ref<Action>,
|
||||
|
||||
SelectItem: '' as Ref<Action>,
|
||||
SelectItemAll: '' as Ref<Action>,
|
||||
SelectItemNone: '' as Ref<Action>,
|
||||
SelectUp: '' as Ref<Action>,
|
||||
SelectDown: '' as Ref<Action>,
|
||||
|
||||
ShowPreview: '' as Ref<Action>,
|
||||
ShowActions: '' as Ref<Action>,
|
||||
|
||||
// Edit document
|
||||
Open: '' as Ref<Action>
|
||||
},
|
||||
actionImpl: {
|
||||
Delete: '' as ViewAction,
|
||||
Move: '' as ViewAction,
|
||||
|
@ -133,6 +133,7 @@ export default plugin(coreId, {
|
||||
Array: '' as IntlString,
|
||||
Bag: '' as IntlString,
|
||||
Name: '' as IntlString,
|
||||
Description: '' as IntlString,
|
||||
Enum: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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<AnyComponent>
|
||||
}
|
||||
})
|
||||
|
||||
export default uis
|
||||
|
7
plugins/automation-assets/.eslintrc.js
Normal file
7
plugins/automation-assets/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/assets/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
6
plugins/automation-assets/assets/icons.svg
Normal file
6
plugins/automation-assets/assets/icons.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<!-- !!! Dublicate chunter-assets (cunter, hashtag, lock) !!! -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="automation" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.6541 10.7952L14.7544 11.6213C14.8576 11.6618 14.9394 11.7434 14.9801 11.8466C15.0511 12.0264 14.9828 12.2268 14.827 12.3284L14.755 12.3656L8.35645 14.8924C8.15935 14.9703 7.94372 14.9831 7.74052 14.9309L7.62035 14.8918L1.25259 12.3653C1.1499 12.3246 1.06864 12.2432 1.02806 12.1404C0.957068 11.9607 1.02536 11.7603 1.1812 11.6587L1.25319 11.6215L3.34307 10.7962L7.06917 12.2751C7.65895 12.5091 8.31525 12.5097 8.9054 12.2766L12.6541 10.7952ZM12.6541 6.77688L14.7544 7.60289C14.8576 7.64346 14.9394 7.72508 14.9801 7.82824C15.0511 8.00803 14.9828 8.20839 14.827 8.31004L14.755 8.3472L10.6001 9.98825L9.619 10.375L8.35645 10.8741L8.317 10.886L8.23566 10.9132C8.20301 10.9215 8.17004 10.9282 8.13688 10.9331C8.12585 10.9346 8.11547 10.936 8.10507 10.9372C8.02541 10.9468 7.94422 10.9464 7.86397 10.9363L7.74052 10.9126L7.62035 10.8735L6.391 10.385L5.38907 9.98825L1.25259 8.34697C1.1499 8.30623 1.06864 8.22483 1.02806 8.12208C0.957068 7.94229 1.02536 7.74192 1.1812 7.64029L1.25319 7.60312L3.34307 6.77788L7.06917 8.25677C7.65895 8.49078 8.31525 8.4913 8.9054 8.25824L12.6541 6.77688ZM7.62186 1.06989C7.85734 0.976906 8.11932 0.976697 8.35494 1.06931L14.7544 3.58452C14.8576 3.62509 14.9394 3.70671 14.9801 3.80987C15.0612 4.01534 14.9605 4.24769 14.755 4.32884L10.6001 5.96988L8.35565 6.856L8.27468 6.88396C8.25405 6.8901 8.23326 6.89557 8.21236 6.90036C8.09824 6.92674 7.98013 6.93258 7.86397 6.91788L7.74052 6.89419L7.62035 6.8551L1.25259 4.3286C1.1499 4.28786 1.06864 4.20646 1.02806 4.10371C0.946925 3.89823 1.04772 3.66589 1.25319 3.58475L7.62186 1.06989Z"></path>
|
||||
</symbol>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
5
plugins/automation-assets/config/rig.json
Normal file
5
plugins/automation-assets/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@anticrm/platform-rig",
|
||||
"rigProfile": "assets"
|
||||
}
|
20
plugins/automation-assets/lang/en.json
Normal file
20
plugins/automation-assets/lang/en.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
20
plugins/automation-assets/lang/ru.json
Normal file
20
plugins/automation-assets/lang/ru.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"string": {
|
||||
"Automation": "Автоматизация",
|
||||
"Actions": "Действия",
|
||||
"Chat": "Чат",
|
||||
"Content": "Контент",
|
||||
"Dates": "Даты",
|
||||
"Tracker": "Трекер",
|
||||
"Trigger": "Триггер",
|
||||
"Set": "Присвоить",
|
||||
"To": "значение",
|
||||
"Mode": "Режим",
|
||||
"AddMenu": "Добавить меню",
|
||||
"Menu": "Menu",
|
||||
"Icon": "Изображение",
|
||||
"SelectClass": "Выберите класс",
|
||||
"In": "в",
|
||||
"Update": "Обновить"
|
||||
}
|
||||
}
|
34
plugins/automation-assets/package.json
Normal file
34
plugins/automation-assets/package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
24
plugins/automation-assets/src/index.ts
Normal file
24
plugins/automation-assets/src/index.ts
Normal file
@ -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`))
|
16
plugins/automation-assets/tsconfig.json
Normal file
16
plugins/automation-assets/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
7
plugins/automation-resources/.eslintrc.js
Normal file
7
plugins/automation-resources/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/ui/config/eslint.config.json'],
|
||||
parserOptions: { tsconfigRootDir: __dirname },
|
||||
settings: {
|
||||
'svelte3/ignore-styles': () => true
|
||||
}
|
||||
}
|
5
plugins/automation-resources/config/rig.json
Normal file
5
plugins/automation-resources/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@anticrm/platform-rig",
|
||||
"rigProfile": "ui"
|
||||
}
|
42
plugins/automation-resources/package.json
Normal file
42
plugins/automation-resources/package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
5
plugins/automation-resources/postcss.config.js
Normal file
5
plugins/automation-resources/postcss.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')
|
||||
]
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
<script lang="ts">
|
||||
import { AutomationSupport } from '@anticrm/automation'
|
||||
import core, { Class, Doc, Ref } from '@anticrm/core'
|
||||
import { Asset } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, Dropdown, EditBox, eventToHTMLElement, IconAdd, Label, ListItem, showPopup } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Trigger } from '../models'
|
||||
import automation from '../plugin'
|
||||
import ClassSelector from './selectors/ClassSelector.svelte'
|
||||
import IconChooser from './selectors/IconChooser.svelte'
|
||||
|
||||
export let trigger: Trigger | undefined = undefined
|
||||
let targetClass: Ref<Class<Doc>>
|
||||
let automationSupport: AutomationSupport<Doc>
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let selectedActionMode: ListItem | undefined = undefined
|
||||
let name: string | undefined = undefined
|
||||
let icon: Asset | undefined = undefined
|
||||
|
||||
function addActionTrigger () {
|
||||
if (!name || !selectedActionMode) {
|
||||
return
|
||||
}
|
||||
trigger = {
|
||||
action: {
|
||||
context: selectedActionMode?._id as 'editor' | 'context',
|
||||
target: targetClass,
|
||||
label: name!,
|
||||
icon
|
||||
}
|
||||
}
|
||||
dispatch('targetClass', targetClass)
|
||||
dispatch('trigger', trigger)
|
||||
}
|
||||
|
||||
function chooseIcon (ev: MouseEvent) {
|
||||
showPopup(IconChooser, { icon }, eventToHTMLElement(ev), (result) => {
|
||||
if (result !== undefined && result !== null) {
|
||||
icon = result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const classes = hierarchy
|
||||
.getDescendants(core.class.Doc)
|
||||
.map((p) => hierarchy.getClass(p))
|
||||
.filter((p) => {
|
||||
if (!hierarchy.hasMixin(p, automation.mixin.AutomationSupport)) {
|
||||
return false
|
||||
}
|
||||
const support = hierarchy.as(p, automation.mixin.AutomationSupport)
|
||||
return !!support.trigger.action
|
||||
})
|
||||
$: if (targetClass) {
|
||||
automationSupport = hierarchy.as(hierarchy.getClass(targetClass), automation.mixin.AutomationSupport)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-col">
|
||||
<Label label={automation.string.AddMenu} />
|
||||
<div class="flex flex-gap-2 mt-2 mb-2 items-center">
|
||||
<Label label={automation.string.SelectClass} />
|
||||
<ClassSelector
|
||||
{classes}
|
||||
on:selected={(e) => {
|
||||
targetClass = e.detail
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if automationSupport?.trigger?.action}
|
||||
<div class="flex-between">
|
||||
<div class="flex flex-gap-3 mb-2">
|
||||
<div class="flex-col items-baseline justify-center flex-gap-2">
|
||||
<Label label={automation.string.Icon} />
|
||||
<Button icon={icon ?? IconAdd} kind="no-border" size="medium" on:click={chooseIcon} />
|
||||
</div>
|
||||
<div class="flex-col items-baseline justify-center flex-gap-2">
|
||||
<Label label={core.string.Name} />
|
||||
<div class="h-7">
|
||||
<EditBox bind:value={name} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-col items-baseline justify-center flex-gap-2">
|
||||
<Label label={automation.string.Mode} />
|
||||
<Dropdown
|
||||
items={automationSupport.trigger.action.mode.map((m) => ({ _id: m, isSelectable: true, label: m }))}
|
||||
bind:selected={selectedActionMode}
|
||||
placeholder={view.string.LabelNA}
|
||||
size="medium"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button icon={IconAdd} kind="transparent" disabled={!name || !selectedActionMode} on:click={addActionTrigger} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
import { Command } from '@anticrm/automation'
|
||||
import core, { Class, Doc, Ref } from '@anticrm/core'
|
||||
import presentation, { getClient } from '@anticrm/presentation'
|
||||
import { Button, EditBox, Label } from '@anticrm/ui'
|
||||
|
||||
import { Trigger } from '../models'
|
||||
|
||||
import { createAutomation } from '../utils'
|
||||
import AutomationActions from './AutomationActions.svelte'
|
||||
import AutomationTrigger from './AutomationTrigger.svelte'
|
||||
|
||||
export let trigger: Trigger | undefined = undefined
|
||||
export let commands: Command<Doc>[] = []
|
||||
const client = getClient()
|
||||
|
||||
let targetClass: Ref<Class<Doc>> | undefined = undefined
|
||||
let name: string | undefined = undefined
|
||||
let description: string | undefined = undefined
|
||||
|
||||
function save () {
|
||||
if (!name || !trigger || !commands.length) {
|
||||
return
|
||||
}
|
||||
createAutomation(client, name, trigger, commands, { description, targetClass })
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-col p-4 w-full">
|
||||
<div class="flex flex-gap-2 mb-2">
|
||||
<Label label={core.string.Name} />
|
||||
<EditBox bind:value={name} />
|
||||
</div>
|
||||
<div class="flex flex-gap-2 mb-4">
|
||||
<Label label={core.string.Description} />
|
||||
<EditBox bind:value={description} />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<AutomationTrigger
|
||||
{trigger}
|
||||
on:trigger={(e) => {
|
||||
trigger = e.detail
|
||||
}}
|
||||
on:targetClass={(e) => {
|
||||
targetClass = e.detail
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if trigger}
|
||||
<div class="mb-2">
|
||||
<AutomationActions {targetClass} bind:commands />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
label={presentation.string.Save}
|
||||
disabled={!name || !trigger || !commands.length}
|
||||
kind="primary"
|
||||
on:click={save}
|
||||
/>
|
||||
</div>
|
@ -0,0 +1,130 @@
|
||||
<script lang="ts">
|
||||
import { Command } from '@anticrm/automation'
|
||||
import core, { AnyAttribute, Class, Doc, Ref } from '@anticrm/core'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, IconDelete, Label } from '@anticrm/ui'
|
||||
import { ActionTab } from '../models'
|
||||
import automation from '../plugin'
|
||||
|
||||
import ContentActionCreate from './actions/ContentActionCreate.svelte'
|
||||
import CommandPresenter from './presenters/CommandPresenter.svelte'
|
||||
|
||||
export let targetClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let commands: Command<Doc>[] = []
|
||||
let attributes: Map<string, AnyAttribute> = new Map()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const attributeSupportMap = new Map()
|
||||
|
||||
const contentAttributes: string[] = []
|
||||
const dateAttributes: string[] = []
|
||||
const collectionAttributes: string[] = []
|
||||
const arrayAttributes: string[] = []
|
||||
const refAttributes: string[] = []
|
||||
let currentTab: ActionTab | undefined = undefined
|
||||
|
||||
function addCommand (e: CustomEvent<Command<Doc>>) {
|
||||
commands.push(e.detail)
|
||||
commands = commands
|
||||
}
|
||||
|
||||
function removeCommand (command: Command<Doc>) {
|
||||
commands = commands.filter((c) => c !== command)
|
||||
}
|
||||
|
||||
if (targetClass) {
|
||||
const classObj = hierarchy.getClass(targetClass)
|
||||
const automationSupport = hierarchy.as(classObj, automation.mixin.AutomationSupport)
|
||||
automationSupport?.attributes?.forEach((attr) => {
|
||||
attributeSupportMap.set(attr.name, attr)
|
||||
attributes = hierarchy.getAllAttributes(targetClass!)
|
||||
const classifier = attributes.get(attr.name)
|
||||
if (classifier) {
|
||||
const typeClass = classifier.type._class
|
||||
if (
|
||||
typeClass === core.class.TypeBoolean ||
|
||||
typeClass === core.class.TypeNumber ||
|
||||
typeClass === core.class.TypeString ||
|
||||
typeClass === core.class.TypeMarkup
|
||||
) {
|
||||
contentAttributes.push(attr.name)
|
||||
} else if (typeClass === core.class.TypeTimestamp || typeClass === core.class.TypeDate) {
|
||||
dateAttributes.push(attr.name)
|
||||
} else if (typeClass === core.class.Collection) {
|
||||
collectionAttributes.push(attr.name) // TODO
|
||||
} else if (typeClass === core.class.ArrOf) {
|
||||
arrayAttributes.push(attr.name) // TODO
|
||||
} else if (typeClass === core.class.RefTo) {
|
||||
refAttributes.push(attr.name) // TODO
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-col">
|
||||
<div class="fs-title mb-4">
|
||||
<Label label={automation.string.Actions} />
|
||||
</div>
|
||||
{#each commands as command}
|
||||
<div class="flex-between mb-2">
|
||||
<CommandPresenter value={command} />
|
||||
<Button
|
||||
icon={IconDelete}
|
||||
kind="transparent"
|
||||
on:click={() => {
|
||||
removeCommand(command)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
<div class="flex flex-gap-2">
|
||||
{#if contentAttributes.length > 0}
|
||||
<Button
|
||||
label={automation.string.Content}
|
||||
kind="no-border"
|
||||
on:click={() => {
|
||||
currentTab = ActionTab.Content
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{#if dateAttributes.length > 0}
|
||||
<Button
|
||||
label={automation.string.Dates}
|
||||
kind="no-border"
|
||||
on:click={() => {
|
||||
currentTab = ActionTab.Dates
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<!--
|
||||
<Button
|
||||
label={automation.string.Chat}
|
||||
kind="no-border"
|
||||
on:click={() => {
|
||||
currentTab = ActionTab.Chat
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label={automation.string.Tracker}
|
||||
kind="no-border"
|
||||
on:click={() => {
|
||||
currentTab = ActionTab.Tracker
|
||||
}}
|
||||
/>
|
||||
-->
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
{#if currentTab === ActionTab.Content && targetClass}
|
||||
{#each contentAttributes as attr}
|
||||
<ContentActionCreate
|
||||
attribute={attributes.get(attr)}
|
||||
automationSupport={attributeSupportMap.get(attr)}
|
||||
{targetClass}
|
||||
on:add={addCommand}
|
||||
/>
|
||||
{/each}
|
||||
<!-- {:else if currentTab === ActionTab.Dates}{:else if currentTab === ActionTab.Chat}{:else if currentTab === ActionTab.Tracker} -->
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import ui, { Button, Icon, IconAdd, IconBack, Label } from '@anticrm/ui'
|
||||
import automation from '../plugin'
|
||||
import AddAutomation from './AddAutomation.svelte'
|
||||
import Automations from './Automations.svelte'
|
||||
|
||||
let isAdding = false
|
||||
</script>
|
||||
|
||||
<div class="antiPanel-component">
|
||||
<div class="ac-header full withSettings short divide">
|
||||
<div class="ac-header__wrap-title">
|
||||
<div class="ac-header__icon"><Icon icon={automation.icon.Automation} size="small" /></div>
|
||||
<span class="ac-header__title"><Label label={automation.string.Automation} /></span>
|
||||
</div>
|
||||
<Button
|
||||
icon={isAdding ? IconBack : IconAdd}
|
||||
label={isAdding ? ui.string.Cancel : automation.string.Automation}
|
||||
kind={isAdding ? 'no-border' : 'primary'}
|
||||
on:click={() => {
|
||||
isAdding = !isAdding
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if isAdding}
|
||||
<div class="ac-body columns hScroll">
|
||||
<AddAutomation
|
||||
on:close={() => {
|
||||
isAdding = false
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<Automations />
|
||||
{/if}
|
||||
</div>
|
@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { Button, IconDelete, Label } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Trigger } from '../models'
|
||||
import automation from '../plugin'
|
||||
import TriggerPresenter from './presenters/TriggerPresenter.svelte'
|
||||
import AddActionTrigger from './AddActionTrigger.svelte'
|
||||
|
||||
export let trigger: Trigger | undefined = undefined
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function clearTrigger () {
|
||||
trigger = undefined
|
||||
dispatch('trigger', trigger)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-col">
|
||||
<div class="fs-title mb-4">
|
||||
<Label label={automation.string.Trigger} />
|
||||
</div>
|
||||
{#if trigger}
|
||||
<div class="flex-between">
|
||||
<TriggerPresenter value={trigger} />
|
||||
<Button icon={IconDelete} kind="transparent" on:click={clearTrigger} />
|
||||
</div>
|
||||
{:else}
|
||||
<AddActionTrigger on:trigger on:targetClass />
|
||||
{/if}
|
||||
</div>
|
@ -0,0 +1,6 @@
|
||||
<script lang="ts">
|
||||
import automation from '@anticrm/automation'
|
||||
import { TableBrowser } from '@anticrm/view-resources'
|
||||
</script>
|
||||
|
||||
<TableBrowser _class={automation.class.Automation} config={['name', 'description', 'modifiedOn']} query={{}} />
|
@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import { CommandType, UpdateDocCommand } from '@anticrm/automation'
|
||||
import core, { AnyAttribute, Class, Doc, Ref } from '@anticrm/core'
|
||||
import ui, { Button, EditBox, IconAdd, Label } from '@anticrm/ui'
|
||||
import { BooleanEditor, NumberEditor } from '@anticrm/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import automation from '../../plugin'
|
||||
|
||||
export let automationSupport: { name: string }
|
||||
export let attribute: AnyAttribute | undefined = undefined
|
||||
export let targetClass: Ref<Class<Doc>>
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const typeClass = attribute?.type._class
|
||||
|
||||
let value: string | undefined = undefined
|
||||
|
||||
function onChange (v: any) {
|
||||
value = v
|
||||
}
|
||||
|
||||
function add () {
|
||||
if (attribute && value !== undefined) {
|
||||
const command: UpdateDocCommand<any> = {
|
||||
type: CommandType.UpdateDoc,
|
||||
targetClass,
|
||||
update: {
|
||||
[attribute.name]: value
|
||||
}
|
||||
}
|
||||
dispatch('add', command)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if attribute && automationSupport}
|
||||
<div class="flex-between">
|
||||
<div class="flex flex-gap-1 mr-3 items-center">
|
||||
<Label label={automation.string.Set} />
|
||||
<span class="font-semi-bold">
|
||||
<Label label={attribute.label} />
|
||||
</span>
|
||||
<Label label={automation.string.To} />
|
||||
<div>
|
||||
{#if typeClass === core.class.TypeString || typeClass === core.class.TypeMarkup}
|
||||
<EditBox bind:value />
|
||||
{:else if typeClass === core.class.TypeNumber}
|
||||
<NumberEditor value={Number(value)} {onChange} focus={true} placeholder={ui.string.EditBoxPlaceholder} />
|
||||
{:else if typeClass === core.class.TypeBoolean}
|
||||
<BooleanEditor {value} {onChange} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<Button icon={IconAdd} kind="transparent" on:click={add} />
|
||||
</div>
|
||||
{/if}
|
@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { Doc } from '@anticrm/core'
|
||||
import { Command, isUpdateDocCommand, UpdateDocCommand } from '@anticrm/automation'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Label } from '@anticrm/ui'
|
||||
import { ClassPresenter } from '@anticrm/view-resources'
|
||||
|
||||
import automation from '../../plugin'
|
||||
|
||||
export let value: Command<Doc>
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
function toUpdateDocCommand (command: Command<Doc>): UpdateDocCommand<Doc> {
|
||||
return command as UpdateDocCommand<Doc>
|
||||
}
|
||||
function toObjectValue (obj: any, attr: any): string {
|
||||
if (!obj || !attr) {
|
||||
return ''
|
||||
}
|
||||
return obj[attr]?.toString()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#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)}
|
||||
<div class="flex flex-gap-1 items-center">
|
||||
<Label label={automation.string.Update} />
|
||||
<span class="font-semi-bold">
|
||||
<ClassPresenter value={targetClass} />
|
||||
</span>
|
||||
<Label label={automation.string.Set} />
|
||||
<span class="font-semi-bold">
|
||||
<Label label={attribute.label} />
|
||||
</span>
|
||||
<Label label={automation.string.To} />
|
||||
<span>{toObjectValue(updateCommand.update, attr)}</span>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Icon, Label } from '@anticrm/ui'
|
||||
import { ClassPresenter } from '@anticrm/view-resources'
|
||||
import { Trigger } from '../../models'
|
||||
import automation from '../../plugin'
|
||||
|
||||
export let value: Trigger
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
</script>
|
||||
|
||||
{#if value.action}
|
||||
{@const targetClass = hierarchy.getClass(value.action.target)}
|
||||
<div class="flex flex-gap-1 items-center">
|
||||
<span class="mr-4">
|
||||
<Label label={automation.string.Menu} />
|
||||
</span>
|
||||
{#if value.action.icon}
|
||||
<Icon icon={value.action.icon} size="small" />
|
||||
{/if}
|
||||
<span class="mr-2">{value.action.label}</span>
|
||||
<Label label={automation.string.In} />
|
||||
<span class="font-semi-bold">
|
||||
<ClassPresenter value={targetClass} />
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { Class, Obj } from '@anticrm/core'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { DropdownLabels, DropdownTextItem } from '@anticrm/ui'
|
||||
|
||||
import automation from '../../plugin'
|
||||
|
||||
export let classes: Class<Obj>[] = []
|
||||
|
||||
async function getClassItems () {
|
||||
const classItems: DropdownTextItem[] = []
|
||||
for (const cl of classes) {
|
||||
const label = await translate(cl.label, {})
|
||||
classItems.push({ id: cl._id, label })
|
||||
}
|
||||
return classItems
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await getClassItems() then classItems}
|
||||
<DropdownLabels label={automation.string.SelectClass} items={classItems} on:selected />
|
||||
{/await}
|
@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import { Metadata } from '@anticrm/platform'
|
||||
import presentation, { Card } from '@anticrm/presentation'
|
||||
import { Button } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import automation from '../../plugin'
|
||||
|
||||
export let icon: Metadata<string> | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const icons = [
|
||||
view.icon.Archive,
|
||||
view.icon.ArrowRight,
|
||||
view.icon.Card,
|
||||
view.icon.Delete,
|
||||
view.icon.Model,
|
||||
view.icon.MoreH,
|
||||
view.icon.Move,
|
||||
view.icon.Open,
|
||||
view.icon.Pin,
|
||||
view.icon.Setting,
|
||||
view.icon.Statuses,
|
||||
view.icon.Table,
|
||||
view.icon.Views
|
||||
]
|
||||
|
||||
function save () {
|
||||
dispatch('close', icon)
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={automation.string.Automation}
|
||||
okLabel={presentation.string.Save}
|
||||
okAction={save}
|
||||
canSave={icon !== undefined}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="float-left-box">
|
||||
{#each icons as obj}
|
||||
<div class="float-left p-2">
|
||||
<Button
|
||||
icon={obj}
|
||||
size="medium"
|
||||
kind={obj === icon ? 'primary' : 'transparent'}
|
||||
on:click={() => {
|
||||
icon = obj
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Card>
|
27
plugins/automation-resources/src/index.ts
Normal file
27
plugins/automation-resources/src/index.ts
Normal file
@ -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<Resources> => ({
|
||||
component: {
|
||||
AutomationSettingsElement
|
||||
},
|
||||
action: {
|
||||
PerformAutomation: performAutomation
|
||||
}
|
||||
})
|
21
plugins/automation-resources/src/models.ts
Normal file
21
plugins/automation-resources/src/models.ts
Normal file
@ -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<Class<Doc>>
|
||||
label: string
|
||||
icon?: Asset
|
||||
}
|
||||
}
|
28
plugins/automation-resources/src/plugin.ts
Normal file
28
plugins/automation-resources/src/plugin.ts
Normal file
@ -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
|
120
plugins/automation-resources/src/utils.ts
Normal file
120
plugins/automation-resources/src/utils.ts
Normal file
@ -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<Command<Doc>>,
|
||||
props: { targetClass?: Ref<Class<Doc>>, attachedTo?: Doc, description?: string } = {}
|
||||
): Promise<Ref<Automation<Doc>>> {
|
||||
let space: Ref<Space> = automation.space.Automation
|
||||
let attachedTo: Ref<Doc> = automation.space.Automation
|
||||
let attachedToClass: Ref<Class<Doc>> = 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<Space>,
|
||||
automationId: Ref<Automation<Doc>>
|
||||
): Promise<Doc | undefined> {
|
||||
if (trigger.action !== undefined) {
|
||||
const triggerId = await client.createDoc<Action<Doc, PerformAutomationProps>>(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<Action<Doc, PerformAutomationProps>>(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<void> {
|
||||
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<Doc>): Promise<void> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
plugins/automation-resources/svelte.config.js
Normal file
5
plugins/automation-resources/svelte.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
const sveltePreprocess = require('svelte-preprocess')
|
||||
|
||||
module.exports = {
|
||||
preprocess: sveltePreprocess()
|
||||
};
|
16
plugins/automation-resources/tsconfig.json
Normal file
16
plugins/automation-resources/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
7
plugins/automation/.eslintrc.js
Normal file
7
plugins/automation/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
plugins/automation/.npmignore
Normal file
4
plugins/automation/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
18
plugins/automation/config/rig.json
Normal file
18
plugins/automation/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
35
plugins/automation/package.json
Normal file
35
plugins/automation/package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
148
plugins/automation/src/index.ts
Normal file
148
plugins/automation/src/index.ts
Normal file
@ -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<T extends Doc> {
|
||||
name: keyof T
|
||||
sort?: {
|
||||
groupBy?: DocumentQuery<Doc>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AttributeAutomationTriggerSupport<T extends Doc> {
|
||||
name: keyof T
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AutomationTriggerSupport<T extends Doc> {
|
||||
action?: {
|
||||
mode: ('editor' | 'context')[]
|
||||
}
|
||||
attributes?: AttributeAutomationTriggerSupport<T>[]
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AutomationSortSupport<T extends Doc> {
|
||||
groupBy?: DocumentQuery<T>
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AutomationSupport<T extends Doc> extends Class<Doc> {
|
||||
attributes: AttributeAutomationSupport<T>[]
|
||||
trigger: AutomationTriggerSupport<T>
|
||||
sort?: AutomationSortSupport<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Command<T extends Doc> {
|
||||
fetch?: DocumentQuery<T>
|
||||
type: CommandType
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface UpdateDocCommand<T extends Doc> extends Command<T> {
|
||||
type: CommandType.UpdateDoc
|
||||
targetClass: Ref<Class<T>>
|
||||
update: DocumentUpdate<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function isUpdateDocCommand (command: Command<Doc>): command is UpdateDocCommand<Doc> {
|
||||
return command.type === CommandType.UpdateDoc
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Automation<T extends Doc> extends AttachedDoc {
|
||||
name: string
|
||||
description: string | null
|
||||
targetClass: Ref<Class<T>> | null
|
||||
trigger: {
|
||||
type: TriggerType
|
||||
}
|
||||
commands: Command<T>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface PerformAutomationProps {
|
||||
automationId: Ref<Automation<Doc>>
|
||||
automationClass: Ref<Class<Automation<Doc>>>
|
||||
}
|
||||
|
||||
export default plugin(automationId, {
|
||||
class: {
|
||||
Automation: '' as Ref<Class<Automation<Doc>>>
|
||||
},
|
||||
action: {
|
||||
PerformAutomation: '' as ViewAction<PerformAutomationProps>
|
||||
},
|
||||
mixin: {
|
||||
AutomationSupport: '' as Ref<Mixin<AutomationSupport<Doc>>>
|
||||
},
|
||||
category: {
|
||||
Automation: '' as Ref<ActionCategory>
|
||||
},
|
||||
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<Space>
|
||||
}
|
||||
})
|
9
plugins/automation/tsconfig.json
Normal file
9
plugins/automation/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"lib": ["esnext", "dom"]
|
||||
}
|
||||
}
|
@ -99,7 +99,12 @@ export {
|
||||
UpDownNavigator,
|
||||
ViewletSettingButton,
|
||||
FilterBar,
|
||||
ClassAttributeBar
|
||||
ClassAttributeBar,
|
||||
ClassPresenter,
|
||||
BooleanEditor,
|
||||
BooleanPresenter,
|
||||
NumberEditor,
|
||||
NumberPresenter
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
|
@ -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,
|
||||
|
@ -400,6 +400,26 @@ const view = plugin(viewId, {
|
||||
LinkPresenter: '' as Ref<Class<LinkPresenter>>,
|
||||
FilterMode: '' as Ref<Class<FilterMode>>
|
||||
},
|
||||
action: {
|
||||
Delete: '' as Ref<Action>,
|
||||
Move: '' as Ref<Action>,
|
||||
MoveLeft: '' as Ref<Action>,
|
||||
MoveRight: '' as Ref<Action>,
|
||||
MoveUp: '' as Ref<Action>,
|
||||
MoveDown: '' as Ref<Action>,
|
||||
|
||||
SelectItem: '' as Ref<Action>,
|
||||
SelectItemAll: '' as Ref<Action>,
|
||||
SelectItemNone: '' as Ref<Action>,
|
||||
SelectUp: '' as Ref<Action>,
|
||||
SelectDown: '' as Ref<Action>,
|
||||
|
||||
ShowPreview: '' as Ref<Action>,
|
||||
ShowActions: '' as Ref<Action>,
|
||||
|
||||
// Edit document
|
||||
Open: '' as Ref<Action>
|
||||
},
|
||||
viewlet: {
|
||||
Table: '' as Ref<ViewletDescriptor>
|
||||
},
|
||||
@ -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,
|
||||
|
20
rush.json
20
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",
|
||||
|
Loading…
Reference in New Issue
Block a user