mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 11:42:30 +03:00
Tasks as Attached Documents (#539)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
78f4916b6d
commit
e958388e4c
@ -51,7 +51,7 @@ specifiers:
|
||||
'@rush-temp/model-server-chunter': file:./projects/model-server-chunter.tgz
|
||||
'@rush-temp/model-server-core': file:./projects/model-server-core.tgz
|
||||
'@rush-temp/model-server-recruit': file:./projects/model-server-recruit.tgz
|
||||
'@rush-temp/model-server-view': file:./projects/model-server-view.tgz
|
||||
'@rush-temp/model-server-task': file:./projects/model-server-task.tgz
|
||||
'@rush-temp/model-setting': file:./projects/model-setting.tgz
|
||||
'@rush-temp/model-task': file:./projects/model-task.tgz
|
||||
'@rush-temp/model-telegram': file:./projects/model-telegram.tgz
|
||||
@ -74,8 +74,8 @@ specifiers:
|
||||
'@rush-temp/server-core': file:./projects/server-core.tgz
|
||||
'@rush-temp/server-recruit': file:./projects/server-recruit.tgz
|
||||
'@rush-temp/server-recruit-resources': file:./projects/server-recruit-resources.tgz
|
||||
'@rush-temp/server-view': file:./projects/server-view.tgz
|
||||
'@rush-temp/server-view-resources': file:./projects/server-view-resources.tgz
|
||||
'@rush-temp/server-task': file:./projects/server-task.tgz
|
||||
'@rush-temp/server-task-resources': file:./projects/server-task-resources.tgz
|
||||
'@rush-temp/server-ws': file:./projects/server-ws.tgz
|
||||
'@rush-temp/setting': file:./projects/setting.tgz
|
||||
'@rush-temp/setting-assets': file:./projects/setting-assets.tgz
|
||||
@ -220,7 +220,7 @@ dependencies:
|
||||
'@rush-temp/model-server-chunter': file:projects/model-server-chunter.tgz_typescript@4.4.3
|
||||
'@rush-temp/model-server-core': file:projects/model-server-core.tgz_typescript@4.4.3
|
||||
'@rush-temp/model-server-recruit': file:projects/model-server-recruit.tgz_typescript@4.4.3
|
||||
'@rush-temp/model-server-view': file:projects/model-server-view.tgz_typescript@4.4.3
|
||||
'@rush-temp/model-server-task': file:projects/model-server-task.tgz_typescript@4.4.3
|
||||
'@rush-temp/model-setting': file:projects/model-setting.tgz_typescript@4.4.3
|
||||
'@rush-temp/model-task': file:projects/model-task.tgz_typescript@4.4.3
|
||||
'@rush-temp/model-telegram': file:projects/model-telegram.tgz_typescript@4.4.3
|
||||
@ -243,8 +243,8 @@ dependencies:
|
||||
'@rush-temp/server-core': file:projects/server-core.tgz
|
||||
'@rush-temp/server-recruit': file:projects/server-recruit.tgz
|
||||
'@rush-temp/server-recruit-resources': file:projects/server-recruit-resources.tgz
|
||||
'@rush-temp/server-view': file:projects/server-view.tgz
|
||||
'@rush-temp/server-view-resources': file:projects/server-view-resources.tgz
|
||||
'@rush-temp/server-task': file:projects/server-task.tgz
|
||||
'@rush-temp/server-task-resources': file:projects/server-task-resources.tgz
|
||||
'@rush-temp/server-ws': file:projects/server-ws.tgz
|
||||
'@rush-temp/setting': file:projects/setting.tgz
|
||||
'@rush-temp/setting-assets': file:projects/setting-assets.tgz
|
||||
@ -10588,7 +10588,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/generator.tgz:
|
||||
resolution: {integrity: sha512-vufLNo3Nd5EZMfZnpDyCgzmtiOxuuOCfdcez1VUXR8EJV+07YGGLKpYcUsJ+hp7vrOMe5/iW0gGun4p42SEL9Q==, tarball: file:projects/generator.tgz}
|
||||
resolution: {integrity: sha512-Difehi/KDbulPB9s3UOP1fSeLfvWQwpSUr77OnMF31EoCLAXT+9ugFduvlveFdnYCttYKpgGO2bvaxBaH7krdA==, tarball: file:projects/generator.tgz}
|
||||
name: '@rush-temp/generator'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -10633,7 +10633,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/lead-resources.tgz_476f694f64637160ae71e12ff57815b9:
|
||||
resolution: {integrity: sha512-o80Jqqi39oV9wTk/nhjlU53bHy92gsqqCQo1nWTsPLlMB+R1nYY5/Yw/GFxsuVHSzbzjmahUnkHIOxltJMSTig==, tarball: file:projects/lead-resources.tgz}
|
||||
resolution: {integrity: sha512-Bko+x/XDsWFrhZ4EfXCZ8GwS56DMgWA5t2tYGyPF7nlzIPY97oJWPZGc2z1T7olyx0bye+Od9Vk7qmZI3KuBkQ==, tarball: file:projects/lead-resources.tgz}
|
||||
id: file:projects/lead-resources.tgz
|
||||
name: '@rush-temp/lead-resources'
|
||||
version: 0.0.0
|
||||
@ -10668,7 +10668,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/lead.tgz:
|
||||
resolution: {integrity: sha512-fdeirdweHpZ+WcGz5xlKcvue+gtDXhK5WNFINY6XnhVgfUvNsEtEgiWX1RaQ8uSReXbaEok6GGmhsV29SOr1lQ==, tarball: file:projects/lead.tgz}
|
||||
resolution: {integrity: sha512-M28QADdDu2RRVXMqicIrIf8ithwSkKwdxaOe2HZJrvO+K8wXarOtFKkFf8Hg0Kqjtf6oYkS7RZMQhnwf3Fe94Q==, tarball: file:projects/lead.tgz}
|
||||
name: '@rush-temp/lead'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -10770,7 +10770,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-all.tgz_typescript@4.4.3:
|
||||
resolution: {integrity: sha512-nbSO6SqE93R4cFPMUkIU19Nn5ZAMItWdQ5+7++7lBjqpJUtUxxgWsIPHjk1wmDmdu4LjPq5F5UruZwQQgu7wJw==, tarball: file:projects/model-all.tgz}
|
||||
resolution: {integrity: sha512-XyhGIWfnSxUKXSXQ/yfvCx+S5xqBMJ4eko1pDFv0v048sQJy6fefxg3LOLyCDodNzsa5KxGdqvk7Y8H7Izop0g==, tarball: file:projects/model-all.tgz}
|
||||
id: file:projects/model-all.tgz
|
||||
name: '@rush-temp/model-all'
|
||||
version: 0.0.0
|
||||
@ -10900,7 +10900,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-lead.tgz_typescript@4.4.3:
|
||||
resolution: {integrity: sha512-qEMZ6aUEiEZgFvIMcqR8lTVkKKiUNM4eAa5OeLjnDmY1TkFF60EJpnE0e2D1SzO8zPdS+cDH+jT9FqRvXtkO9A==, tarball: file:projects/model-lead.tgz}
|
||||
resolution: {integrity: sha512-3+Wf+/TdMpbuSoiTaI/ga+1KHSsPd1q9MDtpG6i4oAhxt/48aTZnfOV4nt9hrpWy2MmQWVhK9YJFTaKGIgKwOA==, tarball: file:projects/model-lead.tgz}
|
||||
id: file:projects/model-lead.tgz
|
||||
name: '@rush-temp/model-lead'
|
||||
version: 0.0.0
|
||||
@ -10921,7 +10921,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-recruit.tgz_typescript@4.4.3:
|
||||
resolution: {integrity: sha512-LgqXtVecQ6ZdlAb4ufvWI+kDIeeMFtm/5mXvUXBbGsmg07rCa4tsqfm9uwecj6TvpaPYC3mlyYv/N316VeKeGA==, tarball: file:projects/model-recruit.tgz}
|
||||
resolution: {integrity: sha512-wIeXWWUD1Lwg8R43hEvmJzZ/ooN4DWdqv1+2vPFDZU/1xKwlW7Gqu1hZM5qUUVHbLLgMOmHsHDscPfn7tB11UQ==, tarball: file:projects/model-recruit.tgz}
|
||||
id: file:projects/model-recruit.tgz
|
||||
name: '@rush-temp/model-recruit'
|
||||
version: 0.0.0
|
||||
@ -11023,10 +11023,10 @@ packages:
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-server-view.tgz_typescript@4.4.3:
|
||||
resolution: {integrity: sha512-1LEHcc6xr1A+plRNPtWBHEp8RooarT/XXKfovuqvHugwdjz9ddTHYThxrQcZFuUMya+F1mBgOHdohjYikWZnXQ==, tarball: file:projects/model-server-view.tgz}
|
||||
id: file:projects/model-server-view.tgz
|
||||
name: '@rush-temp/model-server-view'
|
||||
file:projects/model-server-task.tgz_typescript@4.4.3:
|
||||
resolution: {integrity: sha512-FZRz0sa1wBabVBBZietKGBDDF6vrJzSH6bcWlm8K3OkLl9jHoZlXRjBHIx4f0Vo1DSIPSH++/gULbgNAsd2p1A==, tarball: file:projects/model-server-task.tgz}
|
||||
id: file:projects/model-server-task.tgz
|
||||
name: '@rush-temp/model-server-task'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.1
|
||||
@ -11339,7 +11339,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod.tgz_sass@1.42.1+typescript@4.4.3:
|
||||
resolution: {integrity: sha512-LFYJVCBzP4R4RpgdxrsiolRiVKAZn2F5mKKb0prXnYIKrRVKLlMkPexB7bnNbg5KG0NBey6WZoxek6/cQ1xk6w==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-jgSulZmT4Kq28boQo8BJQrdkcN660EtY7XUH0QWcgGYHDBXFiGfP4eoIxAJL4wtb/JKwKLEkwG+SwTUIuIb7Xw==, tarball: file:projects/prod.tgz}
|
||||
id: file:projects/prod.tgz
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
@ -11413,7 +11413,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/recruit-resources.tgz_476f694f64637160ae71e12ff57815b9:
|
||||
resolution: {integrity: sha512-+AEmp4tNJEHvQatbY5fN1niGYpaIyWMPXbo0QRhUDELdEkm0nhevUfw2Cf44Yj0DroXL+S0YgnXcEI7AtV2QOQ==, tarball: file:projects/recruit-resources.tgz}
|
||||
resolution: {integrity: sha512-DtOMgqpR+/A09g0gIsXhEy9/GmgTcIEypvvZ53m//Q4YVYXG8/u82bpE6Na74+g4FyQ+S9FaNwMXH2Da1WDnwQ==, tarball: file:projects/recruit-resources.tgz}
|
||||
id: file:projects/recruit-resources.tgz
|
||||
name: '@rush-temp/recruit-resources'
|
||||
version: 0.0.0
|
||||
@ -11450,7 +11450,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/recruit.tgz:
|
||||
resolution: {integrity: sha512-nqrkga8ccMV8sqQ6O4ta9qEdvHkgEChdViELXUXViGldrhz6qi9UAE1aDuVYHgEFRX+eC+OACSME0YpHbibylw==, tarball: file:projects/recruit.tgz}
|
||||
resolution: {integrity: sha512-vvQ892DoJCDRaRuVWx+DBpuKPtCic0O0PdgPDtbKxl+pWk31MVdEhO6m+ySpqkPsu297ot/D7lpYXcK/775fow==, tarball: file:projects/recruit.tgz}
|
||||
name: '@rush-temp/recruit'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -11573,9 +11573,9 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-view-resources.tgz:
|
||||
resolution: {integrity: sha512-8Gg8dO9C0bLtdRQ08F7Yegi8o4Y/VwljPEHRevt7Z6+SpLrpBcbouBgdIaiELK+oXQiPfZWdl3Fwyhd54YJGQQ==, tarball: file:projects/server-view-resources.tgz}
|
||||
name: '@rush-temp/server-view-resources'
|
||||
file:projects/server-task-resources.tgz:
|
||||
resolution: {integrity: sha512-Y2B9G8w4IpTnfIoDV+LtiARLb568CfTKG28VyMc2faS4QUz62f3PLU+ODB8NxwG5f1LsJf8DLrZM8fPRzYdc6w==, tarball: file:projects/server-task-resources.tgz}
|
||||
name: '@rush-temp/server-task-resources'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.1
|
||||
@ -11593,9 +11593,9 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-view.tgz:
|
||||
resolution: {integrity: sha512-7VfwkR2Yqg0aeuFS+1MWOhCqtuiQBXFqoyxmflQ5+cigwTpJko06NkKnFlGFUW3V+XCxZH4i36PQJod2MXXrng==, tarball: file:projects/server-view.tgz}
|
||||
name: '@rush-temp/server-view'
|
||||
file:projects/server-task.tgz:
|
||||
resolution: {integrity: sha512-qaB/cZSAPKuvTUfBv5wsqD0r/YoQO/hy7rD7WBC0lx2TOWIp0/aacYKVkEltinbYjJzP4gg9cQQNful4Mvpp5Q==, tarball: file:projects/server-task.tgz}
|
||||
name: '@rush-temp/server-task'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.1
|
||||
@ -11641,7 +11641,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server.tgz:
|
||||
resolution: {integrity: sha512-V8XWLBzilsZ/5Nuh52ZM8D02XsIGYuOHKhu6q+q7ERPCRm/q3GTMzcZ3WpE3A3ngtFDxHIZJMS314gK50XKM6g==, tarball: file:projects/server.tgz}
|
||||
resolution: {integrity: sha512-fFYknnDfAOz+iq8INHKSgCjYUkbxqLiHIBM55W8QtmZq0R8hMLEwCcLiyNqL1iCgXKs8sGHf+aMbZcciBNi1Ow==, tarball: file:projects/server.tgz}
|
||||
name: '@rush-temp/server'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -11673,7 +11673,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/setting-resources.tgz_476f694f64637160ae71e12ff57815b9:
|
||||
resolution: {integrity: sha512-Ybi5GrbbI/5SMBIe5W9Ub8cwA3wIKPCj98lSJ+Z9YCokpjYn2dGhRJ5LeBKBPu8LDWPfxlrU2Yi17YcHu0xufw==, tarball: file:projects/setting-resources.tgz}
|
||||
resolution: {integrity: sha512-r/wZZM67R0JwXnUUHxlo5gS1wshjqgf6qJx/fBKfe5d59ZqIXvLD41NhkNBwA7iQsRd+QNmCS4u1fNtCjl20dQ==, tarball: file:projects/setting-resources.tgz}
|
||||
id: file:projects/setting-resources.tgz
|
||||
name: '@rush-temp/setting-resources'
|
||||
version: 0.0.0
|
||||
@ -11734,7 +11734,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/task-resources.tgz_e1367da94684b005adf08f025c517b1a:
|
||||
resolution: {integrity: sha512-8q7drsS3cuBhtj4PIl+/QIH8QDt0fZWk89fH3FxY/3Oog8Xd5dGkBH4YdPHUSyk9k5u9iS/P8OwvR9CKMBmxRg==, tarball: file:projects/task-resources.tgz}
|
||||
resolution: {integrity: sha512-s/MtNSr/ERDwItbtGtcaE+nnTepUKUsuD2SrtGcpgYUXT3XarZyVaGHC4Q9ol6Ok7RajfDSdAj1ZD8V2LLn3Pw==, tarball: file:projects/task-resources.tgz}
|
||||
id: file:projects/task-resources.tgz
|
||||
name: '@rush-temp/task-resources'
|
||||
version: 0.0.0
|
||||
@ -11769,7 +11769,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/task.tgz:
|
||||
resolution: {integrity: sha512-Yi88o15ExlXV9xqFXzpmi4I5OjnnY7l6SiQasIC7HqKc5/lG4KFvw/dlZHsFnEbRw3Zxa+laq9MejSMqLEzEVw==, tarball: file:projects/task.tgz}
|
||||
resolution: {integrity: sha512-H61a9iYDCDExaqx08I8pBpSZj2gDyDvF+K+R/QjML1YouYeUa1M1UPpjfz4DHwT7a67TJCasvqJ9nh0/ZNvcDw==, tarball: file:projects/task.tgz}
|
||||
name: '@rush-temp/task'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
@ -18,7 +18,7 @@ services:
|
||||
volumes:
|
||||
- files:/data
|
||||
elastic:
|
||||
image: 'elasticsearch:7.14.0'
|
||||
image: 'elasticsearch:7.14.2'
|
||||
command: |
|
||||
/bin/sh -c "./bin/elasticsearch-plugin list | grep -q ingest-attachment || yes | ./bin/elasticsearch-plugin install --silent ingest-attachment;
|
||||
/usr/local/bin/docker-entrypoint.sh eswrapper"
|
||||
|
@ -58,7 +58,7 @@
|
||||
"@anticrm/attachment": "~0.6.1",
|
||||
"minio": "^7.0.19",
|
||||
"@types/pdfkit": "~0.12.3",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"jpeg-js": "~0.4.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import core, { DoneState, Ref, SpaceWithStates, State, TxOperations } from '@anticrm/core'
|
||||
|
||||
import { Ref, TxOperations } from '@anticrm/core'
|
||||
import task, { DoneState, Kanban, SpaceWithStates, State } from '@anticrm/task'
|
||||
import { findOrUpdate } from './utils'
|
||||
import view, { Kanban } from '@anticrm/view'
|
||||
|
||||
export async function createUpdateSpaceKanban (spaceId: Ref<SpaceWithStates>, client: TxOperations): Promise<Ref<State>[]> {
|
||||
const rawStates = [
|
||||
@ -13,7 +14,7 @@ export async function createUpdateSpaceKanban (spaceId: Ref<SpaceWithStates>, cl
|
||||
const states: Array<Ref<State>> = []
|
||||
for (const st of rawStates) {
|
||||
const sid = ('generated-' + spaceId + '.state.' + st.name.toLowerCase().replace(' ', '_')) as Ref<State>
|
||||
await findOrUpdate(client, spaceId, core.class.State,
|
||||
await findOrUpdate(client, spaceId, task.class.State,
|
||||
sid,
|
||||
{
|
||||
title: st.name,
|
||||
@ -24,8 +25,8 @@ export async function createUpdateSpaceKanban (spaceId: Ref<SpaceWithStates>, cl
|
||||
}
|
||||
|
||||
const rawDoneStates = [
|
||||
{ class: core.class.WonState, title: 'Won' },
|
||||
{ class: core.class.LostState, title: 'Lost' }
|
||||
{ class: task.class.WonState, title: 'Won' },
|
||||
{ class: task.class.LostState, title: 'Lost' }
|
||||
]
|
||||
const doneStates: Array<Ref<DoneState>> = []
|
||||
for (const st of rawDoneStates) {
|
||||
@ -40,7 +41,7 @@ export async function createUpdateSpaceKanban (spaceId: Ref<SpaceWithStates>, cl
|
||||
}
|
||||
|
||||
await findOrUpdate(client, spaceId,
|
||||
view.class.Kanban,
|
||||
task.class.Kanban,
|
||||
('generated-' + spaceId + '.kanban') as Ref<Kanban>,
|
||||
{
|
||||
attachedTo: spaceId,
|
||||
|
@ -98,7 +98,7 @@ export async function generateContacts (transactorUrl: string, dbName: string, o
|
||||
|
||||
const applicant: AttachedData<Applicant> = {
|
||||
number: faker.datatype.number(),
|
||||
employee: faker.random.arrayElement(emoloyeeIds),
|
||||
assignee: faker.random.arrayElement(emoloyeeIds),
|
||||
state: faker.random.arrayElement(states),
|
||||
doneState: null
|
||||
}
|
||||
|
@ -71,8 +71,8 @@
|
||||
"@anticrm/contact-assets": "~0.6.0",
|
||||
"@anticrm/server-recruit": "~0.6.1",
|
||||
"@anticrm/server-recruit-resources": "~0.6.0",
|
||||
"@anticrm/server-view": "~0.6.0",
|
||||
"@anticrm/server-view-resources": "~0.6.0",
|
||||
"@anticrm/server-task": "~0.6.0",
|
||||
"@anticrm/server-task-resources": "~0.6.0",
|
||||
"@anticrm/activity": "~0.6.0",
|
||||
"@anticrm/activity-assets": "~0.6.0",
|
||||
"@anticrm/activity-resources": "~0.6.0",
|
||||
|
@ -19,7 +19,7 @@ import login from '@anticrm/login'
|
||||
import { clientId } from '@anticrm/client'
|
||||
import { serverChunterId } from '@anticrm/server-chunter'
|
||||
import { serverRecruitId } from '@anticrm/server-recruit'
|
||||
import { serverViewId } from '@anticrm/server-view'
|
||||
import { serverViewId } from '@anticrm/server-task'
|
||||
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
|
||||
@ -33,7 +33,7 @@ export function configurePlatformDev() {
|
||||
addLocation(clientId, () => import(/* webpackChunkName: "client-dev" */ '@anticrm/dev-client-resources'))
|
||||
addLocation(serverChunterId, () => import(/* webpackChunkName: "server-chunter" */ '@anticrm/dev-server-chunter-resources'))
|
||||
addLocation(serverRecruitId, () => import(/* webpackChunkName: "server-recruit" */ '@anticrm/server-recruit-resources'))
|
||||
addLocation(serverViewId, () => import(/* webpackChunkName: "server-view" */ '@anticrm/server-view-resources'))
|
||||
addLocation(serverViewId, () => import(/* webpackChunkName: "server-task" */ '@anticrm/server-task-resources'))
|
||||
|
||||
// Set devmodel to hook client to be able to present all activity
|
||||
enableDevModel()
|
||||
|
@ -69,4 +69,22 @@ export class MigrateClientImpl implements MigrationClient {
|
||||
return { matched: result.matchedCount, updated: result.modifiedCount }
|
||||
}
|
||||
}
|
||||
|
||||
async move <T extends Doc>(sourceDomain: Domain, query: DocumentQuery<T>, targetDomain: Domain): Promise<MigrationResult> {
|
||||
const q = this.translateQuery(query)
|
||||
const cursor = this.db.collection(sourceDomain).find<T>(q)
|
||||
const target = this.db.collection(targetDomain)
|
||||
const result: MigrationResult = {
|
||||
matched: 0,
|
||||
updated: 0
|
||||
}
|
||||
let doc: Document | null
|
||||
while ((doc = await cursor.next()) != null) {
|
||||
await target.insertOne(doc)
|
||||
result.matched++
|
||||
result.updated++
|
||||
}
|
||||
await this.db.collection(sourceDomain).deleteMany(q)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@
|
||||
"@anticrm/model-server-core": "~0.6.0",
|
||||
"@anticrm/model-server-chunter": "~0.6.0",
|
||||
"@anticrm/model-server-recruit": "~0.6.0",
|
||||
"@anticrm/model-server-view": "~0.6.0",
|
||||
"@anticrm/model-server-task": "~0.6.0",
|
||||
"@anticrm/model-activity": "~0.6.0",
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/core": "~0.6.13"
|
||||
|
@ -30,7 +30,7 @@ import { createModel as leadModel } from '@anticrm/model-lead'
|
||||
import { createModel as serverCoreModel } from '@anticrm/model-server-core'
|
||||
import { createModel as serverChunterModel } from '@anticrm/model-server-chunter'
|
||||
import { createModel as serverRecruitModel } from '@anticrm/model-server-recruit'
|
||||
import { createModel as serverViewModel } from '@anticrm/model-server-view'
|
||||
import { createModel as serverViewModel } from '@anticrm/model-server-task'
|
||||
import { createModel as activityModel } from '@anticrm/model-activity'
|
||||
|
||||
import { createDemo } from '@anticrm/model-demo'
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { Builder } from '@anticrm/model'
|
||||
import core from './component'
|
||||
import { TAttribute, TClass, TDoc, TMixin, TObj, TType, TTypeString, TTypeBoolean, TTypeTimestamp, TTypeDate, TAttachedDoc, TCollection, TRefTo } from './core'
|
||||
import { TSpace, TAccount, TState, TDoneState, TWonState, TLostState, TSpaceWithStates, TDocWithState } from './security'
|
||||
import { TSpace, TAccount } from './security'
|
||||
import { TTx, TTxCreateDoc, TTxMixin, TTxUpdateDoc, TTxCUD, TTxPutBag, TTxRemoveDoc, TTxBulkWrite, TTxCollectionCUD } from './tx'
|
||||
|
||||
export * from './core'
|
||||
@ -41,8 +41,6 @@ export function createModel (builder: Builder): void {
|
||||
TTxRemoveDoc,
|
||||
TTxBulkWrite,
|
||||
TSpace,
|
||||
TDocWithState,
|
||||
TSpaceWithStates,
|
||||
TAccount,
|
||||
TAttribute,
|
||||
TType,
|
||||
@ -51,10 +49,6 @@ export function createModel (builder: Builder): void {
|
||||
TTypeTimestamp,
|
||||
TRefTo,
|
||||
TCollection,
|
||||
TTypeDate,
|
||||
TState,
|
||||
TDoneState,
|
||||
TWonState,
|
||||
TLostState
|
||||
TTypeDate
|
||||
)
|
||||
}
|
||||
|
@ -13,15 +13,13 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Account, Arr, Domain, Ref, Space, State, DoneState, WonState, LostState } from '@anticrm/core'
|
||||
import type { Account, Arr, Ref, Space } from '@anticrm/core'
|
||||
import { DOMAIN_MODEL } from '@anticrm/core'
|
||||
import { Implements, Model, Prop, TypeBoolean, TypeRef, TypeString } from '@anticrm/model'
|
||||
import { Model, Prop, TypeBoolean, TypeString } from '@anticrm/model'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import core from './component'
|
||||
import { TDoc } from './core'
|
||||
|
||||
export const DOMAIN_STATE = 'state' as Domain
|
||||
|
||||
// S P A C E
|
||||
|
||||
@Model(core.class.Space, core.class.Doc, DOMAIN_MODEL)
|
||||
@ -42,39 +40,3 @@ export class TSpace extends TDoc implements Space {
|
||||
export class TAccount extends TDoc implements Account {
|
||||
email!: string
|
||||
}
|
||||
|
||||
@Model(core.class.State, core.class.Doc, DOMAIN_STATE)
|
||||
export class TState extends TDoc implements State {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
color!: string
|
||||
}
|
||||
|
||||
@Model(core.class.DoneState, core.class.Doc, DOMAIN_STATE)
|
||||
export class TDoneState extends TDoc implements DoneState {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
}
|
||||
|
||||
@Model(core.class.WonState, core.class.DoneState, DOMAIN_STATE)
|
||||
export class TWonState extends TDoneState implements WonState {}
|
||||
|
||||
@Model(core.class.LostState, core.class.DoneState, DOMAIN_STATE)
|
||||
export class TLostState extends TDoneState implements LostState {}
|
||||
|
||||
@Implements(core.interface.DocWithState)
|
||||
export class TDocWithState extends TDoc {
|
||||
@Prop(TypeRef(core.class.State), 'State' as IntlString)
|
||||
state!: Ref<State>
|
||||
|
||||
@Prop(TypeRef(core.class.DoneState), 'Done Status' as IntlString)
|
||||
doneState!: Ref<DoneState> | null
|
||||
|
||||
@Prop(TypeString(), 'No.' as IntlString)
|
||||
number!: number
|
||||
}
|
||||
|
||||
@Model(core.class.SpaceWithStates, core.class.Space)
|
||||
export class TSpaceWithStates extends TSpace {
|
||||
}
|
||||
|
@ -38,6 +38,9 @@
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/lead": "~0.6.0",
|
||||
"@anticrm/lead-resources": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0"
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"@anticrm/model-task": "~0.6.0",
|
||||
"@anticrm/workbench": "~0.6.1"
|
||||
}
|
||||
}
|
||||
|
@ -15,31 +15,29 @@
|
||||
//
|
||||
|
||||
// To help typescript locate view plugin properly
|
||||
import type {} from '@anticrm/view'
|
||||
|
||||
import type { Contact } from '@anticrm/contact'
|
||||
import type { Doc, Domain, FindOptions, Ref } from '@anticrm/core'
|
||||
import type { Doc, FindOptions, Ref } from '@anticrm/core'
|
||||
import type { Funnel, Lead } from '@anticrm/lead'
|
||||
import { createKanban } from '@anticrm/lead'
|
||||
import { Builder, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
import chunter from '@anticrm/model-chunter'
|
||||
import contact from '@anticrm/model-contact'
|
||||
import core, { TDocWithState, TSpaceWithStates } from '@anticrm/model-core'
|
||||
import core from '@anticrm/model-core'
|
||||
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
||||
import view from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { Funnel, Lead } from '@anticrm/lead'
|
||||
import { createKanban } from '@anticrm/lead'
|
||||
import type { } from '@anticrm/view'
|
||||
import lead from './plugin'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
|
||||
export const DOMAIN_LEAD = 'lead' as Domain
|
||||
|
||||
@Model(lead.class.Funnel, core.class.SpaceWithStates)
|
||||
@Model(lead.class.Funnel, task.class.SpaceWithStates)
|
||||
@UX(lead.string.Funnel, lead.icon.Funnel)
|
||||
export class TFunnel extends TSpaceWithStates implements Funnel {}
|
||||
|
||||
@Model(lead.class.Lead, core.class.Doc, DOMAIN_LEAD, [core.interface.DocWithState])
|
||||
@Model(lead.class.Lead, task.class.Task)
|
||||
@UX('Lead' as IntlString)
|
||||
export class TLead extends TDocWithState implements Lead {
|
||||
export class TLead extends TTask implements Lead {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
@ -77,7 +75,7 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}, lead.app.Lead)
|
||||
builder.createDoc(lead.class.Funnel, core.space.Model, {
|
||||
name: 'Funnel',
|
||||
description: 'Default funnel',
|
||||
@ -93,7 +91,7 @@ export function createModel (builder: Builder): void {
|
||||
options: {
|
||||
lookup: {
|
||||
customer: contact.class.Contact,
|
||||
state: core.class.State
|
||||
state: task.class.State
|
||||
}
|
||||
} as FindOptions<Doc>, // TODO: fix
|
||||
config: [
|
||||
@ -108,19 +106,19 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: lead.class.Lead,
|
||||
descriptor: view.viewlet.Kanban,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
open: lead.component.EditLead,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
customer: contact.class.Contact,
|
||||
state: core.class.State
|
||||
state: task.class.State
|
||||
}
|
||||
} as FindOptions<Doc>, // TODO: fix
|
||||
config: ['$lookup.customer', '$lookup.state']
|
||||
})
|
||||
|
||||
builder.mixin(lead.class.Lead, core.class.Class, view.mixin.KanbanCard, {
|
||||
builder.mixin(lead.class.Lead, core.class.Class, task.mixin.KanbanCard, {
|
||||
card: lead.component.KanbanCard
|
||||
})
|
||||
|
||||
@ -132,12 +130,12 @@ export function createModel (builder: Builder): void {
|
||||
presenter: lead.component.LeadPresenter
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Sequence, view.space.Sequence, {
|
||||
builder.createDoc(task.class.Sequence, task.space.Sequence, {
|
||||
attachedTo: lead.class.Lead,
|
||||
sequence: 0
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.KanbanTemplateSpace, core.space.Model, {
|
||||
builder.createDoc(task.class.KanbanTemplateSpace, core.space.Model, {
|
||||
name: 'Funnels',
|
||||
description: 'Manage funnel statuses',
|
||||
members: [],
|
||||
@ -151,5 +149,5 @@ export function createModel (builder: Builder): void {
|
||||
}).catch((err) => console.error(err))
|
||||
}
|
||||
|
||||
export { default } from './plugin'
|
||||
export { leadOperation } from './migration'
|
||||
export { default } from './plugin'
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { Doc, TxOperations } from '@anticrm/core'
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||
import core from '@anticrm/model-core'
|
||||
import view from '@anticrm/model-view'
|
||||
import task from '@anticrm/model-task'
|
||||
import { createKanban } from '@anticrm/lead'
|
||||
import lead from './plugin'
|
||||
|
||||
@ -29,7 +29,7 @@ export const leadOperation: MigrateOperation = {
|
||||
console.log('Lead: Performing model upgrades')
|
||||
|
||||
const ops = new TxOperations(client, core.account.System)
|
||||
if (await client.findOne(view.class.Kanban, { attachedTo: lead.space.DefaultFunnel }) === undefined) {
|
||||
if (await client.findOne(task.class.Kanban, { attachedTo: lead.space.DefaultFunnel }) === undefined) {
|
||||
console.info('Create kanban for default funnel.')
|
||||
await createKanban(lead.space.DefaultFunnel, async (_class, space, data, id) => {
|
||||
const doc = await ops.findOne<Doc>(_class, { _id: id })
|
||||
@ -43,6 +43,17 @@ export const leadOperation: MigrateOperation = {
|
||||
console.log('Lead: => default funnel Kanban is ok')
|
||||
}
|
||||
|
||||
if (await client.findOne(task.class.Sequence, { attachedTo: lead.class.Lead }) === undefined) {
|
||||
console.info('Create sequence for default task project.')
|
||||
// We need to create sequence
|
||||
await ops.createDoc(task.class.Sequence, task.space.Sequence, {
|
||||
attachedTo: lead.class.Lead,
|
||||
sequence: 0
|
||||
})
|
||||
} else {
|
||||
console.log('Task: => sequence is ok')
|
||||
}
|
||||
|
||||
const outdatedLeads = (await client.findAll(lead.class.Lead, {}))
|
||||
.filter((x) => x.doneState === undefined)
|
||||
|
||||
|
@ -15,13 +15,18 @@
|
||||
//
|
||||
|
||||
import type { Ref, Space } from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { mergeIds } from '@anticrm/platform'
|
||||
import { leadId } from '@anticrm/lead'
|
||||
import lead from '@anticrm/lead-resources/src/plugin'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { mergeIds } from '@anticrm/platform'
|
||||
import '@anticrm/task'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import { Application } from '@anticrm/workbench'
|
||||
|
||||
export default mergeIds(leadId, lead, {
|
||||
app: {
|
||||
Lead: '' as Ref<Application>
|
||||
},
|
||||
string: {
|
||||
Funnel: '' as IntlString,
|
||||
Funnels: '' as IntlString,
|
||||
|
@ -39,6 +39,9 @@
|
||||
"@anticrm/chunter": "~0.6.0",
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/model-chunter": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0"
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"@anticrm/model-task": "~0.6.0",
|
||||
"@anticrm/workbench": "~0.6.1"
|
||||
}
|
||||
}
|
||||
|
@ -14,21 +14,20 @@
|
||||
//
|
||||
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import { Doc, Domain, FindOptions, Ref, Timestamp } from '@anticrm/core'
|
||||
import { Builder, Model, Prop, TypeBoolean, TypeDate, TypeRef, TypeString, UX, Collection } from '@anticrm/model'
|
||||
import { Doc, FindOptions, Ref, Timestamp } from '@anticrm/core'
|
||||
import { Builder, Collection, Model, Prop, TypeBoolean, TypeDate, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
import chunter from '@anticrm/model-chunter'
|
||||
import contact, { TPerson } from '@anticrm/model-contact'
|
||||
import core, { TAttachedDoc, TDocWithState, TSpace, TSpaceWithStates } from '@anticrm/model-core'
|
||||
import core, { TSpace } from '@anticrm/model-core'
|
||||
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
||||
import view from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { Applicant, Candidate, Candidates, Vacancy } from '@anticrm/recruit'
|
||||
import recruit from './plugin'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
|
||||
export const DOMAIN_RECRUIT = 'recruit' as Domain
|
||||
|
||||
@Model(recruit.class.Vacancy, core.class.SpaceWithStates)
|
||||
@Model(recruit.class.Vacancy, task.class.SpaceWithStates)
|
||||
@UX(recruit.string.Vacancy, recruit.icon.Vacancy)
|
||||
export class TVacancy extends TSpaceWithStates implements Vacancy {
|
||||
@Prop(TypeString(), 'Full description' as IntlString)
|
||||
@ -76,9 +75,9 @@ export class TCandidate extends TPerson implements Candidate {
|
||||
source?: string
|
||||
}
|
||||
|
||||
@Model(recruit.class.Applicant, core.class.AttachedDoc, DOMAIN_RECRUIT, [core.interface.DocWithState])
|
||||
@Model(recruit.class.Applicant, task.class.Task)
|
||||
@UX('Application' as IntlString, recruit.icon.RecruitApplication, 'APP' as IntlString)
|
||||
export class TApplicant extends TAttachedDoc implements Applicant {
|
||||
export class TApplicant extends TTask implements Applicant {
|
||||
// We need to declare, to provide property with label
|
||||
@Prop(TypeRef(recruit.class.Candidate), 'Candidate' as IntlString)
|
||||
declare attachedTo: Ref<Candidate>
|
||||
@ -90,12 +89,7 @@ export class TApplicant extends TAttachedDoc implements Applicant {
|
||||
comments?: number
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), 'Assigned recruiter' as IntlString)
|
||||
employee!: Ref<Employee> | null
|
||||
|
||||
// We need these to make typescript happy.
|
||||
declare state: TDocWithState['state']
|
||||
declare doneState: TDocWithState['doneState']
|
||||
declare number: TDocWithState['number']
|
||||
declare assignee: Ref<Employee> | null
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
@ -136,7 +130,7 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}, recruit.app.Recruit)
|
||||
builder.createDoc(
|
||||
recruit.class.Candidates,
|
||||
core.space.Model,
|
||||
@ -179,7 +173,7 @@ export function createModel (builder: Builder): void {
|
||||
options: {
|
||||
lookup: {
|
||||
attachedTo: recruit.class.Candidate,
|
||||
state: core.class.State
|
||||
state: task.class.State
|
||||
}
|
||||
} as FindOptions<Doc>, // TODO: fix
|
||||
config: [
|
||||
@ -196,19 +190,19 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: view.viewlet.Kanban,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
open: recruit.component.EditCandidate,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
attachedTo: recruit.class.Candidate,
|
||||
state: core.class.State
|
||||
state: task.class.State
|
||||
}
|
||||
} as FindOptions<Doc>, // TODO: fix
|
||||
config: ['$lookup.attachedTo', '$lookup.state', '$lookup.attachedTo.city', '$lookup.attachedTo.channels']
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.KanbanCard, {
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, task.mixin.KanbanCard, {
|
||||
card: recruit.component.KanbanCard
|
||||
})
|
||||
|
||||
@ -236,12 +230,17 @@ export function createModel (builder: Builder): void {
|
||||
action: recruit.action.CreateApplication
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Sequence, view.space.Sequence, {
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: recruit.class.Candidate,
|
||||
action: task.action.CreateTask
|
||||
})
|
||||
|
||||
builder.createDoc(task.class.Sequence, task.space.Sequence, {
|
||||
attachedTo: recruit.class.Applicant,
|
||||
sequence: 0
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.KanbanTemplateSpace, core.space.Model, {
|
||||
builder.createDoc(task.class.KanbanTemplateSpace, core.space.Model, {
|
||||
name: 'Vacancies',
|
||||
description: 'Manage vacancy statuses',
|
||||
members: [],
|
||||
@ -250,6 +249,5 @@ export function createModel (builder: Builder): void {
|
||||
}, recruit.space.VacancyTemplates)
|
||||
}
|
||||
|
||||
export { default } from './plugin'
|
||||
|
||||
export { recruitOperation } from './migration'
|
||||
export { default } from './plugin'
|
||||
|
@ -20,8 +20,13 @@ import { recruitId } from '@anticrm/recruit'
|
||||
import recruit from '@anticrm/recruit-resources/src/plugin'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import type { Action } from '@anticrm/view'
|
||||
import { Application } from '@anticrm/workbench'
|
||||
import '@anticrm/task'
|
||||
|
||||
export default mergeIds(recruitId, recruit, {
|
||||
app: {
|
||||
Recruit: '' as Ref<Application>
|
||||
},
|
||||
action: {
|
||||
CreateApplication: '' as Ref<Action>
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@anticrm/model-server-view",
|
||||
"name": "@anticrm/model-server-task",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
@ -28,7 +28,7 @@
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/model": "~0.6.0",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/server-view": "~0.6.0",
|
||||
"@anticrm/server-task": "~0.6.0",
|
||||
"@anticrm/server-core": "~0.6.0"
|
||||
}
|
||||
}
|
@ -18,10 +18,10 @@ import { Builder } from '@anticrm/model'
|
||||
|
||||
import serverCore from '@anticrm/server-core'
|
||||
import core from '@anticrm/core'
|
||||
import serverView from '@anticrm/server-view'
|
||||
import serverTask from '@anticrm/server-task'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverView.trigger.OnDocWithState
|
||||
trigger: serverTask.trigger.OnTask
|
||||
})
|
||||
}
|
@ -19,27 +19,77 @@ import type {} from '@anticrm/view'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import contact from '@anticrm/contact'
|
||||
import type { Doc, DocWithState, Domain, FindOptions, Ref } from '@anticrm/core'
|
||||
import { Builder, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import { Arr, Class, Doc, Domain, DOMAIN_MODEL, FindOptions, Ref, Space } from '@anticrm/core'
|
||||
import { Builder, Collection, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import chunter from '@anticrm/model-chunter'
|
||||
import core, { TDoc, TSpaceWithStates } from '@anticrm/model-core'
|
||||
import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@anticrm/model-core'
|
||||
import view from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { Project, Task } from '@anticrm/task'
|
||||
import type { Kanban, KanbanCard, Project, State, Issue, Sequence, DoneState, WonState, LostState, KanbanTemplateSpace, StateTemplate, DoneStateTemplate, WonStateTemplate, LostStateTemplate, KanbanTemplate, Task } from '@anticrm/task'
|
||||
import { createProjectKanban } from '@anticrm/task'
|
||||
import task from './plugin'
|
||||
import { AnyComponent } from '@anticrm/ui'
|
||||
|
||||
@Model(task.class.Project, core.class.SpaceWithStates)
|
||||
export { default } from './plugin'
|
||||
|
||||
export const DOMAIN_TASK = 'task' as Domain
|
||||
export const DOMAIN_STATE = 'state' as Domain
|
||||
export const DOMAIN_KANBAN = 'kanban' as Domain
|
||||
@Model(task.class.State, core.class.Doc, DOMAIN_STATE)
|
||||
export class TState extends TDoc implements State {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
color!: string
|
||||
}
|
||||
|
||||
@Model(task.class.DoneState, core.class.Doc, DOMAIN_STATE)
|
||||
export class TDoneState extends TDoc implements DoneState {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
}
|
||||
|
||||
@Model(task.class.WonState, task.class.DoneState, DOMAIN_STATE)
|
||||
export class TWonState extends TDoneState implements WonState {}
|
||||
|
||||
@Model(task.class.LostState, task.class.DoneState, DOMAIN_STATE)
|
||||
export class TLostState extends TDoneState implements LostState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* No domain is specified, since pure Tasks could not exists
|
||||
*/
|
||||
@Model(task.class.Task, core.class.AttachedDoc, DOMAIN_TASK)
|
||||
export class TTask extends TAttachedDoc implements Task {
|
||||
@Prop(TypeRef(task.class.State), 'State' as IntlString)
|
||||
state!: Ref<State>
|
||||
|
||||
@Prop(TypeRef(task.class.DoneState), 'Done Status' as IntlString)
|
||||
doneState!: Ref<DoneState> | null
|
||||
|
||||
@Prop(TypeString(), 'No.' as IntlString)
|
||||
number!: number
|
||||
|
||||
// @Prop(TypeRef(contact.class.Employee), 'Assignee' as IntlString)
|
||||
assignee!: Ref<Employee> | null
|
||||
}
|
||||
|
||||
@Model(task.class.SpaceWithStates, core.class.Space)
|
||||
export class TSpaceWithStates extends TSpace {
|
||||
}
|
||||
|
||||
@Model(task.class.Project, task.class.SpaceWithStates)
|
||||
@UX('Project' as IntlString, task.icon.Task)
|
||||
export class TProject extends TSpaceWithStates implements Project {}
|
||||
|
||||
@Model(task.class.Task, core.class.Doc, 'task' as Domain, [core.interface.DocWithState])
|
||||
@UX('Task' as IntlString, task.icon.Task, 'TASK' as IntlString)
|
||||
export class TTask extends TDoc implements Task {
|
||||
declare number: DocWithState['number']
|
||||
declare state: DocWithState['state']
|
||||
declare doneState: DocWithState['doneState']
|
||||
@Model(task.class.Issue, task.class.Task, DOMAIN_TASK)
|
||||
@UX('Task' as IntlString, task.icon.Task, 'Task' as IntlString)
|
||||
export class TIssue extends TTask implements Issue {
|
||||
// We need to declare, to provide property with label
|
||||
@Prop(TypeRef(core.class.Doc), 'Parent' as IntlString)
|
||||
declare attachedTo: Ref<Doc>
|
||||
|
||||
@Prop(TypeString(), 'Name' as IntlString)
|
||||
name!: string
|
||||
@ -47,9 +97,6 @@ export class TTask extends TDoc implements Task {
|
||||
@Prop(TypeString(), 'Description' as IntlString)
|
||||
description!: string
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), 'Assignee' as IntlString)
|
||||
assignee!: Ref<Employee> | null
|
||||
|
||||
@Prop(TypeString(), 'Comments' as IntlString)
|
||||
comments!: number
|
||||
|
||||
@ -58,13 +105,93 @@ export class TTask extends TDoc implements Task {
|
||||
|
||||
@Prop(TypeString(), 'Labels' as IntlString)
|
||||
labels!: string
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), 'Assignee' as IntlString)
|
||||
declare assignee: Ref<Employee> | null
|
||||
}
|
||||
|
||||
@Mixin(task.mixin.KanbanCard, core.class.Class)
|
||||
export class TKanbanCard extends TClass implements KanbanCard {
|
||||
card!: AnyComponent
|
||||
}
|
||||
|
||||
@Model(task.class.Kanban, core.class.Doc, DOMAIN_KANBAN)
|
||||
export class TKanban extends TDoc implements Kanban {
|
||||
states!: Arr<Ref<State>>
|
||||
doneStates!: Arr<Ref<DoneState>>
|
||||
attachedTo!: Ref<Space>
|
||||
order!: Arr<Ref<Doc>>
|
||||
}
|
||||
|
||||
@Model(task.class.KanbanTemplateSpace, core.class.Space, DOMAIN_MODEL)
|
||||
export class TKanbanTemplateSpace extends TSpace implements KanbanTemplateSpace {
|
||||
icon!: AnyComponent
|
||||
}
|
||||
|
||||
@Model(task.class.StateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN)
|
||||
export class TStateTemplate extends TAttachedDoc implements StateTemplate {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
@Prop(TypeString(), 'Color' as IntlString)
|
||||
color!: string
|
||||
}
|
||||
|
||||
@Model(task.class.DoneStateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN)
|
||||
export class TDoneStateTemplate extends TAttachedDoc implements DoneStateTemplate {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
}
|
||||
|
||||
@Model(task.class.WonStateTemplate, task.class.DoneStateTemplate, DOMAIN_KANBAN)
|
||||
export class TWonStateTemplate extends TDoneStateTemplate implements WonStateTemplate {}
|
||||
|
||||
@Model(task.class.LostStateTemplate, task.class.DoneStateTemplate, DOMAIN_KANBAN)
|
||||
export class TLostStateTemplate extends TDoneStateTemplate implements LostStateTemplate {}
|
||||
|
||||
@Model(task.class.KanbanTemplate, core.class.Doc, DOMAIN_KANBAN)
|
||||
export class TKanbanTemplate extends TDoc implements KanbanTemplate {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
states!: Arr<Ref<StateTemplate>>
|
||||
doneStates!: Arr<Ref<DoneStateTemplate>>
|
||||
|
||||
@Prop(Collection(task.class.StateTemplate), 'States' as IntlString)
|
||||
statesC!: number
|
||||
|
||||
@Prop(Collection(task.class.DoneStateTemplate), 'Done States' as IntlString)
|
||||
doneStatesC!: number
|
||||
}
|
||||
|
||||
@Model(task.class.Sequence, core.class.Doc, DOMAIN_KANBAN)
|
||||
export class TSequence extends TDoc implements Sequence {
|
||||
attachedTo!: Ref<Class<Doc>>
|
||||
sequence!: number
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TProject, TTask)
|
||||
builder.createModel(
|
||||
TState,
|
||||
TDoneState,
|
||||
TWonState,
|
||||
TLostState,
|
||||
TKanbanCard,
|
||||
TKanban,
|
||||
TKanbanTemplateSpace,
|
||||
TStateTemplate,
|
||||
TDoneStateTemplate,
|
||||
TWonStateTemplate,
|
||||
TLostStateTemplate,
|
||||
TKanbanTemplate,
|
||||
TSequence,
|
||||
TTask,
|
||||
TSpaceWithStates,
|
||||
TProject,
|
||||
TIssue)
|
||||
builder.mixin(task.class.Project, core.class.Class, workbench.mixin.SpaceView, {
|
||||
view: {
|
||||
class: task.class.Task,
|
||||
class: task.class.Issue,
|
||||
createItemDialog: task.component.CreateTask
|
||||
}
|
||||
})
|
||||
@ -86,7 +213,7 @@ export function createModel (builder: Builder): void {
|
||||
}, task.app.Tasks)
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: task.class.Task,
|
||||
attachTo: task.class.Issue,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: task.component.EditTask,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
@ -105,34 +232,38 @@ export function createModel (builder: Builder): void {
|
||||
]
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Task, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(task.class.Issue, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: task.component.TaskPresenter
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Task, core.class.Class, view.mixin.ObjectEditor, {
|
||||
builder.mixin(task.class.Issue, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: task.component.EditTask
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Sequence, view.space.Sequence, {
|
||||
attachedTo: task.class.Task,
|
||||
builder.createDoc(task.class.Sequence, task.space.Sequence, {
|
||||
attachedTo: task.class.Issue,
|
||||
sequence: 0
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: task.class.Task,
|
||||
descriptor: view.viewlet.Kanban,
|
||||
attachTo: task.class.Issue,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
open: task.component.EditTask,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
state: core.class.State
|
||||
state: task.class.State
|
||||
// attachedTo: core.class.Doc
|
||||
}
|
||||
} as FindOptions<Doc>, // TODO: fix
|
||||
config: ['$lookup.attachedTo', '$lookup.state']
|
||||
config: [
|
||||
// '$lookup.attachedTo',
|
||||
'$lookup.state',
|
||||
'$lookup.assignee']
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Task, core.class.Class, view.mixin.KanbanCard, {
|
||||
builder.mixin(task.class.Issue, core.class.Class, task.mixin.KanbanCard, {
|
||||
card: task.component.KanbanCard
|
||||
})
|
||||
|
||||
@ -143,7 +274,7 @@ export function createModel (builder: Builder): void {
|
||||
members: []
|
||||
}, task.space.TasksPublic)
|
||||
|
||||
builder.createDoc(view.class.KanbanTemplateSpace, core.space.Model, {
|
||||
builder.createDoc(task.class.KanbanTemplateSpace, core.space.Model, {
|
||||
name: 'Projects',
|
||||
description: 'Manage project statuses',
|
||||
members: [],
|
||||
@ -155,6 +286,44 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(_class, space, data, id)
|
||||
return await Promise.resolve()
|
||||
}).catch((err) => console.error(err))
|
||||
|
||||
builder.createDoc(view.class.Action, core.space.Model, {
|
||||
label: 'Create task' as IntlString,
|
||||
icon: task.icon.Task,
|
||||
action: task.actionImpl.CreateTask
|
||||
}, task.action.CreateTask)
|
||||
|
||||
builder.createDoc(view.class.Action, core.space.Model, {
|
||||
label: 'Edit Statuses' as IntlString,
|
||||
icon: view.icon.MoreH,
|
||||
action: task.actionImpl.EditStatuses
|
||||
}, task.action.EditStatuses)
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: task.class.SpaceWithStates,
|
||||
action: task.action.EditStatuses
|
||||
})
|
||||
|
||||
builder.mixin(task.class.State, core.class.Class, view.mixin.AttributeEditor, {
|
||||
editor: task.component.StateEditor
|
||||
})
|
||||
|
||||
builder.mixin(task.class.State, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: task.component.StatePresenter
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.ViewletDescriptor, core.space.Model, {
|
||||
label: 'Kanban' as IntlString,
|
||||
icon: task.icon.Kanban,
|
||||
component: task.component.KanbanView
|
||||
}, task.viewlet.Kanban)
|
||||
|
||||
builder.createDoc(core.class.Space, core.space.Model, {
|
||||
name: 'Sequences',
|
||||
description: 'Internal space to store sequence numbers',
|
||||
members: [],
|
||||
private: false
|
||||
}, task.space.Sequence)
|
||||
}
|
||||
|
||||
export { taskOperation } from './migration'
|
||||
|
@ -13,32 +13,64 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Doc, TxOperations } from '@anticrm/core'
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||
import { Class, Doc, Domain, DOMAIN_TX, Ref, TxCUD, TxOperations } from '@anticrm/core'
|
||||
import { MigrateOperation, MigrateUpdate, MigrationClient, MigrationResult, MigrationUpgradeClient } from '@anticrm/model'
|
||||
import core from '@anticrm/model-core'
|
||||
import view from '@anticrm/model-view'
|
||||
import { createProjectKanban } from '@anticrm/task'
|
||||
import { DOMAIN_TASK, DOMAIN_STATE, DOMAIN_KANBAN } from '.'
|
||||
import task from './plugin'
|
||||
|
||||
function logInfo (msg: string, result: MigrationResult): void {
|
||||
if (result.updated > 0) {
|
||||
console.log(`Tasks: Migrate ${msg} ${result.updated}`)
|
||||
}
|
||||
}
|
||||
async function migrateClass<T extends Doc> (client: MigrationClient, domain: Domain, from: Ref<Class<Doc>>, to: Ref<Class<T>>, extraOps: MigrateUpdate<T> = {}, txExtraOps: MigrateUpdate<TxCUD<Doc>> = {}): Promise<void> {
|
||||
logInfo(`Migrate ${from} => ${to}: `,
|
||||
await client.update<Doc>(domain, { _class: from }, { ...extraOps, _class: to }))
|
||||
logInfo(`Migrate ${from} => ${to} Transactions`,
|
||||
await client.update<TxCUD<Doc>>(DOMAIN_TX, { objectClass: from }, { ...txExtraOps, objectClass: to }))
|
||||
}
|
||||
|
||||
export const taskOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
// Since we should not have Task class instances, we convert them all to Issue.
|
||||
await migrateClass(client, DOMAIN_TASK, task.class.Task, task.class.Issue)
|
||||
await migrateClass(client, DOMAIN_STATE, 'core:class:State' as Ref<Class<Doc>>, task.class.State)
|
||||
await migrateClass(client, DOMAIN_KANBAN, 'view:class:Kanban' as Ref<Class<Doc>>, task.class.Kanban)
|
||||
await migrateClass(client, DOMAIN_KANBAN, 'view:class:Sequence' as Ref<Class<Doc>>, task.class.Sequence, { space: task.space.Sequence }, { objectSpace: task.space.Sequence })
|
||||
|
||||
// Update attached to for task
|
||||
await client.update(DOMAIN_KANBAN, { _class: task.class.Sequence, attachedTo: task.class.Task }, { attachedTo: task.class.Issue })
|
||||
|
||||
await migrateClass(client, DOMAIN_KANBAN, 'view:class:KanbanTemplate' as Ref<Class<Doc>>, task.class.KanbanTemplate)
|
||||
await migrateClass(client, DOMAIN_KANBAN, 'view:class:StateTemplate' as Ref<Class<Doc>>, task.class.StateTemplate)
|
||||
await migrateClass(client, DOMAIN_KANBAN, 'view:class:DoneStateTemplate' as Ref<Class<Doc>>, task.class.DoneStateTemplate)
|
||||
await migrateClass(client, DOMAIN_KANBAN, 'view:class:LostStateTemplate' as Ref<Class<Doc>>, task.class.LostStateTemplate)
|
||||
|
||||
await client.move('recruit' as Domain, {
|
||||
_class: 'recruit:class:Applicant' as Ref<Class<Doc>>
|
||||
}, DOMAIN_TASK)
|
||||
|
||||
await client.move('lead' as Domain, {
|
||||
_class: 'lead:class:Lead' as Ref<Class<Doc>>
|
||||
}, DOMAIN_TASK)
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
console.log('Task: Performing model upgrades')
|
||||
|
||||
const ops = new TxOperations(client, core.account.System)
|
||||
if (await client.findOne(view.class.Sequence, { attachedTo: task.class.Task }) === undefined) {
|
||||
if (await client.findOne(task.class.Sequence, { attachedTo: task.class.Issue }) === undefined) {
|
||||
console.info('Create sequence for default task project.')
|
||||
// We need to create sequence
|
||||
await ops.createDoc(view.class.Sequence, view.space.Sequence, {
|
||||
attachedTo: task.class.Task,
|
||||
await ops.createDoc(task.class.Sequence, task.space.Sequence, {
|
||||
attachedTo: task.class.Issue,
|
||||
sequence: 0
|
||||
})
|
||||
} else {
|
||||
console.log('Task: => sequence is ok')
|
||||
}
|
||||
if (await client.findOne(view.class.Kanban, { attachedTo: task.space.TasksPublic }) === undefined) {
|
||||
if (await client.findOne(task.class.Kanban, { attachedTo: task.space.TasksPublic }) === undefined) {
|
||||
console.info('Create kanban for default task project.')
|
||||
await createProjectKanban(task.space.TasksPublic, async (_class, space, data, id) => {
|
||||
const doc = await ops.findOne<Doc>(_class, { _id: id })
|
||||
@ -52,6 +84,33 @@ export const taskOperation: MigrateOperation = {
|
||||
console.log('Task: => public project Kanban is ok')
|
||||
}
|
||||
|
||||
console.log('View: Performing model upgrades')
|
||||
|
||||
const kanbans = (await client.findAll(task.class.Kanban, {}))
|
||||
.filter((kanban) => kanban.doneStates == null)
|
||||
|
||||
await Promise.all(
|
||||
kanbans
|
||||
.map(async (kanban) => {
|
||||
console.log(`Updating kanban: ${kanban._id}`)
|
||||
try {
|
||||
const doneStates = await Promise.all([
|
||||
ops.createDoc(task.class.WonState, kanban.space, {
|
||||
title: 'Won'
|
||||
}),
|
||||
ops.createDoc(task.class.LostState, kanban.space, {
|
||||
title: 'Lost'
|
||||
})
|
||||
])
|
||||
|
||||
await ops.updateDoc(kanban._class, kanban.space, kanban._id, {
|
||||
doneStates
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}))
|
||||
|
||||
const outdatedTasks = (await client.findAll(task.class.Task, {}))
|
||||
.filter((x) => x.doneState === undefined)
|
||||
|
||||
|
@ -14,17 +14,26 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Ref, Space } from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { Doc, Ref, Space } from '@anticrm/core'
|
||||
import type { IntlString, Resource } from '@anticrm/platform'
|
||||
import { mergeIds } from '@anticrm/platform'
|
||||
import task, { taskId } from '@anticrm/task'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import { Application } from '@anticrm/workbench'
|
||||
import type { Action } from '@anticrm/view'
|
||||
|
||||
export default mergeIds(taskId, task, {
|
||||
app: {
|
||||
Tasks: '' as Ref<Application>
|
||||
},
|
||||
action: {
|
||||
CreateTask: '' as Ref<Action>,
|
||||
EditStatuses: '' as Ref<Action>
|
||||
},
|
||||
actionImpl: {
|
||||
CreateTask: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
EditStatuses: '' as Resource<(object: Doc) => Promise<void>>
|
||||
},
|
||||
component: {
|
||||
ProjectView: '' as AnyComponent,
|
||||
CreateProject: '' as AnyComponent,
|
||||
@ -32,7 +41,10 @@ export default mergeIds(taskId, task, {
|
||||
EditTask: '' as AnyComponent,
|
||||
TaskPresenter: '' as AnyComponent,
|
||||
KanbanCard: '' as AnyComponent,
|
||||
TemplatesIcon: '' as AnyComponent
|
||||
TemplatesIcon: '' as AnyComponent,
|
||||
StatePresenter: '' as AnyComponent,
|
||||
StateEditor: '' as AnyComponent,
|
||||
KanbanView: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Task: '' as IntlString,
|
||||
|
@ -13,36 +13,15 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { IntlString, Asset, Resource } from '@anticrm/platform'
|
||||
import type { Ref, Class, Space, Doc, Arr, Domain, State, DoneState } from '@anticrm/core'
|
||||
import type { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { DOMAIN_MODEL } from '@anticrm/core'
|
||||
import { Model, Mixin, Builder, Prop, Collection, TypeString } from '@anticrm/model'
|
||||
import { Builder, Mixin, Model } from '@anticrm/model'
|
||||
import core, { TClass, TDoc } from '@anticrm/model-core'
|
||||
import type { Asset, IntlString, Resource } from '@anticrm/platform'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import type {
|
||||
ViewletDescriptor,
|
||||
Viewlet,
|
||||
AttributeEditor,
|
||||
AttributePresenter,
|
||||
KanbanCard,
|
||||
ObjectEditor,
|
||||
Action,
|
||||
ActionTarget,
|
||||
Kanban,
|
||||
Sequence,
|
||||
KanbanTemplateSpace,
|
||||
KanbanTemplate,
|
||||
StateTemplate,
|
||||
DoneStateTemplate,
|
||||
WonStateTemplate,
|
||||
LostStateTemplate
|
||||
} from '@anticrm/view'
|
||||
|
||||
import core, { TDoc, TClass, TSpace, TAttachedDoc } from '@anticrm/model-core'
|
||||
|
||||
import type { Action, ActionTarget, AttributeEditor, AttributePresenter, ObjectEditor, Viewlet, ViewletDescriptor } from '@anticrm/view'
|
||||
import view from './plugin'
|
||||
|
||||
const DOMAIN_KANBAN = 'kanban' as Domain
|
||||
|
||||
@Mixin(view.mixin.AttributeEditor, core.class.Class)
|
||||
export class TAttributeEditor extends TClass implements AttributeEditor {
|
||||
editor!: AnyComponent
|
||||
@ -53,11 +32,6 @@ export class TAttributePresenter extends TClass implements AttributePresenter {
|
||||
presenter!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.KanbanCard, core.class.Class)
|
||||
export class TKanbanCard extends TClass implements KanbanCard {
|
||||
card!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ObjectEditor, core.class.Class)
|
||||
export class TObjectEditor extends TClass implements ObjectEditor {
|
||||
editor!: AnyComponent
|
||||
@ -90,80 +64,8 @@ export class TActionTarget extends TDoc implements ActionTarget {
|
||||
action!: Ref<Action>
|
||||
}
|
||||
|
||||
@Model(view.class.Kanban, core.class.Doc, DOMAIN_KANBAN)
|
||||
export class TKanban extends TDoc implements Kanban {
|
||||
states!: Arr<Ref<State>>
|
||||
doneStates!: Arr<Ref<DoneState>>
|
||||
attachedTo!: Ref<Space>
|
||||
order!: Arr<Ref<Doc>>
|
||||
}
|
||||
|
||||
@Model(view.class.KanbanTemplateSpace, core.class.Space, DOMAIN_MODEL)
|
||||
export class TKanbanTemplateSpace extends TSpace implements KanbanTemplateSpace {
|
||||
icon!: AnyComponent
|
||||
}
|
||||
|
||||
@Model(view.class.StateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN)
|
||||
export class TStateTemplate extends TAttachedDoc implements StateTemplate {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
@Prop(TypeString(), 'Color' as IntlString)
|
||||
color!: string
|
||||
}
|
||||
|
||||
@Model(view.class.DoneStateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN)
|
||||
export class TDoneStateTemplate extends TAttachedDoc implements DoneStateTemplate {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
}
|
||||
|
||||
@Model(view.class.WonStateTemplate, view.class.DoneStateTemplate, DOMAIN_KANBAN)
|
||||
export class TWonStateTemplate extends TDoneStateTemplate implements WonStateTemplate {}
|
||||
|
||||
@Model(view.class.LostStateTemplate, view.class.DoneStateTemplate, DOMAIN_KANBAN)
|
||||
export class TLostStateTemplate extends TDoneStateTemplate implements LostStateTemplate {}
|
||||
|
||||
@Model(view.class.KanbanTemplate, core.class.Doc, DOMAIN_KANBAN)
|
||||
export class TKanbanTemplate extends TDoc implements KanbanTemplate {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
states!: Arr<Ref<StateTemplate>>
|
||||
doneStates!: Arr<Ref<DoneStateTemplate>>
|
||||
|
||||
@Prop(Collection(view.class.StateTemplate), 'States' as IntlString)
|
||||
statesC!: number
|
||||
|
||||
@Prop(Collection(view.class.DoneStateTemplate), 'Done States' as IntlString)
|
||||
doneStatesC!: number
|
||||
}
|
||||
|
||||
@Model(view.class.Sequence, core.class.Doc, DOMAIN_KANBAN)
|
||||
export class TSequence extends TDoc implements Sequence {
|
||||
attachedTo!: Ref<Class<Doc>>
|
||||
sequence!: number
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
TAttributeEditor,
|
||||
TAttributePresenter,
|
||||
TKanbanCard,
|
||||
TObjectEditor,
|
||||
TViewletDescriptor,
|
||||
TViewlet,
|
||||
TAction,
|
||||
TActionTarget,
|
||||
TKanban,
|
||||
TSequence,
|
||||
TKanbanTemplateSpace,
|
||||
TStateTemplate,
|
||||
TDoneStateTemplate,
|
||||
TWonStateTemplate,
|
||||
TLostStateTemplate,
|
||||
TKanbanTemplate
|
||||
)
|
||||
builder.createModel(TAttributeEditor, TAttributePresenter, TObjectEditor, TViewletDescriptor, TViewlet, TAction, TActionTarget)
|
||||
|
||||
builder.mixin(core.class.TypeString, core.class.Class, view.mixin.AttributeEditor, {
|
||||
editor: view.component.StringEditor
|
||||
@ -193,46 +95,17 @@ export function createModel (builder: Builder): void {
|
||||
editor: view.component.DateEditor
|
||||
})
|
||||
|
||||
builder.mixin(core.class.State, core.class.Class, view.mixin.AttributeEditor, {
|
||||
editor: view.component.StateEditor
|
||||
})
|
||||
builder.createDoc(view.class.ViewletDescriptor, core.space.Model, {
|
||||
label: 'Table' as IntlString,
|
||||
icon: view.icon.Table,
|
||||
component: view.component.TableView
|
||||
}, view.viewlet.Table)
|
||||
|
||||
builder.mixin(core.class.State, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: view.component.StatePresenter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Table' as IntlString,
|
||||
icon: view.icon.Table,
|
||||
component: view.component.TableView
|
||||
},
|
||||
view.viewlet.Table
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Kanban' as IntlString,
|
||||
icon: view.icon.Kanban,
|
||||
component: view.component.KanbanView
|
||||
},
|
||||
view.viewlet.Kanban
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Action,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Delete' as IntlString,
|
||||
icon: view.icon.Delete,
|
||||
action: view.actionImpl.Delete
|
||||
},
|
||||
view.action.Delete
|
||||
)
|
||||
builder.createDoc(view.class.Action, core.space.Model, {
|
||||
label: 'Delete' as IntlString,
|
||||
icon: view.icon.Delete,
|
||||
action: view.actionImpl.Delete
|
||||
}, view.action.Delete)
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: core.class.Doc,
|
||||
@ -249,13 +122,6 @@ export function createModel (builder: Builder): void {
|
||||
target: core.class.Doc,
|
||||
action: view.action.Move
|
||||
})
|
||||
|
||||
builder.createDoc(core.class.Space, core.space.Model, {
|
||||
name: 'Sequences',
|
||||
description: 'Internal space to store sequence numbers',
|
||||
members: [],
|
||||
private: false
|
||||
}, view.space.Sequence)
|
||||
}
|
||||
|
||||
export default view
|
||||
|
@ -13,42 +13,12 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { TxOperations } from '@anticrm/core'
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||
import core from '@anticrm/model-core'
|
||||
import view from './plugin'
|
||||
|
||||
export const viewOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
console.log('View: Performing model upgrades')
|
||||
|
||||
const ops = new TxOperations(client, core.account.System)
|
||||
const kanbans = (await client.findAll(view.class.Kanban, {}))
|
||||
.filter((kanban) => kanban.doneStates == null)
|
||||
|
||||
await Promise.all(
|
||||
kanbans
|
||||
.map(async (kanban) => {
|
||||
console.log(`Updating kanban: ${kanban._id}`)
|
||||
try {
|
||||
const doneStates = await Promise.all([
|
||||
ops.createDoc(core.class.WonState, kanban.space, {
|
||||
title: 'Won'
|
||||
}),
|
||||
ops.createDoc(core.class.LostState, kanban.space, {
|
||||
title: 'Lost'
|
||||
})
|
||||
])
|
||||
|
||||
await ops.updateDoc(kanban._class, kanban.space, kanban._id, {
|
||||
doneStates
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -33,12 +33,9 @@ export default mergeIds(viewId, view, {
|
||||
StringPresenter: '' as AnyComponent,
|
||||
BooleanPresenter: '' as AnyComponent,
|
||||
BooleanEditor: '' as AnyComponent,
|
||||
StatePresenter: '' as AnyComponent,
|
||||
StateEditor: '' as AnyComponent,
|
||||
TimestampPresenter: '' as AnyComponent,
|
||||
DateEditor: '' as AnyComponent,
|
||||
DatePresenter: '' as AnyComponent,
|
||||
TableView: '' as AnyComponent,
|
||||
KanbanView: '' as AnyComponent
|
||||
TableView: '' as AnyComponent
|
||||
}
|
||||
})
|
||||
|
@ -44,13 +44,13 @@ describe('hierarchy', () => {
|
||||
|
||||
it('isImplements', async () => {
|
||||
const hierarchy = prepare()
|
||||
let isImplements = hierarchy.isImplements(test.class.Task, core.interface.DocWithState)
|
||||
let isImplements = hierarchy.isImplements(test.class.Task, test.interface.WithState)
|
||||
expect(isImplements).toBeTruthy()
|
||||
|
||||
isImplements = hierarchy.isImplements(test.class.TaskCheckItem, core.interface.DocWithState)
|
||||
isImplements = hierarchy.isImplements(test.class.TaskCheckItem, test.interface.WithState)
|
||||
expect(isImplements).toBeTruthy()
|
||||
|
||||
const notImplements = hierarchy.isImplements(core.class.Space, core.interface.DocWithState)
|
||||
const notImplements = hierarchy.isImplements(core.class.Space, test.interface.WithState)
|
||||
expect(notImplements).not.toBeTruthy()
|
||||
})
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
import type { IntlString, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import { DocWithState } from '..'
|
||||
import type { Arr, Class, Data, Doc, Interface, Mixin, Obj, Ref } from '../classes'
|
||||
import { AttachedDoc, ClassifierKind, DOMAIN_MODEL } from '../classes'
|
||||
import core from '../component'
|
||||
@ -44,11 +43,15 @@ export interface AttachedComment extends AttachedDoc {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface Task extends Doc, DocWithState {
|
||||
export interface WithState extends Doc {
|
||||
state: number
|
||||
number: number
|
||||
}
|
||||
export interface Task extends Doc, WithState {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface TaskCheckItem extends AttachedDoc, DocWithState {
|
||||
export interface TaskCheckItem extends AttachedDoc, WithState {
|
||||
name: string
|
||||
complete: boolean
|
||||
}
|
||||
@ -63,7 +66,8 @@ export const test = plugin('test' as Plugin, {
|
||||
TestComment: '' as Ref<Class<AttachedComment>>
|
||||
},
|
||||
interface: {
|
||||
DummyDocWithState: '' as Ref<Interface<DocWithState>>
|
||||
WithState: '' as Ref<Interface<WithState>>,
|
||||
DummyWithState: '' as Ref<Interface<WithState>>
|
||||
}
|
||||
})
|
||||
|
||||
@ -82,7 +86,7 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
txes.push(createClass(core.class.Space, { label: 'Space' as IntlString, extends: core.class.Doc, kind: ClassifierKind.CLASS, domain: DOMAIN_MODEL }))
|
||||
txes.push(createClass(core.class.Account, { label: 'Account' as IntlString, extends: core.class.Doc, kind: ClassifierKind.CLASS, domain: DOMAIN_MODEL }))
|
||||
|
||||
txes.push(createInterface(core.interface.DocWithState, { label: 'DocWithState' as IntlString, extends: [], kind: ClassifierKind.INTERFACE }))
|
||||
txes.push(createInterface(test.interface.WithState, { label: 'WithState' as IntlString, extends: [], kind: ClassifierKind.INTERFACE }))
|
||||
|
||||
txes.push(createClass(core.class.Tx, { label: 'Tx' as IntlString, extends: core.class.Doc, kind: ClassifierKind.CLASS, domain: DOMAIN_TX }))
|
||||
txes.push(createClass(core.class.TxCUD, { label: 'TxCUD' as IntlString, extends: core.class.Tx, kind: ClassifierKind.CLASS, domain: DOMAIN_TX }))
|
||||
@ -93,10 +97,10 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
|
||||
txes.push(createClass(test.mixin.TestMixin, { label: 'TestMixin' as IntlString, extends: core.class.Doc, kind: ClassifierKind.MIXIN }))
|
||||
|
||||
txes.push(createInterface(test.interface.DummyDocWithState, { label: 'DummyDocWithState' as IntlString, extends: [core.interface.DocWithState], kind: ClassifierKind.INTERFACE }))
|
||||
txes.push(createInterface(test.interface.DummyWithState, { label: 'DummyWithState' as IntlString, extends: [test.interface.WithState], kind: ClassifierKind.INTERFACE }))
|
||||
txes.push(createClass(test.class.TestComment, { label: 'TestComment' as IntlString, extends: core.class.AttachedDoc, kind: ClassifierKind.CLASS }))
|
||||
txes.push(createClass(test.class.Task, { label: 'Task' as IntlString, extends: core.class.Doc, implements: [test.interface.DummyDocWithState], kind: ClassifierKind.CLASS }))
|
||||
txes.push(createClass(test.class.TaskCheckItem, { label: 'Task' as IntlString, extends: core.class.AttachedDoc, implements: [core.interface.DocWithState], kind: ClassifierKind.CLASS }))
|
||||
txes.push(createClass(test.class.Task, { label: 'Task' as IntlString, extends: core.class.Doc, implements: [test.interface.DummyWithState], kind: ClassifierKind.CLASS }))
|
||||
txes.push(createClass(test.class.TaskCheckItem, { label: 'Task' as IntlString, extends: core.class.AttachedDoc, implements: [test.interface.WithState], kind: ClassifierKind.CLASS }))
|
||||
|
||||
txes.push(
|
||||
createDoc(core.class.Space, {
|
||||
|
@ -218,45 +218,3 @@ export interface Space extends Doc {
|
||||
export interface Account extends Doc {
|
||||
email: string
|
||||
}
|
||||
|
||||
// S T A T E
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface State extends Doc {
|
||||
title: string
|
||||
color: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DoneState extends Doc {
|
||||
title: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface WonState extends DoneState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface LostState extends DoneState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DocWithState extends Doc {
|
||||
state: Ref<State>
|
||||
doneState: Ref<DoneState> | null
|
||||
number: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SpaceWithStates extends Space {
|
||||
}
|
||||
|
@ -14,8 +14,7 @@
|
||||
//
|
||||
import type { Plugin, StatusCode } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import { DoneState, LostState, WonState } from '.'
|
||||
import type { Account, AnyAttribute, AttachedDoc, Class, Doc, DocWithState, Interface, Obj, PropertyType, Ref, Space, SpaceWithStates, State, Timestamp, Type, Collection, RefTo } from './classes'
|
||||
import type { Account, AnyAttribute, AttachedDoc, Class, Doc, Interface, Obj, PropertyType, Ref, Space, Timestamp, Type, Collection, RefTo } from './classes'
|
||||
import type { Tx, TxBulkWrite, TxCollectionCUD, TxCreateDoc, TxCUD, TxMixin, TxPutBag, TxRemoveDoc, TxUpdateDoc } from './tx'
|
||||
|
||||
/**
|
||||
@ -41,12 +40,7 @@ export default plugin(coreId, {
|
||||
TxRemoveDoc: '' as Ref<Class<TxRemoveDoc<Doc>>>,
|
||||
TxPutBag: '' as Ref<Class<TxPutBag<PropertyType>>>,
|
||||
Space: '' as Ref<Class<Space>>,
|
||||
SpaceWithStates: '' as Ref<Class<SpaceWithStates>>,
|
||||
Account: '' as Ref<Class<Account>>,
|
||||
State: '' as Ref<Class<State>>,
|
||||
DoneState: '' as Ref<Class<DoneState>>,
|
||||
WonState: '' as Ref<Class<WonState>>,
|
||||
LostState: '' as Ref<Class<LostState>>,
|
||||
TypeString: '' as Ref<Class<Type<string>>>,
|
||||
TypeBoolean: '' as Ref<Class<Type<boolean>>>,
|
||||
TypeTimestamp: '' as Ref<Class<Type<Timestamp>>>,
|
||||
@ -55,9 +49,6 @@ export default plugin(coreId, {
|
||||
Collection: '' as Ref<Class<Collection<AttachedDoc>>>,
|
||||
Bag: '' as Ref<Class<Type<Record<string, PropertyType>>>>
|
||||
},
|
||||
interface: {
|
||||
DocWithState: '' as Ref<Interface<DocWithState>>
|
||||
},
|
||||
space: {
|
||||
Tx: '' as Ref<Space>,
|
||||
Model: '' as Ref<Space>
|
||||
|
@ -28,6 +28,9 @@ export interface MigrationClient {
|
||||
|
||||
// Allow to raw update documents inside domain.
|
||||
update: <T extends Doc>(domain: Domain, query: DocumentQuery<T>, operations: MigrateUpdate<T>) => Promise<MigrationResult>
|
||||
|
||||
// Move documents per domain
|
||||
move: <T extends Doc>(sourceDomain: Domain, query: DocumentQuery<T>, targetDomain: Domain) => Promise<MigrationResult>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,7 +56,7 @@ export class PlatformError<P extends Record<string, any>> extends Error {
|
||||
readonly status: Status<P>
|
||||
|
||||
constructor (status: Status<P>) {
|
||||
super(`${status.severity}: ${status.code}`)
|
||||
super(`${status.severity}: ${status.code} ${JSON.stringify(status.params)}`)
|
||||
this.status = status
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
</Tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<ActionIcon direction={undefined} size='small' icon={view.icon.Kanban} label={toIntl('Perform Class Query')} action={() => {
|
||||
<ActionIcon direction={undefined} size='small' icon={view.icon.MoreH} label={toIntl('Perform Class Query')} action={() => {
|
||||
}}/>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -39,6 +39,8 @@
|
||||
"@anticrm/panel": "~0.6.0",
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"@anticrm/task-resources": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/attachment-resources": "~0.6.0",
|
||||
"@anticrm/contact-resources": "~0.6.0",
|
||||
|
@ -19,8 +19,8 @@
|
||||
import { EditBox, Grid, IconFolder, ToggleWithLabel } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import lead from '../plugin'
|
||||
import view, { createKanban, KanbanTemplate } from '@anticrm/view'
|
||||
import { KanbanTemplateSelector } from '@anticrm/view-resources'
|
||||
import task, { createKanban, KanbanTemplate } from '@anticrm/task'
|
||||
import { KanbanTemplateSelector } from '@anticrm/task-resources'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
const client = getClient()
|
||||
|
||||
async function createFunnel (): Promise<void> {
|
||||
if (templateId !== undefined && await client.findOne(view.class.KanbanTemplate, { _id: templateId }) === undefined) {
|
||||
if (templateId !== undefined && await client.findOne(task.class.KanbanTemplate, { _id: templateId }) === undefined) {
|
||||
throw Error(`Failed to find target kanban template: ${templateId}`)
|
||||
}
|
||||
|
||||
|
@ -15,15 +15,15 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Contact } from '@anticrm/contact'
|
||||
import core, { Data, Ref, Space } from '@anticrm/core'
|
||||
import { Data, Ref, Space } from '@anticrm/core'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { OK, Status } from '@anticrm/platform'
|
||||
import { Card, getClient, UserBox } from '@anticrm/presentation'
|
||||
import type { Lead } from '@anticrm/lead'
|
||||
import { EditBox, Grid, Status as StatusControl } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import lead from '../plugin'
|
||||
import task from '@anticrm/task'
|
||||
|
||||
export let space: Ref<Space>
|
||||
|
||||
@ -43,18 +43,18 @@
|
||||
}
|
||||
|
||||
async function createLead () {
|
||||
const state = await client.findOne(core.class.State, { space: _space })
|
||||
const state = await client.findOne(task.class.State, { space: _space })
|
||||
if (state === undefined) {
|
||||
throw new Error('create application: state not found')
|
||||
}
|
||||
const sequence = await client.findOne(view.class.Sequence, { attachedTo: lead.class.Lead })
|
||||
const sequence = await client.findOne(task.class.Sequence, { attachedTo: lead.class.Lead })
|
||||
if (sequence === undefined) {
|
||||
throw new Error('sequence object not found')
|
||||
}
|
||||
|
||||
const incResult = await client.updateDoc(
|
||||
view.class.Sequence,
|
||||
view.space.Sequence,
|
||||
task.class.Sequence,
|
||||
task.space.Sequence,
|
||||
sequence._id,
|
||||
{
|
||||
$inc: { sequence: 1 }
|
||||
@ -67,7 +67,10 @@
|
||||
doneState: null,
|
||||
number: (incResult as any).object.sequence,
|
||||
title: title,
|
||||
customer: customer!
|
||||
customer: customer!,
|
||||
attachedTo: customer!,
|
||||
attachedToClass: contact.class.Contact,
|
||||
collection: 'leads'
|
||||
}
|
||||
|
||||
await client.createDoc(lead.class.Lead, _space, value, leadId)
|
||||
|
@ -29,6 +29,7 @@
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/view": "~0.6.0"
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import core, { DoneState } from '@anticrm/core'
|
||||
import view, { Kanban, KanbanTemplateSpace } from '@anticrm/view'
|
||||
import type { Class, Data, Doc, DocWithState, Ref, Space, SpaceWithStates, State } from '@anticrm/core'
|
||||
import type { Contact } from '@anticrm/contact'
|
||||
import type { Class, Data, Doc, Ref, Space } from '@anticrm/core'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import task, { DoneState, Kanban, KanbanTemplateSpace, SpaceWithStates, State, Task } from '@anticrm/task'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -29,7 +28,7 @@ export interface Funnel extends SpaceWithStates {}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Lead extends DocWithState {
|
||||
export interface Lead extends Task {
|
||||
title: string
|
||||
customer: Ref<Contact>
|
||||
|
||||
@ -76,7 +75,7 @@ export async function createKanban (
|
||||
for (const st of states) {
|
||||
const sid = (funnelId + '.state.' + st.name.toLowerCase().replace(' ', '_')) as Ref<State>
|
||||
await factory(
|
||||
core.class.State,
|
||||
task.class.State,
|
||||
funnelId,
|
||||
{
|
||||
title: st.name,
|
||||
@ -87,8 +86,8 @@ export async function createKanban (
|
||||
ids.push(sid)
|
||||
}
|
||||
const rawDoneStates = [
|
||||
{ class: core.class.WonState, title: 'Won' },
|
||||
{ class: core.class.LostState, title: 'Lost' }
|
||||
{ class: task.class.WonState, title: 'Won' },
|
||||
{ class: task.class.LostState, title: 'Lost' }
|
||||
]
|
||||
const doneStates: Array<Ref<DoneState>> = []
|
||||
for (const st of rawDoneStates) {
|
||||
@ -105,7 +104,7 @@ export async function createKanban (
|
||||
}
|
||||
|
||||
await factory(
|
||||
view.class.Kanban,
|
||||
task.class.Kanban,
|
||||
funnelId,
|
||||
{
|
||||
attachedTo: funnelId,
|
||||
@ -113,6 +112,6 @@ export async function createKanban (
|
||||
doneStates,
|
||||
order: []
|
||||
},
|
||||
(funnelId + '.kanban.') as Ref<Kanban>
|
||||
(funnelId + '.kanban') as Ref<Kanban>
|
||||
)
|
||||
}
|
||||
|
@ -49,6 +49,8 @@
|
||||
"@anticrm/chunter-resources": "~0.6.0",
|
||||
"@anticrm/setting": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0"
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"@anticrm/task-resources": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
object.attachedTo,
|
||||
object.attachedToClass,
|
||||
object.collection,
|
||||
{ employee: object.employee }
|
||||
{ assignee: object.assignee }
|
||||
)
|
||||
}
|
||||
</script>
|
||||
@ -39,7 +39,7 @@
|
||||
_class={contact.class.Employee}
|
||||
title="Assigned recruiter"
|
||||
caption="Recruiters"
|
||||
bind:value={object.employee}
|
||||
bind:value={object.assignee}
|
||||
on:change={change}
|
||||
allowDeselect
|
||||
titleDeselect={'Unassign recruiter'}
|
||||
|
@ -22,7 +22,7 @@
|
||||
import FileDuo from "./icons/FileDuo.svelte"
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
|
||||
import core from '@anticrm/core'
|
||||
import task from '@anticrm/task'
|
||||
import recruit from '../plugin'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
@ -50,8 +50,8 @@
|
||||
options={
|
||||
{
|
||||
lookup: {
|
||||
state: core.class.State,
|
||||
space: core.class.Space
|
||||
state: task.class.State,
|
||||
space: task.class.Space
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,15 +16,11 @@
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import type { Applicant, Candidate } from '@anticrm/recruit'
|
||||
import { CircleButton, showPopup, closeTooltip } from '@anticrm/ui'
|
||||
import Vacancy from './icons/Vacancy.svelte'
|
||||
import { getClient, createQuery } from '@anticrm/presentation'
|
||||
import EditApplication from './EditApplication.svelte'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
|
||||
import recruit from '@anticrm/recruit'
|
||||
import core from '@anticrm/core'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
import recruit from '@anticrm/recruit'
|
||||
import task from '@anticrm/task'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
|
||||
export let value: Candidate
|
||||
|
||||
@ -36,7 +32,7 @@
|
||||
options={
|
||||
{
|
||||
lookup: {
|
||||
state: core.class.State,
|
||||
state: task.class.State,
|
||||
space: core.class.Space
|
||||
}
|
||||
}
|
||||
|
@ -13,26 +13,21 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Ref, Space, SpaceWithStates } from '@anticrm/core'
|
||||
import { Status, OK, Severity } from '@anticrm/platform'
|
||||
import { DatePicker, EditBox, Tabs, Section, Grid, Status as StatusControl } from '@anticrm/ui'
|
||||
import { UserBox, Card, UserInfo, Avatar } from '@anticrm/presentation'
|
||||
import type { Employee, Person } from '@anticrm/contact'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
import Address from './icons/Address.svelte'
|
||||
import Attachment from './icons/Attachment.svelte'
|
||||
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
|
||||
import core from '@anticrm/core'
|
||||
import recruit from '../plugin'
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import contact from '@anticrm/contact'
|
||||
import view from '@anticrm/view'
|
||||
import type { Ref, Space } from '@anticrm/core'
|
||||
import { OK, Severity, Status } from '@anticrm/platform'
|
||||
import { Card, getClient, UserBox } from '@anticrm/presentation'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
import type { SpaceWithStates } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
import { Grid, Status as StatusControl } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let candidate: Ref<Candidate>
|
||||
export let employee: Ref<Employee>
|
||||
export let assignee: Ref<Employee>
|
||||
|
||||
export let preserveCandidate = false
|
||||
|
||||
@ -43,21 +38,21 @@
|
||||
const client = getClient()
|
||||
|
||||
export function canClose (): boolean {
|
||||
return candidate === undefined && employee === undefined
|
||||
return candidate === undefined && assignee === undefined
|
||||
}
|
||||
|
||||
async function createApplication () {
|
||||
const state = await client.findOne(core.class.State, { space: _space })
|
||||
const state = await client.findOne(task.class.State, { space: _space })
|
||||
if (state === undefined) {
|
||||
throw new Error('create application: state not found')
|
||||
}
|
||||
const sequence = await client.findOne(view.class.Sequence, { attachedTo: recruit.class.Applicant })
|
||||
const sequence = await client.findOne(task.class.Sequence, { attachedTo: recruit.class.Applicant })
|
||||
if (sequence === undefined) {
|
||||
throw new Error('sequence object not found')
|
||||
}
|
||||
const incResult = await client.updateDoc(
|
||||
view.class.Sequence,
|
||||
view.space.Sequence,
|
||||
task.class.Sequence,
|
||||
task.space.Sequence,
|
||||
sequence._id,
|
||||
{
|
||||
$inc: { sequence: 1 }
|
||||
@ -74,13 +69,13 @@
|
||||
state: state._id,
|
||||
doneState: null,
|
||||
number: incResult.object.sequence,
|
||||
employee: employee
|
||||
assignee: assignee
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function validate (candidate: Ref<Candidate>, space: Ref<Space>) {
|
||||
if (candidate == undefined) {
|
||||
if (candidate === undefined) {
|
||||
status = new Status(Severity.INFO, recruit.status.CandidateRequired, {})
|
||||
} else {
|
||||
if (space === undefined) {
|
||||
@ -120,7 +115,7 @@
|
||||
_class={contact.class.Employee}
|
||||
title="Assigned recruiter"
|
||||
caption="Recruiters"
|
||||
bind:value={employee}
|
||||
bind:value={assignee}
|
||||
allowDeselect
|
||||
titleDeselect={'Unassign recruiter'}
|
||||
/>
|
||||
|
@ -19,8 +19,8 @@
|
||||
import core, { Ref } from '@anticrm/core'
|
||||
import { EditBox, Grid, Dropdown } from '@anticrm/ui'
|
||||
import { getClient, SpaceCreateCard } from '@anticrm/presentation'
|
||||
import view, { KanbanTemplate, createKanban } from '@anticrm/view'
|
||||
import { KanbanTemplateSelector } from '@anticrm/view-resources'
|
||||
import task, { KanbanTemplate, createKanban } from '@anticrm/task'
|
||||
import { KanbanTemplateSelector } from '@anticrm/task-resources'
|
||||
|
||||
import Company from './icons/Company.svelte'
|
||||
import Vacancy from './icons/Vacancy.svelte'
|
||||
@ -40,7 +40,7 @@
|
||||
const client = getClient()
|
||||
|
||||
async function createVacancy() {
|
||||
if (templateId !== undefined && await client.findOne(view.class.KanbanTemplate, { _id: templateId }) === undefined) {
|
||||
if (templateId !== undefined && await client.findOne(task.class.KanbanTemplate, { _id: templateId }) === undefined) {
|
||||
throw Error(`Failed to find target kanban template: ${templateId}`)
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,6 @@
|
||||
<div class="attachments">
|
||||
<Attachments objectId={object._id} _class={object._class} space={object.space} />
|
||||
</div>
|
||||
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
@ -65,7 +64,7 @@
|
||||
.attachments {
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
|
||||
|
||||
.grid-cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
@ -166,7 +166,7 @@
|
||||
<div class="mt-14">
|
||||
<Applications objectId={object._id} _class={object._class} space={object.space} />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-14">
|
||||
<Attachments
|
||||
objectId={object._id}
|
||||
|
@ -1,30 +1,29 @@
|
||||
//
|
||||
// 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 { Ref } from '@anticrm/core'
|
||||
import type { IntlString, StatusCode } from '@anticrm/platform'
|
||||
import { mergeIds } from '@anticrm/platform'
|
||||
import type { IntlString, StatusCode } from '@anticrm/platform'
|
||||
import type { Ref, Class } from '@anticrm/core'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import type { Applicant, Candidate, Candidates, Vacancy } from '@anticrm/recruit'
|
||||
import recruit, { recruitId } from '@anticrm/recruit'
|
||||
import { KanbanTemplateSpace } from '@anticrm/task'
|
||||
|
||||
export default mergeIds(recruitId, recruit, {
|
||||
status: {
|
||||
ApplicationExists: '' as StatusCode,
|
||||
CandidateRequired: '' as StatusCode,
|
||||
VacancyRequired: '' as StatusCode,
|
||||
VacancyRequired: '' as StatusCode
|
||||
},
|
||||
string: {
|
||||
CreateVacancy: '' as IntlString,
|
||||
@ -41,6 +40,9 @@ export default mergeIds(recruitId, recruit, {
|
||||
NoAttachmentsForCandidate: '' as IntlString,
|
||||
|
||||
FirstName: '' as IntlString,
|
||||
LastName: '' as IntlString,
|
||||
LastName: '' as IntlString
|
||||
},
|
||||
space: {
|
||||
VacancyTemplates: '' as Ref<KanbanTemplateSpace>
|
||||
}
|
||||
})
|
||||
|
@ -30,6 +30,6 @@
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/chunter": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0"
|
||||
"@anticrm/task": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Person } from '@anticrm/contact'
|
||||
import type { Class, Ref, Space, Timestamp } from '@anticrm/core'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { Plugin, Asset } from '@anticrm/platform'
|
||||
import type { Space, SpaceWithStates, DocWithState, Ref, Class, AttachedDoc, Timestamp } from '@anticrm/core'
|
||||
import type { Employee, Person } from '@anticrm/contact'
|
||||
import type { KanbanTemplateSpace } from '@anticrm/view'
|
||||
import type { SpaceWithStates, Task } from '@anticrm/task'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -49,10 +49,9 @@ export interface Candidate extends Person {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Applicant extends DocWithState, AttachedDoc {
|
||||
export interface Applicant extends Task {
|
||||
attachments?: number
|
||||
comments?: number
|
||||
employee: Ref<Employee> | null
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,8 +72,5 @@ export default plugin(recruitId, {
|
||||
Location: '' as Asset,
|
||||
Calendar: '' as Asset,
|
||||
Create: '' as Asset
|
||||
},
|
||||
space: {
|
||||
VacancyTemplates: '' as Ref<KanbanTemplateSpace>
|
||||
}
|
||||
})
|
||||
|
@ -38,6 +38,8 @@
|
||||
"@anticrm/presentation": "~0.6.2",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"@anticrm/task-resources": "~0.6.0",
|
||||
"@anticrm/workbench": "~0.6.1"
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,14 @@
|
||||
<script lang="ts">
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Label, Component } from '@anticrm/ui'
|
||||
import view, { KanbanTemplateSpace } from '@anticrm/view'
|
||||
import task, { KanbanTemplateSpace } from '@anticrm/task'
|
||||
import setting from '@anticrm/setting'
|
||||
|
||||
|
||||
export let folder: KanbanTemplateSpace | undefined
|
||||
let folders: KanbanTemplateSpace[] = []
|
||||
const query = createQuery()
|
||||
$: query.query(view.class.KanbanTemplateSpace, {}, (result) => { folders = result })
|
||||
$: query.query(task.class.KanbanTemplateSpace, {}, (result) => { folders = result })
|
||||
|
||||
$: if (folder === undefined && folders.length > 0) {
|
||||
folder = folders[0]
|
||||
|
@ -18,8 +18,8 @@
|
||||
import type { Ref, Space, Doc, Class } from '@anticrm/core'
|
||||
import { getClient, MessageBox } from '@anticrm/presentation'
|
||||
import { Label, Icon, showPopup } from '@anticrm/ui'
|
||||
import type { KanbanTemplate, KanbanTemplateSpace, StateTemplate } from '@anticrm/view'
|
||||
import { KanbanTemplateEditor } from '@anticrm/view-resources'
|
||||
import type { KanbanTemplate, KanbanTemplateSpace, StateTemplate } from '@anticrm/task'
|
||||
import { KanbanTemplateEditor } from '@anticrm/task-resources'
|
||||
import setting from '@anticrm/setting'
|
||||
|
||||
import Folders from './Folders.svelte'
|
||||
@ -40,14 +40,14 @@
|
||||
}
|
||||
|
||||
showPopup(MessageBox, {
|
||||
label: 'Delete status',
|
||||
message: 'Do you want to delete this status?'
|
||||
}, undefined, async (result) => {
|
||||
if (result && template !== undefined) {
|
||||
await client.updateDoc(template._class, template.space, template._id, { $pull: { states: state._id }})
|
||||
await client.removeCollection(state._class, template.space, state._id, template._id, template._class, 'statesC')
|
||||
}
|
||||
})
|
||||
label: 'Delete status',
|
||||
message: 'Do you want to delete this status?'
|
||||
}, undefined, async (result) => {
|
||||
if (result && template !== undefined) {
|
||||
await client.updateDoc(template._class, template.space, template._id, { $pull: { states: state._id } })
|
||||
await client.removeCollection(state._class, template.space, state._id, template._id, template._class, 'statesC')
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import { AttributeEditor, createQuery, getClient } from '@anticrm/presentation'
|
||||
import { CircleButton, IconAdd, IconMoreH, Label, showPopup } from '@anticrm/ui'
|
||||
import view, { KanbanTemplate, KanbanTemplateSpace, LostStateTemplate, WonStateTemplate } from '@anticrm/view'
|
||||
import task, { KanbanTemplate, KanbanTemplateSpace, LostStateTemplate, WonStateTemplate } from '@anticrm/task'
|
||||
import { ContextMenu } from '@anticrm/view-resources'
|
||||
import setting from '@anticrm/setting'
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
let templateMap = new Map<Ref<KanbanTemplate>, KanbanTemplate>()
|
||||
const templatesQ = createQuery()
|
||||
$: if (folder !== undefined) {
|
||||
templatesQ.query(view.class.KanbanTemplate, { space: folder._id }, (result) => {
|
||||
templatesQ.query(task.class.KanbanTemplate, { space: folder._id }, (result) => {
|
||||
templates = result
|
||||
})
|
||||
}
|
||||
@ -51,7 +51,7 @@
|
||||
|
||||
const space = folder._id
|
||||
|
||||
const template = await client.createDoc(view.class.KanbanTemplate, space, {
|
||||
const template = await client.createDoc(task.class.KanbanTemplate, space, {
|
||||
states: [],
|
||||
doneStates: [],
|
||||
doneStatesC: 0,
|
||||
@ -62,12 +62,12 @@
|
||||
const doneStates = [
|
||||
{
|
||||
id: generateId<WonStateTemplate>(),
|
||||
class: view.class.WonStateTemplate,
|
||||
class: task.class.WonStateTemplate,
|
||||
title: 'Won'
|
||||
},
|
||||
{
|
||||
id: generateId<LostStateTemplate>(),
|
||||
class: view.class.LostStateTemplate,
|
||||
class: task.class.LostStateTemplate,
|
||||
title: 'Lost'
|
||||
}
|
||||
]
|
||||
@ -77,7 +77,7 @@
|
||||
ds.class,
|
||||
space,
|
||||
template,
|
||||
view.class.KanbanTemplate,
|
||||
task.class.KanbanTemplate,
|
||||
'doneStatesC',
|
||||
{
|
||||
title: ds.title
|
||||
@ -87,7 +87,7 @@
|
||||
}))
|
||||
|
||||
for (const ds of doneStates) {
|
||||
await client.updateDoc(view.class.KanbanTemplate, space, template, {
|
||||
await client.updateDoc(task.class.KanbanTemplate, space, template, {
|
||||
$push: {
|
||||
doneStates: ds.id
|
||||
}
|
||||
@ -108,7 +108,7 @@
|
||||
<div class="content">
|
||||
{#each templates as t (t._id)}
|
||||
<div class="item flex-between" class:selected={t._id === template?._id} on:click={() => select(t)}>
|
||||
<AttributeEditor maxWidth="20rem" _class={view.class.KanbanTemplate} object={t} key="title"/>
|
||||
<AttributeEditor maxWidth="20rem" _class={task.class.KanbanTemplate} object={t} key="title"/>
|
||||
<div class="tool hover-trans"
|
||||
on:click|stopPropagation={(ev) => {
|
||||
showPopup(ContextMenu, { object: t }, ev.target, () => {})
|
||||
|
@ -5,4 +5,11 @@
|
||||
<path d="M19.2,11.4c-0.3,0-0.6,0.3-0.6,0.6v6.6c0,0.7-0.5,1.3-1.2,1.3H4.8c-0.7,0-1.2-0.6-1.2-1.3V5.4 c0-0.7,0.5-1.3,1.2-1.3h9.9c0.3,0,0.6-0.3,0.6-0.6S15,2.9,14.7,2.9H4.8C3.5,2.9,2.4,4,2.4,5.4v13.2c0,1.4,1.1,2.5,2.4,2.5h12.6 c1.3,0,2.4-1.1,2.4-2.5V12C19.8,11.7,19.5,11.4,19.2,11.4z"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="kanban" viewBox="0 0 16 16">
|
||||
<path d="M14,0h-2.8H9.6H6.4H4.8H2C0.9,0,0,0.9,0,2v12c0,1.1,0.9,2,2,2h2.8h1.6h3.2h1.6H14c1.1,0,2-0.9,2-2V2C16,0.9,15.1,0,14,0z M2,14.8c-0.4,0-0.8-0.4-0.8-0.8V2c0-0.4,0.4-0.8,0.8-0.8h2.8v13.6H2z M6.4,14.8V1.2h3.2v13.6H6.4z M14.8,14c0,0.4-0.4,0.8-0.8,0.8 h-2.8V1.2H14c0.4,0,0.8,0.4,0.8,0.8V14z"/>
|
||||
</symbol>
|
||||
<symbol id='status' viewBox="0 0 16 16">
|
||||
<path d="M14.4,8c0,3.5-2.9,6.4-6.4,6.4c-3.5,0-6.4-2.9-6.4-6.4c0-3.5,2.9-6.4,6.4-6.4c0.1-0.4,0.3-0.8,0.5-1.2c-0.2,0-0.3,0-0.5,0 C3.8,0.4,0.4,3.8,0.4,8c0,4.2,3.4,7.6,7.6,7.6s7.6-3.4,7.6-7.6c0-0.2,0-0.3,0-0.5C15.2,7.7,14.8,7.9,14.4,8z"/>
|
||||
<circle cx="13" cy="3" r="3"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 1.3 KiB |
@ -16,6 +16,8 @@
|
||||
"NoAttachmentsForTask": "There are no attachments for this task.",
|
||||
"AssigneeRequired": "Assignee is required",
|
||||
"More": "Options",
|
||||
"TaskUnAssign": "Unassign"
|
||||
"TaskUnAssign": "Unassign",
|
||||
"NoTaskForObject": "No tasks defined",
|
||||
"Delete": "Delete"
|
||||
}
|
||||
}
|
@ -19,6 +19,8 @@ import task, {taskId} from '@anticrm/task'
|
||||
const icons = require('../assets/icons.svg')
|
||||
loadMetadata(task.icon, {
|
||||
Task: `${icons}#task`,
|
||||
Kanban: `${icons}#kanban`,
|
||||
Status: `${icons}#status`
|
||||
})
|
||||
|
||||
addStringsLoader(taskId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
|
@ -44,6 +44,7 @@
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/login": "~0.6.1",
|
||||
"@anticrm/attachment-resources": "~0.6.0",
|
||||
"@anticrm/chunter-resources": "~0.6.0"
|
||||
"@anticrm/chunter-resources": "~0.6.0",
|
||||
"@anticrm/workbench": "~0.6.1"
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,8 @@
|
||||
import { EditBox, Grid, IconFolder, ToggleWithLabel } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import task from '../plugin'
|
||||
import view, { createKanban, KanbanTemplate } from '@anticrm/view'
|
||||
import { KanbanTemplateSelector } from '@anticrm/view-resources'
|
||||
import { createKanban, KanbanTemplate } from '@anticrm/task'
|
||||
import KanbanTemplateSelector from './kanban/KanbanTemplateSelector.svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
const client = getClient()
|
||||
|
||||
async function createProject (): Promise<void> {
|
||||
if (templateId !== undefined && await client.findOne(view.class.KanbanTemplate, { _id: templateId }) === undefined) {
|
||||
if (templateId !== undefined && await client.findOne(task.class.KanbanTemplate, { _id: templateId }) === undefined) {
|
||||
throw Error(`Failed to find target kanban template: ${templateId}`)
|
||||
}
|
||||
|
||||
|
@ -14,53 +14,58 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import type { Data, Ref, Space } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import type { AttachedData, Data, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { OK, Status } from '@anticrm/platform'
|
||||
import { Card, getClient, UserBox } from '@anticrm/presentation'
|
||||
import { Task } from '@anticrm/task'
|
||||
import { Issue, State } from '@anticrm/task'
|
||||
import { EditBox, Grid, Status as StatusControl } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import task from '../plugin'
|
||||
|
||||
export let space: Ref<Space>
|
||||
export let parent: Pick<Doc, '_id' | '_class'> | undefined
|
||||
|
||||
let _space = space
|
||||
|
||||
$: _space = space
|
||||
const status: Status = OK
|
||||
|
||||
let assignee: Ref<Employee> | null = null
|
||||
|
||||
const object: Data<Task> = {
|
||||
const object: Data<Issue> = {
|
||||
name: '',
|
||||
description: '',
|
||||
assignee: null,
|
||||
number: 0
|
||||
number: 0,
|
||||
attachedTo: task.global.Task,
|
||||
attachedToClass: task.class.Issue,
|
||||
collection: 'tasks',
|
||||
state: '' as Ref<State>
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const taskId = generateId()
|
||||
const taskId: Ref<Issue> = generateId()
|
||||
|
||||
export function canClose (): boolean {
|
||||
return object.name !== ''
|
||||
}
|
||||
|
||||
async function createTask () {
|
||||
const state = await client.findOne(core.class.State, { space: _space })
|
||||
const state = await client.findOne(task.class.State, { space: _space })
|
||||
if (state === undefined) {
|
||||
throw new Error('create application: state not found')
|
||||
}
|
||||
|
||||
const sequence = await client.findOne(view.class.Sequence, { attachedTo: task.class.Task })
|
||||
const sequence = await client.findOne(task.class.Sequence, { attachedTo: task.class.Issue })
|
||||
if (sequence === undefined) {
|
||||
throw new Error('sequence object not found')
|
||||
}
|
||||
|
||||
const incResult = await client.updateDoc(
|
||||
view.class.Sequence,
|
||||
view.space.Sequence,
|
||||
task.class.Sequence,
|
||||
task.space.Sequence,
|
||||
sequence._id,
|
||||
{
|
||||
$inc: { sequence: 1 }
|
||||
@ -68,7 +73,7 @@
|
||||
true
|
||||
)
|
||||
|
||||
const value: Data<Task> = {
|
||||
const value: AttachedData<Issue> = {
|
||||
name: object.name,
|
||||
description: object.description,
|
||||
assignee,
|
||||
@ -77,8 +82,7 @@
|
||||
state: state._id
|
||||
}
|
||||
|
||||
await client.createDoc(task.class.Task, _space, value, taskId)
|
||||
dispatch('close')
|
||||
await client.addCollection(task.class.Issue, _space, parent?._id ?? task.global.Task, parent?._class ?? task.class.Issue, 'tasks', value, taskId)
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -87,7 +91,7 @@
|
||||
<Card
|
||||
label={task.string.CreateTask}
|
||||
okAction={createTask}
|
||||
canSave={object.name.length > 0}
|
||||
canSave={object.name.length > 0 && _space != null}
|
||||
spaceClass={task.class.Project}
|
||||
spaceLabel={task.string.ProjectName}
|
||||
spacePlaceholder={task.string.SelectProject}
|
||||
|
@ -16,7 +16,7 @@
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { Task } from '@anticrm/task'
|
||||
import type { Issue } from '@anticrm/task'
|
||||
import { EditBox, Grid } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@ -24,11 +24,11 @@
|
||||
import { Attachments } from '@anticrm/attachment-resources'
|
||||
import TaskHeader from './TaskHeader.svelte'
|
||||
|
||||
export let _id: Ref<Task>
|
||||
let object: Task
|
||||
export let _id: Ref<Issue>
|
||||
let object: Issue
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(task.class.Task, { _id }, (result) => {
|
||||
$: query.query(task.class.Issue, { _id }, (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
const client = getClient()
|
||||
|
||||
function change (field: string, value: any) {
|
||||
client.updateDoc(task.class.Task, object.space, object._id, { [field]: value })
|
||||
client.updateCollection(object._class, object.space, object._id, object.attachedTo, object.attachedToClass, object.collection, { [field]: value })
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -18,13 +18,13 @@
|
||||
import { formatName } from '@anticrm/contact'
|
||||
import type { WithLookup } from '@anticrm/core'
|
||||
import { Avatar } from '@anticrm/presentation'
|
||||
import type { Task } from '@anticrm/task'
|
||||
import type { Issue } from '@anticrm/task'
|
||||
import { ActionIcon, IconMoreH, Label, showPopup } from '@anticrm/ui'
|
||||
import { ContextMenu } from '@anticrm/view-resources'
|
||||
import task from '../plugin'
|
||||
import TaskPresenter from './TaskPresenter.svelte'
|
||||
|
||||
export let object: WithLookup<Task>
|
||||
export let object: WithLookup<Issue>
|
||||
export let draggable: boolean
|
||||
|
||||
const showMenu = (ev?: Event): void => {
|
||||
|
@ -16,14 +16,14 @@
|
||||
<script lang="ts">
|
||||
import contact from '@anticrm/contact'
|
||||
import { AttributeBarEditor, getClient, UserBox } from '@anticrm/presentation'
|
||||
import { Task } from '@anticrm/task'
|
||||
import { Issue } from '@anticrm/task'
|
||||
import task from '../plugin'
|
||||
|
||||
export let object: Task
|
||||
export let object: Issue
|
||||
const client = getClient()
|
||||
|
||||
function change () {
|
||||
client.updateDoc(object._class, object.space, object._id, { assignee: object.assignee })
|
||||
client.updateCollection(object._class, object.space, object._id, object.attachedTo, object.attachedToClass, object.collection, { assignee: object.assignee })
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Task } from '@anticrm/task'
|
||||
import type { Issue } from '@anticrm/task'
|
||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||
import EditTask from './EditTask.svelte'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import task from '../plugin'
|
||||
|
||||
export let value: Task
|
||||
export let value: Issue
|
||||
|
||||
const client = getClient()
|
||||
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
|
||||
|
93
plugins/task-resources/src/components/Tasks.svelte
Normal file
93
plugins/task-resources/src/components/Tasks.svelte
Normal file
@ -0,0 +1,93 @@
|
||||
<!--
|
||||
// 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.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Ref, Space, Doc, Class } from '@anticrm/core'
|
||||
import type { Issue } from '@anticrm/task'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { CircleButton, IconAdd, showPopup, Label } from '@anticrm/ui'
|
||||
import CreateTask from './CreateTask.svelte'
|
||||
// import FileDuo from "./icons/FileDuo.svelte"
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
|
||||
import core from '@anticrm/core'
|
||||
import task from '../plugin'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
export let space: Ref<Space>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
|
||||
let tasks: Issue[] = []
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(task.class.Issue, { attachedTo: objectId }, result => { tasks = result })
|
||||
|
||||
const createApp = (ev: MouseEvent): void =>
|
||||
showPopup(CreateTask, { parent: { _id: objectId, _class, space } }, ev.target as HTMLElement, () => {})
|
||||
</script>
|
||||
|
||||
<div class="applications-container">
|
||||
<div class="flex-row-center">
|
||||
<div class="title">Tasks</div>
|
||||
<CircleButton icon={IconAdd} size={'small'} on:click={createApp} />
|
||||
</div>
|
||||
{#if tasks.length > 0}
|
||||
<Table
|
||||
_class={task.class.Issue}
|
||||
config={['', '$lookup.space.name', '$lookup.state']}
|
||||
options={
|
||||
{
|
||||
lookup: {
|
||||
state: task.class.State,
|
||||
space: core.class.Space
|
||||
}
|
||||
}
|
||||
}
|
||||
query={ { attachedTo: objectId } }
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex-col-center mt-5 createapp-container">
|
||||
<!-- <FileDuo size={'large'} /> -->
|
||||
<div class="small-text content-dark-color mt-2">
|
||||
<Label label={task.string.NoTaskForObject} />
|
||||
</div>
|
||||
<div class="small-text">
|
||||
<a href={'#'} on:click={createApp}><Label label={task.string.CreateTask} /></a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.applications-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
margin-right: .75rem;
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
|
||||
.createapp-container {
|
||||
padding: 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
background: var(--theme-bg-accent-color);
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
border-radius: .75rem;
|
||||
}
|
||||
</style>
|
@ -14,12 +14,12 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Ref, State, Doc, DoneState } from '@anticrm/core'
|
||||
import type { Ref, Doc } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { Kanban } from '@anticrm/view'
|
||||
import type { Kanban, State, DoneState } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
|
||||
import core from '@anticrm/core'
|
||||
import StatesEditor from './StatesEditor.svelte'
|
||||
import StatesEditor from '../state/StatesEditor.svelte'
|
||||
|
||||
export let kanban: Kanban
|
||||
|
||||
@ -28,12 +28,12 @@
|
||||
let doneStates: DoneState[] = []
|
||||
let wonStates: DoneState[] = []
|
||||
let lostStates: DoneState[] = []
|
||||
$: wonStates = doneStates.filter((x) => x._class === core.class.WonState)
|
||||
$: lostStates = doneStates.filter((x) => x._class === core.class.LostState)
|
||||
$: wonStates = doneStates.filter((x) => x._class === task.class.WonState)
|
||||
$: lostStates = doneStates.filter((x) => x._class === task.class.LostState)
|
||||
|
||||
const client = getClient()
|
||||
|
||||
function sort <T extends Doc>(order: Ref<T>[], items: T[]): T[] {
|
||||
function sort <T extends Doc> (order: Ref<T>[], items: T[]): T[] {
|
||||
if (items.length === 0) {
|
||||
return []
|
||||
}
|
||||
@ -47,10 +47,10 @@
|
||||
}
|
||||
|
||||
const statesQ = createQuery()
|
||||
$: statesQ.query(core.class.State, { _id: { $in: kanban.states ?? [] } }, result => { states = sort(kanban.states, result) })
|
||||
$: statesQ.query(task.class.State, { _id: { $in: kanban.states ?? [] } }, result => { states = sort(kanban.states, result) })
|
||||
|
||||
const doneStatesQ = createQuery()
|
||||
$: doneStatesQ.query(core.class.DoneState, { _id: { $in: kanban.doneStates }}, (result) => { doneStates = sort(kanban.doneStates, result) })
|
||||
$: doneStatesQ.query(task.class.DoneState, { _id: { $in: kanban.doneStates } }, (result) => { doneStates = sort(kanban.doneStates, result) })
|
||||
|
||||
async function onMove ({ detail: { stateID, position } }: { detail: { stateID: Ref<State>, position: number } }) {
|
||||
client.updateDoc(kanban._class, kanban.space, kanban._id, {
|
||||
@ -64,7 +64,7 @@
|
||||
}
|
||||
|
||||
async function onAdd () {
|
||||
const state = await client.createDoc(core.class.State, kanban.space, {
|
||||
const state = await client.createDoc(task.class.State, kanban.space, {
|
||||
title: 'New State',
|
||||
color: '#7C6FCD'
|
||||
})
|
@ -15,13 +15,13 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Ref, State, Space, Doc, generateId } from '@anticrm/core'
|
||||
import { Ref, Space, Doc, generateId } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { DoneStateTemplate, KanbanTemplate, StateTemplate } from '@anticrm/view'
|
||||
import view from '@anticrm/view'
|
||||
import type { State, DoneStateTemplate, KanbanTemplate, StateTemplate } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
|
||||
import StatesEditor from './StatesEditor.svelte'
|
||||
import StatesEditor from '../state/StatesEditor.svelte'
|
||||
|
||||
export let kanban: KanbanTemplate
|
||||
|
||||
@ -29,8 +29,8 @@
|
||||
let doneStates: DoneStateTemplate[] = []
|
||||
let wonStates: DoneStateTemplate[] = []
|
||||
let lostStates: DoneStateTemplate[] = []
|
||||
$: wonStates = doneStates.filter((x) => x._class === view.class.WonStateTemplate)
|
||||
$: lostStates = doneStates.filter((x) => x._class === view.class.LostStateTemplate)
|
||||
$: wonStates = doneStates.filter((x) => x._class === task.class.WonStateTemplate)
|
||||
$: lostStates = doneStates.filter((x) => x._class === task.class.LostStateTemplate)
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@ -49,10 +49,10 @@
|
||||
}
|
||||
|
||||
const statesQ = createQuery()
|
||||
$: statesQ.query(view.class.StateTemplate, { attachedTo: kanban._id }, result => { states = sort(kanban.states, result) })
|
||||
$: statesQ.query(task.class.StateTemplate, { attachedTo: kanban._id }, result => { states = sort(kanban.states, result) })
|
||||
|
||||
const doneStatesQ = createQuery()
|
||||
$: doneStatesQ.query(view.class.DoneStateTemplate, { attachedTo: kanban._id }, (result) => { doneStates = sort(kanban.doneStates, result) })
|
||||
$: doneStatesQ.query(task.class.DoneStateTemplate, { attachedTo: kanban._id }, (result) => { doneStates = sort(kanban.doneStates, result) })
|
||||
|
||||
let space: Space | undefined
|
||||
const spaceQ = createQuery()
|
||||
@ -72,7 +72,7 @@
|
||||
async function onAdd () {
|
||||
const stateID = generateId<StateTemplate>()
|
||||
await client.addCollection(
|
||||
view.class.StateTemplate,
|
||||
task.class.StateTemplate,
|
||||
kanban.space,
|
||||
kanban._id,
|
||||
kanban._class,
|
@ -14,18 +14,18 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation';
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { DumbDropdown } from '@anticrm/ui'
|
||||
import type { DumbDropdownItem } from '@anticrm/ui/src/types';
|
||||
import type { KanbanTemplate, KanbanTemplateSpace } from '@anticrm/view'
|
||||
import view from '@anticrm/view'
|
||||
import type { DumbDropdownItem } from '@anticrm/ui/src/types'
|
||||
import type { KanbanTemplate, KanbanTemplateSpace } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
|
||||
export let folders: Ref<KanbanTemplateSpace>[]
|
||||
export let template: Ref<KanbanTemplate> | undefined = undefined
|
||||
|
||||
let templates: KanbanTemplate[] = []
|
||||
const templatesQ = createQuery()
|
||||
$: templatesQ.query(view.class.KanbanTemplate, { space: { $in: folders }}, (result) => { templates = result })
|
||||
$: templatesQ.query(task.class.KanbanTemplate, { space: { $in: folders } }, (result) => { templates = result })
|
||||
|
||||
let items: DumbDropdownItem[] = []
|
||||
$: items = [
|
@ -15,14 +15,14 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { AttachedDoc, Class, Doc, DoneState, FindOptions, LostState, Ref, SpaceWithStates, State, TxCUD, WonState } from '@anticrm/core'
|
||||
import type { AttachedDoc, Class, Doc, FindOptions, Ref } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { Kanban, SpaceWithStates, State, Task } from '@anticrm/task'
|
||||
import task, { DoneState, LostState, WonState } from '@anticrm/task'
|
||||
import type { AnySvelteComponent } from '@anticrm/ui'
|
||||
import { AnyComponent, Loading, ScrollBox } from '@anticrm/ui'
|
||||
import type { Kanban } from '@anticrm/view'
|
||||
import view from '@anticrm/view'
|
||||
import KanbanPanel from './KanbanPanel.svelte'
|
||||
import KanbanPanelEmpty from './KanbanPanelEmpty.svelte'
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
let lostState: LostState | undefined
|
||||
|
||||
const kanbanQuery = createQuery()
|
||||
$: kanbanQuery.query(view.class.Kanban, { attachedTo: space }, result => { kanban = result[0] })
|
||||
$: kanbanQuery.query(task.class.Kanban, { attachedTo: space }, result => { kanban = result[0] })
|
||||
|
||||
$: kanbanStates = kanban?.states ?? []
|
||||
$: kanbanDoneStates = kanban?.doneStates ?? []
|
||||
@ -65,14 +65,14 @@
|
||||
}
|
||||
|
||||
const statesQuery = createQuery()
|
||||
$: if (kanbanStates.length > 0) statesQuery.query(core.class.State, { _id: { $in: kanbanStates } }, result => { rawStates = result })
|
||||
$: if (kanbanStates.length > 0) statesQuery.query(task.class.State, { _id: { $in: kanbanStates } }, result => { rawStates = result })
|
||||
$: states = sort(kanban, rawStates)
|
||||
|
||||
const doneStatesQ = createQuery()
|
||||
$: if (kanbanDoneStates.length > 0) {
|
||||
doneStatesQ.query(core.class.DoneState, { _id: { $in: kanbanDoneStates }}, (result) => {
|
||||
wonState = result.find((x) => x._class === core.class.WonState)
|
||||
lostState = result.find((x) => x._class === core.class.LostState)
|
||||
doneStatesQ.query(task.class.DoneState, { _id: { $in: kanbanDoneStates } }, (result) => {
|
||||
wonState = result.find((x) => x._class === task.class.WonState)
|
||||
lostState = result.find((x) => x._class === task.class.LostState)
|
||||
})
|
||||
}
|
||||
|
||||
@ -91,7 +91,6 @@
|
||||
|
||||
async function move (state: Ref<State>) {
|
||||
const id = dragCard._id
|
||||
const txes: TxCUD<Doc>[] = []
|
||||
|
||||
if (dragCardInitialState !== state) {
|
||||
if (client.getHierarchy().isDerived(_class, core.class.AttachedDoc)) {
|
||||
@ -99,12 +98,12 @@
|
||||
// We need to update using updateCollection
|
||||
client.updateCollection(_class, space, id as Ref<Doc> as Ref<AttachedDoc>, adoc.attachedTo, adoc.attachedToClass, adoc.collection, { state })
|
||||
} else {
|
||||
client.updateDoc(_class, space, id, { state })
|
||||
client.updateDoc<Task>(_class, space, id as Ref<Task>, { state })
|
||||
}
|
||||
}
|
||||
|
||||
if (dragCardInitialPosition !== dragCardEndPosition) {
|
||||
client.updateDoc(view.class.Kanban, space, kanban._id, {
|
||||
client.updateDoc(task.class.Kanban, space, kanban._id, {
|
||||
$move: {
|
||||
order: {
|
||||
$value: id,
|
||||
@ -124,17 +123,17 @@
|
||||
|
||||
async function cardPresenter (_class: Ref<Class<Doc>>): Promise<AnySvelteComponent> {
|
||||
const clazz = client.getHierarchy().getClass(_class)
|
||||
const presenterMixin = client.getHierarchy().as(clazz, view.mixin.KanbanCard)
|
||||
const presenterMixin = client.getHierarchy().as(clazz, task.mixin.KanbanCard)
|
||||
return await getResource(presenterMixin.card)
|
||||
}
|
||||
|
||||
const onDone = (state: DoneState) => async () => {
|
||||
if (client.getHierarchy().isDerived(_class, core.class.AttachedDoc)) {
|
||||
const adoc: AttachedDoc = dragCard as Doc as AttachedDoc
|
||||
await client.updateCollection(
|
||||
await client.updateCollection<Doc, Task>(
|
||||
_class,
|
||||
space,
|
||||
adoc._id as Ref<Doc> as Ref<AttachedDoc>,
|
||||
adoc._id as Ref<Task>,
|
||||
adoc.attachedTo,
|
||||
adoc.attachedToClass,
|
||||
adoc.collection,
|
16
plugins/task-resources/src/components/state/Circles.svelte
Normal file
16
plugins/task-resources/src/components/state/Circles.svelte
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
const fill: string = 'var(--theme-caption-color)'
|
||||
</script>
|
||||
|
||||
<svg {fill} viewBox="0 0 6 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="1" cy="1" r="1"/>
|
||||
<circle cx="4.5" cy="1" r="1"/>
|
||||
<circle cx="1" cy="4.5" r="1"/>
|
||||
<circle cx="4.5" cy="4.5" r="1"/>
|
||||
<circle cx="1" cy="8" r="1"/>
|
||||
<circle cx="4.5" cy="8" r="1"/>
|
||||
<circle cx="1" cy="11.5" r="1"/>
|
||||
<circle cx="4.5" cy="11.5" r="1"/>
|
||||
<circle cx="1" cy="15" r="1"/>
|
||||
<circle cx="4.5" cy="15" r="1"/>
|
||||
</svg>
|
@ -0,0 +1,50 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import PopupDialog from './PopupDialog.svelte'
|
||||
|
||||
export let colors: string[] = ['#A5D179', '#77C07B', '#60B96E', '#45AEA3', '#46CBDE', '#47BDF6',
|
||||
'#5AADF6', '#73A6CD', '#B977CB', '#7C6FCD', '#6F7BC5', '#F28469']
|
||||
export let columns: number = 5
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<PopupDialog label={'CHOOSE A COLOR'}>
|
||||
<div class="color-grid" style="grid-template-columns: repeat({columns}, 1.5rem)">
|
||||
{#each colors as color}
|
||||
<div class="color" style="background-color: {color}" on:click={() => { dispatch('close', color) }} />
|
||||
{/each}
|
||||
</div>
|
||||
</PopupDialog>
|
||||
|
||||
<style lang="scss">
|
||||
.color-grid {
|
||||
display: grid;
|
||||
grid-auto-rows: 1.5rem;
|
||||
gap: 1rem;
|
||||
|
||||
.color {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover { border-color: var(--theme-button-border-focused); }
|
||||
}
|
||||
}
|
||||
</style>
|
@ -15,19 +15,15 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import type { Ref, SpaceWithStates, State, Class, Obj, Space } from '@anticrm/core'
|
||||
import { Label, showPopup } from '@anticrm/ui'
|
||||
import { createQuery, getClient, MessageBox } from '@anticrm/presentation'
|
||||
import type { Kanban } from '@anticrm/view'
|
||||
import { KanbanEditor } from '@anticrm/view-resources'
|
||||
import Close from './icons/Close.svelte'
|
||||
import Status from './icons/Status.svelte'
|
||||
import workbench from '../plugin'
|
||||
|
||||
import type { Class, Obj, Ref } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import view from '@anticrm/view'
|
||||
import { createQuery, getClient, MessageBox } from '@anticrm/presentation'
|
||||
import type { Kanban, SpaceWithStates, State } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
import KanbanEditor from '../kanban/KanbanEditor.svelte'
|
||||
import { Icon, IconClose, Label, showPopup } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import workbench from '@anticrm/workbench'
|
||||
|
||||
export let _id: Ref<SpaceWithStates>
|
||||
export let spaceClass: Ref<Class<Obj>>
|
||||
@ -40,7 +36,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const kanbanQ = createQuery()
|
||||
$: kanbanQ.query(view.class.Kanban, { attachedTo: _id }, result => { kanban = result[0] })
|
||||
$: kanbanQ.query(task.class.Kanban, { attachedTo: _id }, result => { kanban = result[0] })
|
||||
|
||||
const spaceQ = createQuery()
|
||||
$: spaceQ.query<Class<SpaceWithStates>>(core.class.Class, { _id: spaceClass }, result => { spaceClassInstance = result.shift() })
|
||||
@ -48,7 +44,7 @@
|
||||
const spaceI = createQuery()
|
||||
$: spaceI.query<SpaceWithStates>(spaceClass, { _id: _id }, result => { spaceInstance = result.shift() })
|
||||
|
||||
async function deleteState({ state }: { state: State }) {
|
||||
async function deleteState ({ state }: { state: State }) {
|
||||
if (spaceInstance === undefined) {
|
||||
return
|
||||
}
|
||||
@ -82,12 +78,12 @@
|
||||
<div class="flex-between header">
|
||||
<div class="flex-grow flex-col">
|
||||
<div class="flex-row-center">
|
||||
<div class="icon"><Status size={'small'} /></div>
|
||||
<div class="icon"><Icon icon={task.icon.Status} size={'small'} /></div>
|
||||
<span class="overflow-label title">Manage application statuses within <Label label={spaceClassInstance?.label}/></span>
|
||||
</div>
|
||||
<div class="overflow-label subtitle">{spaceInstance?.name}</div>
|
||||
</div>
|
||||
<div class="tool" on:click={() => dispatch('close')}><Close size={'small'} /></div>
|
||||
<div class="tool" on:click={() => dispatch('close')}><IconClose size={'small'} /></div>
|
||||
</div>
|
||||
<div class="flex-grow flex-col content">
|
||||
{#if kanban !== undefined}
|
@ -0,0 +1,66 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { Label } from '@anticrm/ui'
|
||||
|
||||
export let label: IntlString
|
||||
</script>
|
||||
|
||||
<div class="card-container">
|
||||
<div class="card-bg" />
|
||||
<div class="overflow-label label"><Label {label} /></div>
|
||||
<div class="content"><slot /></div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.card-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 32px 1.5rem 1.25rem;
|
||||
border-radius: .75rem;
|
||||
|
||||
.label {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 1.25rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: .75rem;
|
||||
color: var(--theme-content-trans-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.card-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: var(--theme-card-bg);
|
||||
border-radius: .75rem;
|
||||
backdrop-filter: blur(24px);
|
||||
box-shadow: var(--theme-card-shadow);
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -15,7 +15,8 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import core, { Ref, State } from '@anticrm/core'
|
||||
import { Ref } from '@anticrm/core'
|
||||
import task, { State } from '@anticrm/task'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
import StatePresenter from './StatePresenter.svelte'
|
||||
@ -28,7 +29,7 @@
|
||||
let opened: boolean = false
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(core.class.State, { _id: value }, (res) => {
|
||||
$: query.query(task.class.State, { _id: value }, (res) => {
|
||||
state = res[0]
|
||||
}, { limit: 1 })
|
||||
</script>
|
||||
@ -39,7 +40,7 @@
|
||||
if (!opened) {
|
||||
opened = true
|
||||
showPopup(StatesPopup, { space: state.space }, container, (result) => {
|
||||
if (result) {
|
||||
if (result) {
|
||||
value = result._id
|
||||
onChange(value)
|
||||
}
|
@ -14,16 +14,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { AttributeEditor, getClient } from '@anticrm/presentation'
|
||||
import type { DoneState, State } from '@anticrm/task'
|
||||
import { CircleButton, IconAdd, IconMoreH, Label, showPopup } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Ref, State, DoneState } from '@anticrm/core'
|
||||
import { CircleButton, IconAdd, Label, IconMoreH, showPopup } from '@anticrm/ui'
|
||||
import { getClient, AttributeEditor } from '@anticrm/presentation'
|
||||
import Circles from './icons/Circles.svelte'
|
||||
import ColorsPopup from './ColorsPopup.svelte'
|
||||
import Circles from './Circles.svelte'
|
||||
import StatusesPopup from './StatusesPopup.svelte'
|
||||
|
||||
import core from '@anticrm/core'
|
||||
|
||||
export let states: State[] = []
|
||||
export let wonStates: DoneState[] = []
|
||||
export let lostStates: DoneState[] = []
|
@ -15,15 +15,16 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import core, { Ref, SpaceWithStates, State } from "@anticrm/core"
|
||||
import { createQuery } from "@anticrm/presentation"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import task, { SpaceWithStates, State } from '@anticrm/task'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let space: Ref<SpaceWithStates>
|
||||
let states: State[] = []
|
||||
const dispatch = createEventDispatcher()
|
||||
const statesQuery = createQuery()
|
||||
statesQuery.query(core.class.State, { space }, (res) => states = res)
|
||||
statesQuery.query(task.class.State, { space }, (res) => { states = res })
|
||||
|
||||
</script>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Label, IconDelete as Delete } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import task from '../../plugin'
|
||||
|
||||
export let onDelete: () => void
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<div class="icon">
|
||||
<Delete size={'medium'} />
|
||||
</div>
|
||||
<div class="flex-grow"><Label label={view.string.Delete} /></div>
|
||||
<div class="flex-grow"><Label label={task.string.Delete} /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -21,6 +21,27 @@ import CreateProject from './components/CreateProject.svelte'
|
||||
import TaskPresenter from './components/TaskPresenter.svelte'
|
||||
import KanbanCard from './components/KanbanCard.svelte'
|
||||
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import { Doc } from '@anticrm/core'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
|
||||
import KanbanView from './components/kanban/KanbanView.svelte'
|
||||
import StateEditor from './components/state/StateEditor.svelte'
|
||||
import StatePresenter from './components/state/StatePresenter.svelte'
|
||||
import EditStatuses from './components/state/EditStatuses.svelte'
|
||||
import { SpaceWithStates } from '@anticrm/task'
|
||||
|
||||
export { default as KanbanTemplateEditor } from './components/kanban/KanbanTemplateEditor.svelte'
|
||||
export { default as KanbanTemplateSelector } from './components/kanban/KanbanTemplateSelector.svelte'
|
||||
|
||||
export { default as Tasks } from './components/Tasks.svelte'
|
||||
|
||||
async function createTask (object: Doc): Promise<void> {
|
||||
showPopup(CreateTask, { parent: object._id, space: object.space })
|
||||
}
|
||||
|
||||
async function editStatuses (object: SpaceWithStates): Promise<void> {
|
||||
showPopup(EditStatuses, { _id: object._id, spaceClass: object._class }, 'right')
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
@ -28,6 +49,13 @@ export default async (): Promise<Resources> => ({
|
||||
CreateProject,
|
||||
TaskPresenter,
|
||||
KanbanCard,
|
||||
TemplatesIcon
|
||||
TemplatesIcon,
|
||||
KanbanView,
|
||||
StatePresenter,
|
||||
StateEditor
|
||||
},
|
||||
actionImpl: {
|
||||
CreateTask: createTask,
|
||||
EditStatuses: editStatuses
|
||||
}
|
||||
})
|
||||
|
@ -33,7 +33,10 @@ export default mergeIds(taskId, task, {
|
||||
TaskUnAssign: '' as IntlString,
|
||||
TaskDescription: '' as IntlString,
|
||||
NoAttachmentsForTask: '' as IntlString,
|
||||
More: '' as IntlString
|
||||
More: '' as IntlString,
|
||||
UploadDropFilesHere: '' as IntlString,
|
||||
NoTaskForObject: '' as IntlString,
|
||||
Delete: '' as IntlString
|
||||
},
|
||||
status: {
|
||||
AssigneeRequired: '' as IntlString
|
||||
|
@ -29,6 +29,7 @@
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/view": "~0.6.0"
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -14,49 +14,186 @@
|
||||
//
|
||||
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import type { Class, Data, Doc, DocWithState, DoneState, Ref, Space, State } from '@anticrm/core'
|
||||
import type { AttachedDoc, Class, Client, Data, Doc, Mixin, Ref, Space, TxOperations } from '@anticrm/core'
|
||||
import { Arr } from '@anticrm/core'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import core from '@anticrm/core'
|
||||
import view, { Kanban, KanbanTemplateSpace } from '@anticrm/view'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import { ViewletDescriptor } from '@anticrm/view'
|
||||
|
||||
// S T A T E
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Project extends Space {}
|
||||
export interface State extends Doc {
|
||||
title: string
|
||||
color: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Task extends DocWithState {
|
||||
export interface DoneState extends Doc {
|
||||
title: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface WonState extends DoneState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface LostState extends DoneState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Task extends AttachedDoc {
|
||||
state: Ref<State>
|
||||
doneState: Ref<DoneState> | null
|
||||
number: number
|
||||
assignee: Ref<Employee> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SpaceWithStates extends Space {
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Project extends SpaceWithStates {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Issue extends Task {
|
||||
number: number // Sequence number
|
||||
|
||||
name: string
|
||||
description: string
|
||||
assignee: Ref<Employee> | null
|
||||
|
||||
comments?: number
|
||||
attachments?: number
|
||||
labels?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KanbanCard extends Class<Doc> {
|
||||
card: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Kanban extends Doc {
|
||||
attachedTo: Ref<Space>
|
||||
states: Arr<Ref<State>>
|
||||
doneStates: Arr<Ref<DoneState>>
|
||||
order: Arr<Ref<Doc>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Sequence extends Doc {
|
||||
attachedTo: Ref<Class<Doc>>
|
||||
sequence: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface StateTemplate extends AttachedDoc, State {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DoneStateTemplate extends AttachedDoc, DoneState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface WonStateTemplate extends DoneStateTemplate, WonState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface LostStateTemplate extends DoneStateTemplate, LostState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KanbanTemplate extends Doc {
|
||||
title: string
|
||||
states: Arr<Ref<StateTemplate>>
|
||||
doneStates: Arr<Ref<DoneStateTemplate>>
|
||||
statesC: number
|
||||
doneStatesC: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KanbanTemplateSpace extends Space {
|
||||
icon: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const taskId = 'task' as Plugin
|
||||
|
||||
export default plugin(taskId, {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const task = plugin(taskId, {
|
||||
mixin: {
|
||||
KanbanCard: '' as Ref<Mixin<KanbanCard>>
|
||||
},
|
||||
class: {
|
||||
Issue: '' as Ref<Class<Issue>>,
|
||||
Project: '' as Ref<Class<Project>>,
|
||||
State: '' as Ref<Class<State>>,
|
||||
DoneState: '' as Ref<Class<DoneState>>,
|
||||
WonState: '' as Ref<Class<WonState>>,
|
||||
LostState: '' as Ref<Class<LostState>>,
|
||||
SpaceWithStates: '' as Ref<Class<SpaceWithStates>>,
|
||||
Task: '' as Ref<Class<Task>>,
|
||||
Project: '' as Ref<Class<Project>>
|
||||
Kanban: '' as Ref<Class<Kanban>>,
|
||||
Sequence: '' as Ref<Class<Sequence>>,
|
||||
StateTemplate: '' as Ref<Class<StateTemplate>>,
|
||||
DoneStateTemplate: '' as Ref<Class<DoneStateTemplate>>,
|
||||
WonStateTemplate: '' as Ref<Class<WonStateTemplate>>,
|
||||
LostStateTemplate: '' as Ref<Class<LostStateTemplate>>,
|
||||
KanbanTemplate: '' as Ref<Class<KanbanTemplate>>,
|
||||
KanbanTemplateSpace: '' as Ref<Class<KanbanTemplateSpace>>
|
||||
},
|
||||
viewlet: {
|
||||
Kanban: '' as Ref<ViewletDescriptor>
|
||||
},
|
||||
icon: {
|
||||
Task: '' as Asset
|
||||
Task: '' as Asset,
|
||||
Kanban: '' as Asset,
|
||||
Status: '' as Asset
|
||||
},
|
||||
global: {
|
||||
// Global task root, if not attached to some other object.
|
||||
Task: '' as Ref<Issue>
|
||||
},
|
||||
space: {
|
||||
ProjectTemplates: '' as Ref<KanbanTemplateSpace>
|
||||
ProjectTemplates: '' as Ref<KanbanTemplateSpace>,
|
||||
Sequence: '' as Ref<Space>
|
||||
}
|
||||
})
|
||||
export default task
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -76,7 +213,7 @@ export async function createProjectKanban (
|
||||
for (const st of states) {
|
||||
const sid = (projectId + '.state.' + st.name.toLowerCase().replace(' ', '_')) as Ref<State>
|
||||
await factory(
|
||||
core.class.State,
|
||||
task.class.State,
|
||||
projectId,
|
||||
{
|
||||
title: st.name,
|
||||
@ -88,8 +225,8 @@ export async function createProjectKanban (
|
||||
}
|
||||
|
||||
const rawDoneStates = [
|
||||
{ class: core.class.WonState, title: 'Won' },
|
||||
{ class: core.class.LostState, title: 'Lost' }
|
||||
{ class: task.class.WonState, title: 'Won' },
|
||||
{ class: task.class.LostState, title: 'Lost' }
|
||||
]
|
||||
const doneStates: Array<Ref<DoneState>> = []
|
||||
for (const st of rawDoneStates) {
|
||||
@ -106,7 +243,7 @@ export async function createProjectKanban (
|
||||
}
|
||||
|
||||
await factory(
|
||||
view.class.Kanban,
|
||||
task.class.Kanban,
|
||||
projectId,
|
||||
{
|
||||
attachedTo: projectId,
|
||||
@ -114,6 +251,68 @@ export async function createProjectKanban (
|
||||
doneStates,
|
||||
order: []
|
||||
},
|
||||
(projectId + '.kanban.') as Ref<Kanban>
|
||||
(projectId + '.kanban') as Ref<Kanban>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function createKanban (client: Client & TxOperations, attachedTo: Ref<Space>, templateId?: Ref<KanbanTemplate>): Promise<Ref<Kanban>> {
|
||||
if (templateId === undefined) {
|
||||
return await client.createDoc(task.class.Kanban, attachedTo, {
|
||||
attachedTo,
|
||||
states: [],
|
||||
doneStates: await Promise.all([
|
||||
client.createDoc(task.class.WonState, attachedTo, {
|
||||
title: 'Won'
|
||||
}),
|
||||
client.createDoc(task.class.LostState, attachedTo, {
|
||||
title: 'Lost'
|
||||
})
|
||||
]),
|
||||
order: []
|
||||
})
|
||||
}
|
||||
|
||||
const template = await client.findOne(task.class.KanbanTemplate, { _id: templateId })
|
||||
|
||||
if (template === undefined) {
|
||||
throw Error(`Failed to find target kanban template: ${templateId}`)
|
||||
}
|
||||
|
||||
const tmplStates = await client.findAll(task.class.StateTemplate, { attachedTo: template._id })
|
||||
const states = await Promise.all(
|
||||
template.states
|
||||
.map((id) => tmplStates.find((x) => x._id === id))
|
||||
.filter((tstate): tstate is StateTemplate => tstate !== undefined)
|
||||
.map(async (state) => await client.createDoc(task.class.State, attachedTo, { color: state.color, title: state.title }))
|
||||
)
|
||||
|
||||
const doneClassMap = new Map<Ref<Class<DoneStateTemplate>>, Ref<Class<DoneState>>>([
|
||||
[task.class.WonStateTemplate, task.class.WonState],
|
||||
[task.class.LostStateTemplate, task.class.LostState]
|
||||
])
|
||||
const tmplDoneStates = await client.findAll(task.class.DoneStateTemplate, { attachedTo: template._id })
|
||||
const doneStates = (await Promise.all(
|
||||
template.doneStates
|
||||
.map((id) => tmplDoneStates.find((x) => x._id === id))
|
||||
.filter((tstate): tstate is DoneStateTemplate => tstate !== undefined)
|
||||
.map(async (state) => {
|
||||
const cl = doneClassMap.get(state._class)
|
||||
|
||||
if (cl === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
return await client.createDoc(cl, attachedTo, { title: state.title })
|
||||
})
|
||||
)).filter((x): x is Ref<DoneState> => x !== undefined)
|
||||
|
||||
return await client.createDoc(task.class.Kanban, attachedTo, {
|
||||
attachedTo,
|
||||
states,
|
||||
doneStates,
|
||||
order: []
|
||||
})
|
||||
}
|
||||
|
@ -6,9 +6,6 @@
|
||||
<rect y="14.3" width="16" height="1.7" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="kanban" viewBox="0 0 16 16">
|
||||
<path d="M14,0h-2.8H9.6H6.4H4.8H2C0.9,0,0,0.9,0,2v12c0,1.1,0.9,2,2,2h2.8h1.6h3.2h1.6H14c1.1,0,2-0.9,2-2V2C16,0.9,15.1,0,14,0z M2,14.8c-0.4,0-0.8-0.4-0.8-0.8V2c0-0.4,0.4-0.8,0.8-0.8h2.8v13.6H2z M6.4,14.8V1.2h3.2v13.6H6.4z M14.8,14c0,0.4-0.4,0.8-0.8,0.8 h-2.8V1.2H14c0.4,0,0.8,0.4,0.8,0.8V14z"/>
|
||||
</symbol>
|
||||
<symbol id="card" viewBox="0 0 16 16">
|
||||
<g>
|
||||
<rect width="2.9" height="2.9" />
|
||||
@ -28,4 +25,11 @@
|
||||
<symbol id="move" viewBox="0 0 16 16">
|
||||
<path d="M9.5,1.3C9.3,1,8.9,1,8.6,1.3c-0.2,0.2-0.2,0.6,0,0.9l5.2,5.2H0.6C0.3,7.4,0,7.7,0,8c0,0.3,0.3,0.6,0.6,0.6 h13.2l-5.2,5.2c-0.2,0.2-0.2,0.6,0,0.9c0.2,0.2,0.6,0.2,0.9,0l6.3-6.3c0.2-0.2,0.2-0.6,0-0.9L9.5,1.3z"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id='more-h' viewBox="0 0 16 16">
|
||||
<path d="M8,6.6C7.2,6.6,6.6,7.2,6.6,8c0,0.8,0.6,1.4,1.4,1.4S9.4,8.8,9.4,8C9.4,7.2,8.8,6.6,8,6.6z" />
|
||||
<path d="M3.2,6.6C2.4,6.6,1.8,7.2,1.8,8c0,0.8,0.6,1.4,1.4,1.4S4.6,8.8,4.6,8C4.6,7.2,4,6.6,3.2,6.6z" />
|
||||
<path d="M12.8,6.6c-0.8,0-1.4,0.6-1.4,1.4c0,0.8,0.6,1.4,1.4,1.4s1.4-0.6,1.4-1.4C14.2,7.2,13.6,6.6,12.8,6.6z" />
|
||||
</symbol>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
@ -19,9 +19,9 @@ import view, { viewId } from '@anticrm/view'
|
||||
const icons = require('../assets/icons.svg')
|
||||
loadMetadata(view.icon, {
|
||||
Table: `${icons}#table`,
|
||||
Kanban: `${icons}#kanban`,
|
||||
Delete: `${icons}#delete`,
|
||||
Move: `${icons}#move`
|
||||
Move: `${icons}#move`,
|
||||
MoreH: `${icons}#more-h`
|
||||
})
|
||||
|
||||
addStringsLoader(viewId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
|
@ -19,7 +19,7 @@
|
||||
import PopupDialog from './PopupDialog.svelte'
|
||||
|
||||
export let colors: string[] = ['#A5D179', '#77C07B', '#60B96E', '#45AEA3', '#46CBDE', '#47BDF6',
|
||||
'#5AADF6', '#73A6CD', '#B977CB', '#7C6FCD', '#6F7BC5', '#F28469']
|
||||
'#5AADF6', '#73A6CD', '#B977CB', '#7C6FCD', '#6F7BC5', '#F28469']
|
||||
export let columns: number = 5
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
@ -21,9 +21,6 @@ import BooleanEditor from './components/BooleanEditor.svelte'
|
||||
import BooleanPresenter from './components/BooleanPresenter.svelte'
|
||||
import DateEditor from './components/DateEditor.svelte'
|
||||
import DatePresenter from './components/DatePresenter.svelte'
|
||||
import KanbanView from './components/KanbanView.svelte'
|
||||
import StateEditor from './components/StateEditor.svelte'
|
||||
import StatePresenter from './components/StatePresenter.svelte'
|
||||
import StringEditor from './components/StringEditor.svelte'
|
||||
import StringPresenter from './components/StringPresenter.svelte'
|
||||
import Table from './components/Table.svelte'
|
||||
@ -33,9 +30,6 @@ import { deleteObject } from './utils'
|
||||
import MoveView from './components/Move.svelte'
|
||||
|
||||
export { default as ContextMenu } from './components/Menu.svelte'
|
||||
export { default as KanbanEditor } from './components/KanbanEditor.svelte'
|
||||
export { default as KanbanTemplateEditor } from './components/KanbanTemplateEditor.svelte'
|
||||
export { default as KanbanTemplateSelector } from './components/KanbanTemplateSelector.svelte'
|
||||
export { buildModel, getActions, getObjectPresenter } from './utils'
|
||||
export { Table }
|
||||
|
||||
@ -69,10 +63,7 @@ export default async (): Promise<Resources> => ({
|
||||
StringPresenter,
|
||||
BooleanPresenter,
|
||||
BooleanEditor,
|
||||
StatePresenter,
|
||||
StateEditor,
|
||||
TableView,
|
||||
KanbanView,
|
||||
TimestampPresenter,
|
||||
DateEditor,
|
||||
DatePresenter
|
||||
|
@ -128,19 +128,28 @@ export async function buildModel (options: BuildModelOptions): Promise<Attribute
|
||||
return (await Promise.all(model)).filter(a => a !== undefined) as AttributeModel[]
|
||||
}
|
||||
|
||||
function filterActions (client: Client, _class: Ref<Class<Obj>>, targets: ActionTarget[]): Array<Ref<Action>> {
|
||||
function filterActions (client: Client, _class: Ref<Class<Obj>>, targets: ActionTarget[], derived: Ref<Class<Doc>> = core.class.Doc): Array<Ref<Action>> {
|
||||
const result: Array<Ref<Action>> = []
|
||||
const hierarchy = client.getHierarchy()
|
||||
for (const target of targets) {
|
||||
if (client.getHierarchy().isDerived(_class, target.target)) {
|
||||
if (hierarchy.isDerived(_class, target.target) && client.getHierarchy().isDerived(target.target, derived)) {
|
||||
result.push(target.action)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getActions (client: Client, _class: Ref<Class<Obj>>): Promise<FindResult<Action>> {
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* Find all action contributions applicable for specified _class.
|
||||
* If derivedFrom is specifie, only actions applicable to derivedFrom class will be used.
|
||||
* So if we have contribution for Doc, Space and we ask for SpaceWithStates and derivedFrom=Space,
|
||||
* we won't recieve Doc contribution but recieve Space ones.
|
||||
*/
|
||||
export async function getActions (client: Client, _class: Ref<Class<Obj>>, derived: Ref<Class<Doc>> = core.class.Doc): Promise<FindResult<Action>> {
|
||||
const targets = await client.findAll(view.class.ActionTarget, {})
|
||||
return await client.findAll(view.class.Action, { _id: { $in: filterActions(client, _class, targets) } })
|
||||
return await client.findAll(view.class.Action, { _id: { $in: filterActions(client, _class, targets, derived) } })
|
||||
}
|
||||
|
||||
export async function deleteObject (client: Client & TxOperations, object: Doc): Promise<void> {
|
||||
|
@ -14,11 +14,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core from '@anticrm/core'
|
||||
import type { Plugin, Asset, Resource, IntlString } from '@anticrm/platform'
|
||||
import type { Class, Client, Doc, FindOptions, Mixin, Obj, Ref, Space, UXObject } from '@anticrm/core'
|
||||
import type { Asset, IntlString, Plugin, Resource } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { Ref, Mixin, UXObject, Space, FindOptions, Class, Doc, Arr, State, Client, Obj, DoneState, AttachedDoc, WonState, LostState, TxOperations } from '@anticrm/core'
|
||||
|
||||
import type { AnyComponent, AnySvelteComponent } from '@anticrm/ui'
|
||||
|
||||
/**
|
||||
@ -35,13 +33,6 @@ export interface AttributePresenter extends Class<Doc> {
|
||||
presenter: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KanbanCard extends Class<Doc> {
|
||||
card: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -82,62 +73,6 @@ export interface ActionTarget extends Doc {
|
||||
action: Ref<Action>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Sequence extends Doc {
|
||||
attachedTo: Ref<Class<Doc>>
|
||||
sequence: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Kanban extends Doc {
|
||||
attachedTo: Ref<Space>
|
||||
states: Arr<Ref<State>>
|
||||
doneStates: Arr<Ref<DoneState>>
|
||||
order: Arr<Ref<Doc>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface StateTemplate extends AttachedDoc, State {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DoneStateTemplate extends AttachedDoc, DoneState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface WonStateTemplate extends DoneStateTemplate, WonState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface LostStateTemplate extends DoneStateTemplate, LostState {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KanbanTemplate extends Doc {
|
||||
title: string
|
||||
states: Arr<Ref<StateTemplate>>
|
||||
doneStates: Arr<Ref<DoneStateTemplate>>
|
||||
statesC: number
|
||||
doneStatesC: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KanbanTemplateSpace extends Space {
|
||||
icon: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -181,100 +116,22 @@ const view = plugin(viewId, {
|
||||
mixin: {
|
||||
AttributeEditor: '' as Ref<Mixin<AttributeEditor>>,
|
||||
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
|
||||
KanbanCard: '' as Ref<Mixin<KanbanCard>>,
|
||||
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>
|
||||
},
|
||||
class: {
|
||||
ViewletDescriptor: '' as Ref<Class<ViewletDescriptor>>,
|
||||
Viewlet: '' as Ref<Class<Viewlet>>,
|
||||
Action: '' as Ref<Class<Action>>,
|
||||
ActionTarget: '' as Ref<Class<ActionTarget>>,
|
||||
Kanban: '' as Ref<Class<Kanban>>,
|
||||
Sequence: '' as Ref<Class<Sequence>>,
|
||||
StateTemplate: '' as Ref<Class<StateTemplate>>,
|
||||
DoneStateTemplate: '' as Ref<Class<DoneStateTemplate>>,
|
||||
WonStateTemplate: '' as Ref<Class<WonStateTemplate>>,
|
||||
LostStateTemplate: '' as Ref<Class<LostStateTemplate>>,
|
||||
KanbanTemplate: '' as Ref<Class<KanbanTemplate>>,
|
||||
KanbanTemplateSpace: '' as Ref<Class<KanbanTemplateSpace>>
|
||||
ActionTarget: '' as Ref<Class<ActionTarget>>
|
||||
},
|
||||
viewlet: {
|
||||
Table: '' as Ref<ViewletDescriptor>,
|
||||
Kanban: '' as Ref<ViewletDescriptor>
|
||||
},
|
||||
space: {
|
||||
Sequence: '' as Ref<Space>
|
||||
Table: '' as Ref<ViewletDescriptor>
|
||||
},
|
||||
icon: {
|
||||
Table: '' as Asset,
|
||||
Kanban: '' as Asset,
|
||||
Delete: '' as Asset,
|
||||
MoreH: '' as Asset,
|
||||
Move: '' as Asset
|
||||
},
|
||||
string: {
|
||||
Delete: '' as IntlString
|
||||
}
|
||||
})
|
||||
export default view
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function createKanban (client: Client & TxOperations, attachedTo: Ref<Space>, templateId?: Ref<KanbanTemplate>): Promise<Ref<Kanban>> {
|
||||
if (templateId === undefined) {
|
||||
return await client.createDoc(view.class.Kanban, attachedTo, {
|
||||
attachedTo,
|
||||
states: [],
|
||||
doneStates: await Promise.all([
|
||||
client.createDoc(core.class.WonState, attachedTo, {
|
||||
title: 'Won'
|
||||
}),
|
||||
client.createDoc(core.class.LostState, attachedTo, {
|
||||
title: 'Lost'
|
||||
})
|
||||
]),
|
||||
order: []
|
||||
})
|
||||
}
|
||||
|
||||
const template = await client.findOne(view.class.KanbanTemplate, { _id: templateId })
|
||||
|
||||
if (template === undefined) {
|
||||
throw Error(`Failed to find target kanban template: ${templateId}`)
|
||||
}
|
||||
|
||||
const tmplStates = await client.findAll(view.class.StateTemplate, { attachedTo: template._id })
|
||||
const states = await Promise.all(
|
||||
template.states
|
||||
.map((id) => tmplStates.find((x) => x._id === id))
|
||||
.filter((tstate): tstate is StateTemplate => tstate !== undefined)
|
||||
.map(async (state) => await client.createDoc(core.class.State, attachedTo, { color: state.color, title: state.title }))
|
||||
)
|
||||
|
||||
const doneClassMap = new Map<Ref<Class<DoneStateTemplate>>, Ref<Class<DoneState>>>([
|
||||
[view.class.WonStateTemplate, core.class.WonState],
|
||||
[view.class.LostStateTemplate, core.class.LostState]
|
||||
])
|
||||
const tmplDoneStates = await client.findAll(view.class.DoneStateTemplate, { attachedTo: template._id })
|
||||
const doneStates = (await Promise.all(
|
||||
template.doneStates
|
||||
.map((id) => tmplDoneStates.find((x) => x._id === id))
|
||||
.filter((tstate): tstate is DoneStateTemplate => tstate !== undefined)
|
||||
.map(async (state) => {
|
||||
const cl = doneClassMap.get(state._class)
|
||||
|
||||
if (cl === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
return await client.createDoc(cl, attachedTo, { title: state.title })
|
||||
})
|
||||
)).filter((x): x is Ref<DoneState> => x !== undefined)
|
||||
|
||||
return await client.createDoc(view.class.Kanban, attachedTo, {
|
||||
attachedTo,
|
||||
states,
|
||||
doneStates,
|
||||
order: []
|
||||
})
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'var(--theme-caption-color)'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.4,8c0,3.5-2.9,6.4-6.4,6.4c-3.5,0-6.4-2.9-6.4-6.4c0-3.5,2.9-6.4,6.4-6.4c0.1-0.4,0.3-0.8,0.5-1.2c-0.2,0-0.3,0-0.5,0 C3.8,0.4,0.4,3.8,0.4,8c0,4.2,3.4,7.6,7.6,7.6s7.6-3.4,7.6-7.6c0-0.2,0-0.3,0-0.5C15.2,7.7,14.8,7.9,14.4,8z"/>
|
||||
<circle cx="13" cy="3" r="3"/>
|
||||
</svg>
|
@ -14,24 +14,18 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
import type { Asset, IntlString } from '@anticrm/platform'
|
||||
import type { Ref, Space, Doc } from '@anticrm/core'
|
||||
import type { Doc, Ref, Space } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import { getResource, IntlString } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Action, getCurrentLocation, IconAdd, IconEdit, location, navigate, showPopup } from '@anticrm/ui'
|
||||
import { getActions as getContributedActions } from '@anticrm/view-resources'
|
||||
import type { SpacesNavModel } from '@anticrm/workbench'
|
||||
import { Action, navigate, getCurrentLocation, location, IconAdd, IconMoreH, IconEdit } from '@anticrm/ui'
|
||||
|
||||
import { getClient, createQuery } from '@anticrm/presentation'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
|
||||
import { onDestroy } from 'svelte'
|
||||
import { classIcon } from '../../utils'
|
||||
|
||||
import TreeNode from './TreeNode.svelte'
|
||||
import TreeItem from './TreeItem.svelte'
|
||||
|
||||
import EditStatuses from '../EditStatuses.svelte'
|
||||
import SpacePanel from './SpacePanel.svelte'
|
||||
import TreeItem from './TreeItem.svelte'
|
||||
import TreeNode from './TreeNode.svelte'
|
||||
|
||||
export let model: SpacesNavModel
|
||||
|
||||
@ -50,14 +44,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
const editStatuses: Action = {
|
||||
label: 'Edit Statuses' as IntlString,
|
||||
icon: IconMoreH,
|
||||
action: async (_id: Ref<Doc>): Promise<void> => {
|
||||
showPopup(EditStatuses, { _id, spaceClass: model.spaceClass }, 'right')
|
||||
}
|
||||
}
|
||||
|
||||
const editSpace: Action = {
|
||||
label: 'Open' as IntlString,
|
||||
icon: IconEdit,
|
||||
@ -78,10 +64,19 @@
|
||||
selected = loc.path[2] as Ref<Space>
|
||||
}))
|
||||
|
||||
function getActions (space: Space): Action[] {
|
||||
async function getActions (space: Space): Promise<Action[]> {
|
||||
const result = [editSpace]
|
||||
if (client.getHierarchy().isDerived(space._class, core.class.SpaceWithStates)) {
|
||||
result.push(editStatuses)
|
||||
|
||||
const extraActions = await getContributedActions(client, space._class, core.class.Space)
|
||||
for (const act of extraActions) {
|
||||
result.push({
|
||||
icon: act.icon ?? IconEdit,
|
||||
label: act.label,
|
||||
action: async (props, ev) => {
|
||||
const impl = await getResource(act.action)
|
||||
await impl(space)
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -90,7 +85,9 @@
|
||||
<div>
|
||||
<TreeNode label={model.label} actions={[addSpace]}>
|
||||
{#each spaces as space}
|
||||
<TreeItem _id={space._id} title={space.name} icon={classIcon(client, space._class)} selected={selected === space._id} actions={getActions(space)} on:click={() => { selectSpace(space._id) }}/>
|
||||
{#await getActions(space) then actions}
|
||||
<TreeItem _id={space._id} title={space.name} icon={classIcon(client, space._class)} selected={selected === space._id} {actions} on:click={() => { selectSpace(space._id) }}/>
|
||||
{/await}
|
||||
{/each}
|
||||
</TreeNode>
|
||||
</div>
|
||||
|
12
rush.json
12
rush.json
@ -737,18 +737,18 @@
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server-view-resources",
|
||||
"projectFolder": "server/view-resources",
|
||||
"packageName": "@anticrm/server-task-resources",
|
||||
"projectFolder": "server/task-resources",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server-view",
|
||||
"projectFolder": "server/view",
|
||||
"packageName": "@anticrm/server-task",
|
||||
"projectFolder": "server/task",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/model-server-view",
|
||||
"projectFolder": "models/server-view",
|
||||
"packageName": "@anticrm/model-server-task",
|
||||
"projectFolder": "models/server-task",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
|
@ -118,14 +118,13 @@ class TServerStorage implements ServerStorage {
|
||||
return []
|
||||
}
|
||||
|
||||
if (colTx.tx._class === core.class.TxCreateDoc) {
|
||||
attachedTo = (await this.findAll(_class, { _id }))[0]
|
||||
const txFactory = new TxFactory(tx.modifiedBy)
|
||||
return [txFactory.createTxUpdateDoc(_class, attachedTo.space, _id, { $inc: { [colTx.collection]: 1 } })]
|
||||
} else if (colTx.tx._class === core.class.TxRemoveDoc) {
|
||||
attachedTo = (await this.findAll(_class, { _id }))[0]
|
||||
const txFactory = new TxFactory(tx.modifiedBy)
|
||||
return [txFactory.createTxUpdateDoc(_class, attachedTo.space, _id, { $inc: { [colTx.collection]: -1 } })]
|
||||
const isCreateTx = colTx.tx._class === core.class.TxCreateDoc
|
||||
if (isCreateTx || colTx.tx._class === core.class.TxRemoveDoc) {
|
||||
attachedTo = (await this.findAll(_class, { _id }, { limit: 1 }))[0]
|
||||
if (attachedTo !== undefined) {
|
||||
const txFactory = new TxFactory(tx.modifiedBy)
|
||||
return [txFactory.createTxUpdateDoc(_class, attachedTo.space, _id, { $inc: { [colTx.collection]: isCreateTx ? 1 : -1 } })]
|
||||
}
|
||||
}
|
||||
}
|
||||
return []
|
||||
|
@ -44,7 +44,7 @@
|
||||
"@anticrm/server-recruit-resources": "~0.6.0",
|
||||
"@anticrm/mongo": "~0.6.1",
|
||||
"@anticrm/elastic": "~0.6.0",
|
||||
"@anticrm/server-view": "~0.6.0",
|
||||
"@anticrm/server-view-resources": "~0.6.0"
|
||||
"@anticrm/server-task": "~0.6.0",
|
||||
"@anticrm/server-task-resources": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user