mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-25 19:58:30 +03:00
parent
34b9495631
commit
b0ffc0ed46
@ -51,7 +51,6 @@ 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-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 +73,6 @@ 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-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
|
||||
@ -144,6 +141,7 @@ specifiers:
|
||||
koa: ^2.13.1
|
||||
koa-bodyparser: ^4.3.0
|
||||
koa-router: ^10.1.1
|
||||
lexorank: ~1.0.4
|
||||
mini-css-extract-plugin: ^2.2.0
|
||||
minio: ^7.0.19
|
||||
node-html-parser: ^4.1.3
|
||||
@ -220,7 +218,6 @@ 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-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 +240,6 @@ 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-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
|
||||
@ -313,6 +308,7 @@ dependencies:
|
||||
koa: 2.13.3
|
||||
koa-bodyparser: 4.3.0
|
||||
koa-router: 10.1.1
|
||||
lexorank: 1.0.4
|
||||
mini-css-extract-plugin: 2.4.1_webpack@5.57.1
|
||||
minio: 7.0.19
|
||||
node-html-parser: 4.1.5
|
||||
@ -6409,6 +6405,10 @@ packages:
|
||||
type-check: 0.4.0
|
||||
dev: false
|
||||
|
||||
/lexorank/1.0.4:
|
||||
resolution: {integrity: sha512-CMgA8AMJIX/QfoYHKyjg0hv9W1SGL2xRkt0uLyhT9xKKRj73fHi+IhsrB3W36wwk4I0iz8YlKHfdW14QDwerMA==}
|
||||
dev: false
|
||||
|
||||
/lilconfig/2.0.3:
|
||||
resolution: {integrity: sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==}
|
||||
engines: {node: '>=10'}
|
||||
@ -10345,7 +10345,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/core.tgz:
|
||||
resolution: {integrity: sha512-1RXdycw5v+KGu+kmqXvnrKAZpNYbc0U/rInLVwKYJ17U/hED4ONAIPEaVDxp+fxgyPHpEo7AQjZe2aD87ogwtg==, tarball: file:projects/core.tgz}
|
||||
resolution: {integrity: sha512-Xjk/aGZgRnNWYrQpTy8f2H8oQVVIvbkX6MdesJnRH7AjJTI2RtloF+8sUQMSjSahPh7thHwaYyDVlJ37vMXXNQ==, tarball: file:projects/core.tgz}
|
||||
name: '@rush-temp/core'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -10359,6 +10359,7 @@ packages:
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.1.1_eslint@7.32.0
|
||||
just-clone: 3.2.1
|
||||
lexorank: 1.0.4
|
||||
prettier: 2.4.1
|
||||
simplytyped: 3.3.0_typescript@4.4.3
|
||||
typescript: 4.4.3
|
||||
@ -10770,7 +10771,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-all.tgz_typescript@4.4.3:
|
||||
resolution: {integrity: sha512-XyhGIWfnSxUKXSXQ/yfvCx+S5xqBMJ4eko1pDFv0v048sQJy6fefxg3LOLyCDodNzsa5KxGdqvk7Y8H7Izop0g==, tarball: file:projects/model-all.tgz}
|
||||
resolution: {integrity: sha512-iCugm1e/KTBXZ0FNnog/eP07l49sFIDU2orN0vAMUJ5pgZC7T38LSjnOL2y80OmoTbQK/3G2eH7Or+bxOYk2Cg==, tarball: file:projects/model-all.tgz}
|
||||
id: file:projects/model-all.tgz
|
||||
name: '@rush-temp/model-all'
|
||||
version: 0.0.0
|
||||
@ -10837,7 +10838,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-contact.tgz_typescript@4.4.3:
|
||||
resolution: {integrity: sha512-qZPBOQC2Oi/Qxpjt91OM1tGBPwShe2SLIHKmDNGSsk79zzdmW3+ZDrge4VVrK/ngc5P2fJQwpQRXgOkNgz2vnA==, tarball: file:projects/model-contact.tgz}
|
||||
resolution: {integrity: sha512-ffJA0hXhTeEjKGbCFKTgYk9ch8JsAnZKmDOtyTSzE+tmIklA0Q1gWQGwqttPSgrGVQuy2UXkYXv1m7cnhBICxg==, tarball: file:projects/model-contact.tgz}
|
||||
id: file:projects/model-contact.tgz
|
||||
name: '@rush-temp/model-contact'
|
||||
version: 0.0.0
|
||||
@ -11023,27 +11024,6 @@ packages:
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
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
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.4.0_87dbf04088b125598d0271706532eaf3
|
||||
'@typescript-eslint/parser': 5.4.0_eslint@7.32.0+typescript@4.4.3
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_05a8ea1454e6ca4c9f98b94b8f3abf9c
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.1.1_eslint@7.32.0
|
||||
prettier: 2.4.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-setting.tgz_typescript@4.4.3:
|
||||
resolution: {integrity: sha512-U6P3iLbt33/iJI21fs7trAHtpwWzmI/zJn+3qyxjjnFP88i11vmbd1zfb6+GYu5muRCutqNi1pTX/5cDo+NtrA==, tarball: file:projects/model-setting.tgz}
|
||||
id: file:projects/model-setting.tgz
|
||||
@ -11339,7 +11319,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod.tgz_sass@1.42.1+typescript@4.4.3:
|
||||
resolution: {integrity: sha512-jgSulZmT4Kq28boQo8BJQrdkcN660EtY7XUH0QWcgGYHDBXFiGfP4eoIxAJL4wtb/JKwKLEkwG+SwTUIuIb7Xw==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-cWieTfrEsukKvJvSGr/ZOX6Vo4aZQHGDpOrE2smLt21Rk+/lSNpUcpAKKGRoFmW08sWkQoYrU6BdPMgw4+/UNQ==, tarball: file:projects/prod.tgz}
|
||||
id: file:projects/prod.tgz
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
@ -11573,47 +11553,6 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
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
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.4.0_87dbf04088b125598d0271706532eaf3
|
||||
'@typescript-eslint/parser': 5.4.0_eslint@7.32.0+typescript@4.4.3
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_05a8ea1454e6ca4c9f98b94b8f3abf9c
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.1.1_eslint@7.32.0
|
||||
prettier: 2.4.1
|
||||
typescript: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
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
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@types/node': 16.11.12
|
||||
'@typescript-eslint/eslint-plugin': 5.4.0_87dbf04088b125598d0271706532eaf3
|
||||
'@typescript-eslint/parser': 5.4.0_eslint@7.32.0+typescript@4.4.3
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_05a8ea1454e6ca4c9f98b94b8f3abf9c
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.1.1_eslint@7.32.0
|
||||
prettier: 2.4.1
|
||||
typescript: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-ws.tgz:
|
||||
resolution: {integrity: sha512-MSFFpLjIMFt0oyH4+8JUkNOkCNtdEtMDoxcyN7+kDdz44wSZjSOmheJHYkXO6JTEffcaaRhQ9vO/e7MBNMeoxQ==, tarball: file:projects/server-ws.tgz}
|
||||
name: '@rush-temp/server-ws'
|
||||
@ -11641,7 +11580,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server.tgz:
|
||||
resolution: {integrity: sha512-fFYknnDfAOz+iq8INHKSgCjYUkbxqLiHIBM55W8QtmZq0R8hMLEwCcLiyNqL1iCgXKs8sGHf+aMbZcciBNi1Ow==, tarball: file:projects/server.tgz}
|
||||
resolution: {integrity: sha512-UBFOAisptjpTNIZoU6hWJVBKTk7mftZU0Dz+pJiVhMBJSD+iW393yv4v9GedEpor5rIYDGGcps7IVCXZlr76tw==, tarball: file:projects/server.tgz}
|
||||
name: '@rush-temp/server'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { Ref, TxOperations } from '@anticrm/core'
|
||||
import { genRanks, Ref, TxOperations } from '@anticrm/core'
|
||||
import task, { DoneState, Kanban, SpaceWithStates, State } from '@anticrm/task'
|
||||
import { findOrUpdate } from './utils'
|
||||
|
||||
@ -12,42 +12,55 @@ export async function createUpdateSpaceKanban (spaceId: Ref<SpaceWithStates>, cl
|
||||
{ color: '#F28469', name: 'Invalid' }
|
||||
]
|
||||
const states: Array<Ref<State>> = []
|
||||
const stateRanks = genRanks(rawStates.length)
|
||||
for (const st of rawStates) {
|
||||
const rank = stateRanks.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
console.error('Failed to generate rank')
|
||||
break
|
||||
}
|
||||
|
||||
const sid = ('generated-' + spaceId + '.state.' + st.name.toLowerCase().replace(' ', '_')) as Ref<State>
|
||||
await findOrUpdate(client, spaceId, task.class.State,
|
||||
sid,
|
||||
{
|
||||
title: st.name,
|
||||
color: st.color
|
||||
color: st.color,
|
||||
rank
|
||||
}
|
||||
)
|
||||
states.push(sid)
|
||||
}
|
||||
|
||||
const rawDoneStates = [
|
||||
const doneStates = [
|
||||
{ class: task.class.WonState, title: 'Won' },
|
||||
{ class: task.class.LostState, title: 'Lost' }
|
||||
]
|
||||
const doneStates: Array<Ref<DoneState>> = []
|
||||
for (const st of rawDoneStates) {
|
||||
const doneStateRanks = genRanks(doneStates.length)
|
||||
for (const st of doneStates) {
|
||||
const rank = doneStateRanks.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
console.error('Failed to generate rank')
|
||||
break
|
||||
}
|
||||
|
||||
const sid = ('generated-' + spaceId + '.done-state.' + st.title.toLowerCase().replace(' ', '_')) as Ref<DoneState>
|
||||
await findOrUpdate(client, spaceId, st.class,
|
||||
sid,
|
||||
{
|
||||
title: st.title
|
||||
title: st.title,
|
||||
rank
|
||||
}
|
||||
)
|
||||
doneStates.push(sid)
|
||||
}
|
||||
|
||||
await findOrUpdate(client, spaceId,
|
||||
task.class.Kanban,
|
||||
('generated-' + spaceId + '.kanban') as Ref<Kanban>,
|
||||
{
|
||||
attachedTo: spaceId,
|
||||
states,
|
||||
doneStates,
|
||||
order: []
|
||||
attachedTo: spaceId
|
||||
}
|
||||
)
|
||||
return states
|
||||
|
@ -1,5 +1,5 @@
|
||||
import contact from '@anticrm/contact'
|
||||
import core, { AttachedData, Data, generateId, Ref, TxOperations } from '@anticrm/core'
|
||||
import core, { AttachedData, Data, generateId, genRanks, Ref, TxOperations } from '@anticrm/core'
|
||||
import recruit from '@anticrm/model-recruit'
|
||||
import { Applicant, Candidate, Vacancy } from '@anticrm/recruit'
|
||||
import faker from 'faker'
|
||||
@ -79,7 +79,7 @@ export async function generateContacts (transactorUrl: string, dbName: string, o
|
||||
}
|
||||
const vacancyId = (options.random ? `vacancy-${generateId()}-${i}` : `vacancy-genid-${i}`) as Ref<Vacancy>
|
||||
|
||||
console.log('Creating vacandy', vacancy.name)
|
||||
console.log('Creating vacancy', vacancy.name)
|
||||
// Update or create candidate
|
||||
await findOrUpdate(client, core.space.Model, recruit.class.Vacancy, vacancyId, vacancy)
|
||||
|
||||
@ -93,14 +93,22 @@ export async function generateContacts (transactorUrl: string, dbName: string, o
|
||||
|
||||
console.log('States generated', vacancy.name)
|
||||
|
||||
const rankGen = genRanks(candidates.length)
|
||||
for (const candidateId of candidates) {
|
||||
const rank = rankGen.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
throw Error('Failed to generate rank')
|
||||
}
|
||||
|
||||
const applicantId = `vacancy-${vacancyId}-${candidateId}` as Ref<Applicant>
|
||||
|
||||
const applicant: AttachedData<Applicant> = {
|
||||
number: faker.datatype.number(),
|
||||
assignee: faker.random.arrayElement(emoloyeeIds),
|
||||
state: faker.random.arrayElement(states),
|
||||
doneState: null
|
||||
doneState: null,
|
||||
rank
|
||||
}
|
||||
|
||||
// Update or create candidate
|
||||
|
@ -71,8 +71,6 @@
|
||||
"@anticrm/contact-assets": "~0.6.0",
|
||||
"@anticrm/server-recruit": "~0.6.1",
|
||||
"@anticrm/server-recruit-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,6 @@ 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-task'
|
||||
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
|
||||
@ -33,7 +32,6 @@ 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-task" */ '@anticrm/server-task-resources'))
|
||||
|
||||
// Set devmodel to hook client to be able to present all activity
|
||||
enableDevModel()
|
||||
|
@ -43,7 +43,6 @@
|
||||
"@anticrm/model-server-core": "~0.6.0",
|
||||
"@anticrm/model-server-chunter": "~0.6.0",
|
||||
"@anticrm/model-server-recruit": "~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,6 @@ 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-task'
|
||||
import { createModel as activityModel } from '@anticrm/model-activity'
|
||||
|
||||
import { createDemo } from '@anticrm/model-demo'
|
||||
@ -53,7 +52,6 @@ leadModel(builder)
|
||||
serverCoreModel(builder)
|
||||
serverChunterModel(builder)
|
||||
serverRecruitModel(builder)
|
||||
serverViewModel(builder)
|
||||
|
||||
createDemo(builder)
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
import type { Account, Arr, Ref, Space } from '@anticrm/core'
|
||||
import { DOMAIN_MODEL } from '@anticrm/core'
|
||||
import { Model, Prop, TypeBoolean, TypeString } from '@anticrm/model'
|
||||
import { Implements, Model, Prop, TypeBoolean, TypeString } from '@anticrm/model'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import core from './component'
|
||||
import { TDoc } from './core'
|
||||
@ -40,3 +40,8 @@ export class TSpace extends TDoc implements Space {
|
||||
export class TAccount extends TDoc implements Account {
|
||||
email!: string
|
||||
}
|
||||
@Implements(core.interface.DocWithRank)
|
||||
export class TDocWithRank extends TDoc {
|
||||
@Prop(TypeString(), 'Rank' as IntlString)
|
||||
rank!: string
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/model-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
@ -1,18 +0,0 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/model-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "@anticrm/model-server-task",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/model-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.41.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/model": "~0.6.0",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/server-task": "~0.6.0",
|
||||
"@anticrm/server-core": "~0.6.0"
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Builder } from '@anticrm/model'
|
||||
|
||||
import serverCore from '@anticrm/server-core'
|
||||
import core from '@anticrm/core'
|
||||
import serverTask from '@anticrm/server-task'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTask.trigger.OnTask
|
||||
})
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/model-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
}
|
||||
}
|
@ -53,18 +53,22 @@ 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)
|
||||
@Model(task.class.State, core.class.Doc, DOMAIN_STATE, [core.interface.DocWithRank])
|
||||
export class TState extends TDoc implements State {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
color!: string
|
||||
|
||||
declare rank: string
|
||||
}
|
||||
|
||||
@Model(task.class.DoneState, core.class.Doc, DOMAIN_STATE)
|
||||
@Model(task.class.DoneState, core.class.Doc, DOMAIN_STATE, [core.interface.DocWithRank])
|
||||
export class TDoneState extends TDoc implements DoneState {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
declare rank: string
|
||||
}
|
||||
|
||||
@Model(task.class.WonState, task.class.DoneState, DOMAIN_STATE)
|
||||
@ -78,7 +82,7 @@ export class TLostState extends TDoneState implements LostState {}
|
||||
*
|
||||
* No domain is specified, since pure Tasks could not exists
|
||||
*/
|
||||
@Model(task.class.Task, core.class.AttachedDoc, DOMAIN_TASK)
|
||||
@Model(task.class.Task, core.class.AttachedDoc, DOMAIN_TASK, [core.interface.DocWithRank])
|
||||
export class TTask extends TAttachedDoc implements Task {
|
||||
@Prop(TypeRef(task.class.State), 'State' as IntlString)
|
||||
state!: Ref<State>
|
||||
@ -91,6 +95,8 @@ export class TTask extends TAttachedDoc implements Task {
|
||||
|
||||
// @Prop(TypeRef(contact.class.Employee), 'Assignee' as IntlString)
|
||||
assignee!: Ref<Employee> | null
|
||||
|
||||
declare rank: string
|
||||
}
|
||||
|
||||
@Model(task.class.SpaceWithStates, core.class.Space)
|
||||
@ -136,7 +142,6 @@ 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)
|
||||
@ -144,19 +149,23 @@ export class TKanbanTemplateSpace extends TSpace implements KanbanTemplateSpace
|
||||
icon!: AnyComponent
|
||||
}
|
||||
|
||||
@Model(task.class.StateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN)
|
||||
@Model(task.class.StateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN, [core.interface.DocWithRank])
|
||||
export class TStateTemplate extends TAttachedDoc implements StateTemplate {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
@Prop(TypeString(), 'Color' as IntlString)
|
||||
color!: string
|
||||
|
||||
declare rank: string
|
||||
}
|
||||
|
||||
@Model(task.class.DoneStateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN)
|
||||
@Model(task.class.DoneStateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN, [core.interface.DocWithRank])
|
||||
export class TDoneStateTemplate extends TAttachedDoc implements DoneStateTemplate {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
|
||||
declare rank: string
|
||||
}
|
||||
|
||||
@Model(task.class.WonStateTemplate, task.class.DoneStateTemplate, DOMAIN_KANBAN)
|
||||
@ -170,9 +179,6 @@ 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
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Class, Doc, Domain, DOMAIN_TX, Ref, TxCUD, TxOperations } from '@anticrm/core'
|
||||
import { AttachedDoc, Class, Client, Doc, DocWithRank, Domain, DOMAIN_TX, genRanks, Ref, Space, TxCUD, TxOperations } from '@anticrm/core'
|
||||
import {
|
||||
MigrateOperation,
|
||||
MigrateUpdate,
|
||||
@ -22,7 +22,7 @@ import {
|
||||
MigrationUpgradeClient
|
||||
} from '@anticrm/model'
|
||||
import core from '@anticrm/model-core'
|
||||
import { createProjectKanban } from '@anticrm/task'
|
||||
import { createProjectKanban, KanbanTemplate } from '@anticrm/task'
|
||||
import { DOMAIN_TASK, DOMAIN_STATE, DOMAIN_KANBAN } from '.'
|
||||
import task from './plugin'
|
||||
|
||||
@ -134,28 +134,148 @@ export const taskOperation: MigrateOperation = {
|
||||
|
||||
console.log('View: Performing model upgrades')
|
||||
|
||||
const kanbans = (await client.findAll(task.class.Kanban, {})).filter((kanban) => kanban.doneStates == null)
|
||||
await createMissingDoneStates(client, ops)
|
||||
await updateRankItems({ client, ops, _class: task.class.State, extractOrder: (kanban) => kanban.states })
|
||||
await updateRankItems({ client, ops, _class: task.class.DoneState, extractOrder: (kanban) => kanban.doneStates })
|
||||
await updateRankItems({ client, ops, _class: task.class.Task, extractOrder: (kanban) => kanban.order })
|
||||
await updateTemplateRankItems({ client, ops, _class: task.class.StateTemplate, extractOrder: (kanban) => kanban.states })
|
||||
await updateTemplateRankItems({ client, ops, _class: task.class.DoneStateTemplate, extractOrder: (kanban) => kanban.doneStates })
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
kanbans.map(async (kanban) => {
|
||||
console.log(`Updating kanban: ${kanban._id}`)
|
||||
async function createMissingDoneStates (client: Client, ops: TxOperations): Promise<void> {
|
||||
const spacesWithStates = await client.findAll(task.class.SpaceWithStates, {})
|
||||
const doneStates = await client.findAll(task.class.DoneState, {})
|
||||
const spaceIdsWithDoneStates = new Set(doneStates.map(x => x.space))
|
||||
const outdatedSpaces = spacesWithStates.filter((space) => !spaceIdsWithDoneStates.has(space._id))
|
||||
|
||||
const pairRanks = [...genRanks(2)]
|
||||
|
||||
await Promise.all(
|
||||
outdatedSpaces
|
||||
.map(async (space) => {
|
||||
console.log(`Creating done states for space: ${space._id}`)
|
||||
try {
|
||||
const doneStates = await Promise.all([
|
||||
ops.createDoc(task.class.WonState, kanban.space, {
|
||||
title: 'Won'
|
||||
await Promise.all([
|
||||
ops.createDoc(task.class.WonState, space._id, {
|
||||
title: 'Won',
|
||||
rank: pairRanks[0]
|
||||
}),
|
||||
ops.createDoc(task.class.LostState, kanban.space, {
|
||||
title: 'Lost'
|
||||
ops.createDoc(task.class.LostState, space._id, {
|
||||
title: 'Lost',
|
||||
rank: pairRanks[1]
|
||||
})
|
||||
])
|
||||
|
||||
await ops.updateDoc(kanban._class, kanban.space, kanban._id, {
|
||||
doneStates
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
async function updateRankItems<T extends DocWithRank> ({
|
||||
client,
|
||||
ops,
|
||||
_class,
|
||||
extractOrder
|
||||
}: {
|
||||
client: Client
|
||||
ops: TxOperations
|
||||
_class: Ref<Class<T>>
|
||||
extractOrder: (kanban: any) => Ref<T>[]
|
||||
}): Promise<void> {
|
||||
const allItems = await client.findAll(_class, {})
|
||||
const unorderedItems = allItems
|
||||
.filter((item) => item.rank === undefined)
|
||||
const groupedUnsortedItems = new Map<Ref<Space>, T[]>()
|
||||
|
||||
unorderedItems.forEach((item) => {
|
||||
const existing = groupedUnsortedItems.get(item.space) ?? []
|
||||
groupedUnsortedItems.set(item.space, [...existing, item])
|
||||
})
|
||||
|
||||
for (const [space, items] of groupedUnsortedItems.entries()) {
|
||||
const kanban = await client.findOne(task.class.Kanban, { attachedTo: space })
|
||||
|
||||
if (kanban === undefined) {
|
||||
console.error(`Failed to find kanban attached to space '${space}'`)
|
||||
continue
|
||||
}
|
||||
|
||||
const order = extractOrder(kanban)
|
||||
|
||||
if (order === undefined) {
|
||||
console.error(`Kanban doesn't contain items order: ${kanban._id}`)
|
||||
continue
|
||||
}
|
||||
|
||||
const orderedItems = order
|
||||
.map((id) => items.find(x => x._id === id))
|
||||
.filter((items): items is T => items !== undefined)
|
||||
const ranks = genRanks(orderedItems.length)
|
||||
|
||||
for (const item of orderedItems) {
|
||||
const rank = ranks.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
console.error('Failed to generate rank')
|
||||
break
|
||||
}
|
||||
|
||||
await ops.updateDoc(item._class as Ref<Class<DocWithRank>>, item.space, item._id, { rank })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function updateTemplateRankItems<T extends DocWithRank & AttachedDoc> ({
|
||||
client,
|
||||
ops,
|
||||
_class,
|
||||
extractOrder
|
||||
}: {
|
||||
client: Client
|
||||
ops: TxOperations
|
||||
_class: Ref<Class<T>>
|
||||
extractOrder: (kanban: any) => Ref<T>[]
|
||||
}): Promise<void> {
|
||||
const allItems = await client.findAll(_class, {})
|
||||
const unorderedItems = allItems
|
||||
.filter((state) => state.rank === undefined)
|
||||
const groupedUnsortedItems = new Map<Ref<Doc>, T[]>()
|
||||
|
||||
unorderedItems.forEach((item) => {
|
||||
const existing = groupedUnsortedItems.get(item.attachedTo) ?? []
|
||||
groupedUnsortedItems.set(item.attachedTo, [...existing, item])
|
||||
})
|
||||
|
||||
for (const [attachedTo, items] of groupedUnsortedItems.entries()) {
|
||||
const kanban = await client.findOne(task.class.KanbanTemplate, { _id: attachedTo as Ref<KanbanTemplate> })
|
||||
|
||||
if (kanban === undefined) {
|
||||
console.error(`Failed to find kanban '${attachedTo}'`)
|
||||
continue
|
||||
}
|
||||
|
||||
const order = extractOrder(kanban)
|
||||
|
||||
if (order === undefined) {
|
||||
console.error(`Kanban doesn't contain items order: ${kanban._id}`)
|
||||
continue
|
||||
}
|
||||
|
||||
const orderedItems = order
|
||||
.map((id) => items.find(x => x._id === id))
|
||||
.filter((items): items is T => items !== undefined)
|
||||
const ranks = genRanks(orderedItems.length)
|
||||
|
||||
for (const item of orderedItems) {
|
||||
const rank = ranks.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
console.error('Failed to generate rank')
|
||||
break
|
||||
}
|
||||
|
||||
await ops.updateDoc(item._class as Ref<Class<DocWithRank>>, item.space, item._id, { rank })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"just-clone": "^3.2.1"
|
||||
"just-clone": "^3.2.1",
|
||||
"lexorank": "~1.0.4"
|
||||
}
|
||||
}
|
||||
|
@ -218,3 +218,10 @@ export interface Space extends Doc {
|
||||
export interface Account extends Doc {
|
||||
email: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DocWithRank extends Doc {
|
||||
rank: string
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
//
|
||||
import type { Plugin, StatusCode } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { Account, AnyAttribute, AttachedDoc, Class, Doc, Interface, Obj, PropertyType, Ref, Space, Timestamp, Type, Collection, RefTo } from './classes'
|
||||
import type { Account, AnyAttribute, AttachedDoc, DocWithRank, 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'
|
||||
|
||||
/**
|
||||
@ -49,6 +49,9 @@ export default plugin(coreId, {
|
||||
Collection: '' as Ref<Class<Collection<AttachedDoc>>>,
|
||||
Bag: '' as Ref<Class<Type<Record<string, PropertyType>>>>
|
||||
},
|
||||
interface: {
|
||||
DocWithRank: '' as Ref<Interface<DocWithRank>>
|
||||
},
|
||||
space: {
|
||||
Tx: '' as Ref<Space>,
|
||||
Model: '' as Ref<Space>
|
||||
|
@ -13,6 +13,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { LexoRank, LexoDecimal, LexoNumeralSystem36 } from 'lexorank'
|
||||
import LexoRankBucket from 'lexorank/lib/lexoRank/lexoRankBucket'
|
||||
|
||||
import type { Doc, Ref, Account } from './classes'
|
||||
|
||||
function toHex (value: number, chars: number): string {
|
||||
@ -59,3 +62,30 @@ export function getCurrentAccount (): Account { return currentAccount }
|
||||
export function setCurrentAccount (account: Account): void {
|
||||
currentAccount = account
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const genRanks = (count: number): Generator<string, void, unknown> =>
|
||||
(function * () {
|
||||
const sys = new LexoNumeralSystem36()
|
||||
const base = 36
|
||||
const max = base ** 6
|
||||
const gap = LexoDecimal.parse(Math.trunc(max / (count + 2)).toString(base), sys)
|
||||
let cur = LexoDecimal.parse('0', sys)
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
cur = cur.add(gap)
|
||||
yield new LexoRank(LexoRankBucket.BUCKET_0, cur).toString()
|
||||
}
|
||||
})()
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const calcRank = (prev?: { rank: string }, next?: { rank: string }): string => {
|
||||
const a = prev?.rank !== undefined ? LexoRank.parse(prev.rank) : LexoRank.min()
|
||||
const b = next?.rank !== undefined ? LexoRank.parse(next.rank) : LexoRank.max()
|
||||
|
||||
return a.between(b).toString()
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Contact } from '@anticrm/contact'
|
||||
import { Data, Ref, Space } from '@anticrm/core'
|
||||
import { calcRank, Data, Ref, SortingOrder, Space } from '@anticrm/core'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { OK, Status } from '@anticrm/platform'
|
||||
import { Card, getClient, UserBox } from '@anticrm/presentation'
|
||||
@ -52,6 +52,11 @@
|
||||
throw new Error('sequence object not found')
|
||||
}
|
||||
|
||||
const lastOne = await client.findOne(
|
||||
lead.class.Lead,
|
||||
{ state: state._id },
|
||||
{ sort: { rank: SortingOrder.Descending } }
|
||||
)
|
||||
const incResult = await client.updateDoc(
|
||||
task.class.Sequence,
|
||||
task.space.Sequence,
|
||||
@ -70,7 +75,8 @@
|
||||
customer: customer!,
|
||||
attachedTo: customer!,
|
||||
attachedToClass: contact.class.Contact,
|
||||
collection: 'leads'
|
||||
collection: 'leads',
|
||||
rank: calcRank(lastOne, undefined)
|
||||
}
|
||||
|
||||
await client.createDoc(lead.class.Lead, _space, value, leadId)
|
||||
|
@ -15,7 +15,7 @@
|
||||
//
|
||||
|
||||
import type { Contact } from '@anticrm/contact'
|
||||
import type { Class, Data, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { Class, Data, Doc, genRanks, 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'
|
||||
@ -71,46 +71,55 @@ export async function createKanban (
|
||||
{ color: '#F28469', name: 'Contract conclusion' },
|
||||
{ color: '#7C6FCD', name: 'Done' }
|
||||
]
|
||||
const ids: Array<Ref<State>> = []
|
||||
const stateRank = genRanks(states.length)
|
||||
for (const st of states) {
|
||||
const sid = (funnelId + '.state.' + st.name.toLowerCase().replace(' ', '_')) as Ref<State>
|
||||
const rank = stateRank.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
throw Error('Failed to generate rank')
|
||||
}
|
||||
|
||||
await factory(
|
||||
task.class.State,
|
||||
funnelId,
|
||||
{
|
||||
title: st.name,
|
||||
color: st.color
|
||||
color: st.color,
|
||||
rank
|
||||
},
|
||||
sid
|
||||
)
|
||||
ids.push(sid)
|
||||
}
|
||||
const rawDoneStates = [
|
||||
const doneStates = [
|
||||
{ class: task.class.WonState, title: 'Won' },
|
||||
{ class: task.class.LostState, title: 'Lost' }
|
||||
]
|
||||
const doneStates: Array<Ref<DoneState>> = []
|
||||
for (const st of rawDoneStates) {
|
||||
const doneStateRank = genRanks(doneStates.length)
|
||||
for (const st of doneStates) {
|
||||
const rank = doneStateRank.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
throw Error('Failed to generate rank')
|
||||
}
|
||||
|
||||
const sid = (funnelId + '.done-state.' + st.title.toLowerCase().replace(' ', '_')) as Ref<DoneState>
|
||||
await factory(
|
||||
st.class,
|
||||
funnelId,
|
||||
{
|
||||
title: st.title
|
||||
title: st.title,
|
||||
rank
|
||||
},
|
||||
sid
|
||||
)
|
||||
doneStates.push(sid)
|
||||
}
|
||||
|
||||
await factory(
|
||||
task.class.Kanban,
|
||||
funnelId,
|
||||
{
|
||||
attachedTo: funnelId,
|
||||
states: ids,
|
||||
doneStates,
|
||||
order: []
|
||||
attachedTo: funnelId
|
||||
},
|
||||
(funnelId + '.kanban') as Ref<Kanban>
|
||||
)
|
||||
|
@ -15,7 +15,8 @@
|
||||
<script lang="ts">
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import contact from '@anticrm/contact'
|
||||
import type { Ref, Space } from '@anticrm/core'
|
||||
import { Ref, SortingOrder, Space } from '@anticrm/core'
|
||||
import { calcRank } from '@anticrm/core'
|
||||
import { OK, Severity, Status } from '@anticrm/platform'
|
||||
import { Card, getClient, UserBox } from '@anticrm/presentation'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
@ -50,6 +51,12 @@
|
||||
if (sequence === undefined) {
|
||||
throw new Error('sequence object not found')
|
||||
}
|
||||
|
||||
const lastOne = await client.findOne(
|
||||
recruit.class.Applicant,
|
||||
{ state: state._id },
|
||||
{ sort: { rank: SortingOrder.Descending } }
|
||||
)
|
||||
const incResult = await client.updateDoc(
|
||||
task.class.Sequence,
|
||||
task.space.Sequence,
|
||||
@ -59,7 +66,7 @@
|
||||
},
|
||||
true
|
||||
)
|
||||
const id = await client.addCollection(
|
||||
await client.addCollection(
|
||||
recruit.class.Applicant,
|
||||
_space,
|
||||
candidate,
|
||||
@ -69,7 +76,8 @@
|
||||
state: state._id,
|
||||
doneState: null,
|
||||
number: incResult.object.sequence,
|
||||
assignee: assignee
|
||||
assignee: assignee,
|
||||
rank: calcRank(lastOne, undefined)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import core, { generateId } from '@anticrm/core'
|
||||
import core, { generateId, genRanks } from '@anticrm/core'
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import { AttributeEditor, createQuery, getClient } from '@anticrm/presentation'
|
||||
import { CircleButton, IconAdd, IconMoreH, Label, showPopup } from '@anticrm/ui'
|
||||
@ -52,23 +52,22 @@
|
||||
const space = folder._id
|
||||
|
||||
const template = await client.createDoc(task.class.KanbanTemplate, space, {
|
||||
states: [],
|
||||
doneStates: [],
|
||||
doneStatesC: 0,
|
||||
statesC: 0,
|
||||
title: 'New Template'
|
||||
})
|
||||
|
||||
const ranks = [...genRanks(2)]
|
||||
const doneStates = [
|
||||
{
|
||||
id: generateId<WonStateTemplate>(),
|
||||
class: task.class.WonStateTemplate,
|
||||
title: 'Won'
|
||||
title: 'Won',
|
||||
rank: ranks[0]
|
||||
},
|
||||
{
|
||||
id: generateId<LostStateTemplate>(),
|
||||
class: task.class.LostStateTemplate,
|
||||
title: 'Lost'
|
||||
title: 'Lost',
|
||||
rank: ranks[1]
|
||||
}
|
||||
]
|
||||
|
||||
@ -80,19 +79,11 @@
|
||||
task.class.KanbanTemplate,
|
||||
'doneStatesC',
|
||||
{
|
||||
title: ds.title
|
||||
},
|
||||
ds.id
|
||||
title: ds.title,
|
||||
rank: ds.rank
|
||||
}
|
||||
)
|
||||
}))
|
||||
|
||||
for (const ds of doneStates) {
|
||||
await client.updateDoc(task.class.KanbanTemplate, space, template, {
|
||||
$push: {
|
||||
doneStates: ds.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function select (item: KanbanTemplate) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import type { AttachedData, Data, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { AttachedData, calcRank, 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'
|
||||
@ -63,6 +63,11 @@
|
||||
throw new Error('sequence object not found')
|
||||
}
|
||||
|
||||
const lastOne = await client.findOne(
|
||||
task.class.Task,
|
||||
{ state: state._id },
|
||||
{ sort: { rank: SortingOrder.Descending } }
|
||||
)
|
||||
const incResult = await client.updateDoc(
|
||||
task.class.Sequence,
|
||||
task.space.Sequence,
|
||||
@ -79,7 +84,8 @@
|
||||
assignee,
|
||||
number: (incResult as any).object.sequence,
|
||||
doneState: null,
|
||||
state: state._id
|
||||
state: state._id,
|
||||
rank: calcRank(lastOne, undefined)
|
||||
}
|
||||
|
||||
await client.addCollection(task.class.Issue, _space, parent?._id ?? task.global.Task, parent?._class ?? task.class.Issue, 'tasks', value, taskId)
|
||||
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Ref, Doc } from '@anticrm/core'
|
||||
import { Ref, Doc, SortingOrder, calcRank } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { Kanban, State, DoneState } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
@ -33,45 +33,49 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
function sort <T extends Doc> (order: Ref<T>[], items: T[]): T[] {
|
||||
if (items.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const itemMap = new Map(items.map(x => [x._id, x]))
|
||||
const x = order
|
||||
.map(id => itemMap.get(id))
|
||||
.filter((x): x is T => x !== undefined)
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
const statesQ = createQuery()
|
||||
$: statesQ.query(task.class.State, { _id: { $in: kanban.states ?? [] } }, result => { states = sort(kanban.states, result) })
|
||||
$: statesQ.query(task.class.State, { space: kanban.space }, result => { states = result}, {
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
})
|
||||
|
||||
const doneStatesQ = createQuery()
|
||||
$: doneStatesQ.query(task.class.DoneState, { _id: { $in: kanban.doneStates } }, (result) => { doneStates = sort(kanban.doneStates, result) })
|
||||
$: doneStatesQ.query(task.class.DoneState, { space: kanban.space }, (result) => { doneStates = result }, {
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
})
|
||||
|
||||
async function onMove ({ detail: { stateID, position } }: { detail: { stateID: Ref<State>, position: number } }) {
|
||||
client.updateDoc(kanban._class, kanban.space, kanban._id, {
|
||||
$move: {
|
||||
states: {
|
||||
$value: stateID,
|
||||
$position: position
|
||||
}
|
||||
const [prev, next] = [states[position - 1], states[position + 1]]
|
||||
const state = states.find((x) => x._id === stateID)
|
||||
|
||||
if (state === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.updateDoc(
|
||||
state._class,
|
||||
state.space,
|
||||
state._id,
|
||||
{
|
||||
rank: calcRank(prev, next)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async function onAdd () {
|
||||
const state = await client.createDoc(task.class.State, kanban.space, {
|
||||
const lastOne = await client.findOne(
|
||||
task.class.State,
|
||||
{ space: kanban.space },
|
||||
{ sort: { rank: SortingOrder.Descending } }
|
||||
)
|
||||
|
||||
await client.createDoc(task.class.State, kanban.space, {
|
||||
title: 'New State',
|
||||
color: '#7C6FCD'
|
||||
})
|
||||
await client.updateDoc(kanban._class, kanban.space, kanban._id, {
|
||||
$push: {
|
||||
states: state
|
||||
}
|
||||
color: '#7C6FCD',
|
||||
rank: calcRank(lastOne, undefined)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Ref, Space, Doc, generateId } from '@anticrm/core'
|
||||
import { Ref, Space, SortingOrder, calcRank } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { State, DoneStateTemplate, KanbanTemplate, StateTemplate } from '@anticrm/task'
|
||||
@ -35,42 +35,49 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
function sort <T extends Doc>(order: Ref<T>[], items: T[]): T[] {
|
||||
if (items.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const itemMap = new Map(items.map(x => [x._id, x]))
|
||||
const x = order
|
||||
.map(id => itemMap.get(id))
|
||||
.filter((x): x is T => x !== undefined)
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
const statesQ = createQuery()
|
||||
$: statesQ.query(task.class.StateTemplate, { attachedTo: kanban._id }, result => { states = sort(kanban.states, result) })
|
||||
$: statesQ.query(task.class.StateTemplate, { attachedTo: kanban._id }, result => { states = result }, {
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
})
|
||||
|
||||
const doneStatesQ = createQuery()
|
||||
$: doneStatesQ.query(task.class.DoneStateTemplate, { attachedTo: kanban._id }, (result) => { doneStates = sort(kanban.doneStates, result) })
|
||||
$: doneStatesQ.query(task.class.DoneStateTemplate, { attachedTo: kanban._id }, (result) => { doneStates = result }, {
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
})
|
||||
|
||||
let space: Space | undefined
|
||||
const spaceQ = createQuery()
|
||||
$: spaceQ.query(core.class.Space, { _id: kanban.space }, (result) => { space = result[0] })
|
||||
|
||||
async function onMove ({ detail: { stateID, position } }: { detail: { stateID: Ref<StateTemplate>, position: number } }) {
|
||||
client.updateDoc(kanban._class, kanban.space, kanban._id, {
|
||||
$move: {
|
||||
states: {
|
||||
$value: stateID,
|
||||
$position: position
|
||||
}
|
||||
const [prev, next] = [states[position - 1], states[position + 1]]
|
||||
const state = states.find((x) => x._id === stateID)
|
||||
|
||||
if (state === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.updateDoc(
|
||||
state._class,
|
||||
state.space,
|
||||
state._id,
|
||||
{
|
||||
rank: calcRank(prev, next)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async function onAdd () {
|
||||
const stateID = generateId<StateTemplate>()
|
||||
const lastOne = await client.findOne(
|
||||
task.class.StateTemplate,
|
||||
{ attachedTo: kanban._id },
|
||||
{ sort: { rank: SortingOrder.Descending } }
|
||||
)
|
||||
|
||||
await client.addCollection(
|
||||
task.class.StateTemplate,
|
||||
kanban.space,
|
||||
@ -79,16 +86,10 @@
|
||||
'statesC',
|
||||
{
|
||||
title: 'New State',
|
||||
color: '#7C6FCD'
|
||||
},
|
||||
stateID
|
||||
)
|
||||
|
||||
await client.updateDoc(kanban._class, kanban.space, kanban._id, {
|
||||
$push: {
|
||||
states: stateID
|
||||
color: '#7C6FCD',
|
||||
rank: calcRank(lastOne, undefined)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function onDelete ({ detail: { state } }: { detail: { state: State }}) {
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { AttachedDoc, Class, Doc, FindOptions, Ref } from '@anticrm/core'
|
||||
import { AttachedDoc, calcRank, Class, Doc, DocumentUpdate, DocWithRank, FindOptions, Ref, SortingOrder } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
@ -26,7 +26,7 @@
|
||||
import KanbanPanel from './KanbanPanel.svelte'
|
||||
// import KanbanPanelEmpty from './KanbanPanelEmpty.svelte'
|
||||
|
||||
type Item = Doc & { state: Ref<State>, doneState: Ref<DoneState> | null }
|
||||
type Item = DocWithRank & { state: Ref<State>, doneState: Ref<DoneState> | null }
|
||||
|
||||
export let _class: Ref<Class<Item>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
@ -36,50 +36,38 @@
|
||||
|
||||
let kanban: Kanban
|
||||
let states: State[] = []
|
||||
let rawStates: State[] = []
|
||||
|
||||
let objects: (Item | undefined)[] = []
|
||||
let rawObjects: Item[] = []
|
||||
let kanbanStates: Ref<State>[] = []
|
||||
let kanbanDoneStates: Ref<DoneState>[] = []
|
||||
let objects: Item[] = []
|
||||
let wonState: WonState | undefined
|
||||
let lostState: LostState | undefined
|
||||
|
||||
const kanbanQuery = createQuery()
|
||||
$: kanbanQuery.query(task.class.Kanban, { attachedTo: space }, result => { kanban = result[0] })
|
||||
|
||||
$: kanbanStates = kanban?.states ?? []
|
||||
$: kanbanDoneStates = kanban?.doneStates ?? []
|
||||
|
||||
function sort (kanban: Kanban, states: State[]): State[] {
|
||||
if (kanban === undefined || states.length === 0) { return [] }
|
||||
const map = states.reduce((map, state) => { map.set(state._id, state); return map }, new Map<Ref<State>, State>())
|
||||
return kanban.states.map(id => map.get(id) as State)
|
||||
}
|
||||
|
||||
function sortObjects<T extends Doc> (kanban: Kanban, objects: T[]): (T | undefined)[] {
|
||||
if (kanban === undefined || objects.length === 0) { return [] }
|
||||
const map = objects.reduce((map, doc) => { map.set(doc._id, doc); return map }, new Map<Ref<T>, T>())
|
||||
return kanban.order
|
||||
.map(id => map.get(id as Ref<T>))
|
||||
}
|
||||
|
||||
const statesQuery = createQuery()
|
||||
$: if (kanbanStates.length > 0) statesQuery.query(task.class.State, { _id: { $in: kanbanStates } }, result => { rawStates = result })
|
||||
$: states = sort(kanban, rawStates)
|
||||
$: if (kanban !== undefined) {
|
||||
statesQuery.query(task.class.State, { space: kanban.space }, result => { states = result }, {
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const doneStatesQ = createQuery()
|
||||
$: if (kanbanDoneStates.length > 0) {
|
||||
doneStatesQ.query(task.class.DoneState, { _id: { $in: kanbanDoneStates } }, (result) => {
|
||||
$: if (kanban !== undefined) {
|
||||
doneStatesQ.query(task.class.DoneState, { space: kanban.space }, (result) => {
|
||||
wonState = result.find((x) => x._class === task.class.WonState)
|
||||
lostState = result.find((x) => x._class === task.class.LostState)
|
||||
})
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(_class, { space, doneState: null }, result => { rawObjects = result }, options)
|
||||
|
||||
$: objects = sortObjects(kanban, rawObjects)
|
||||
$: query.query(_class, { space, doneState: null }, result => { objects = result }, {
|
||||
...options,
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
},
|
||||
})
|
||||
|
||||
function dragover (ev: MouseEvent, object: Item) {
|
||||
if (dragCard !== object) {
|
||||
@ -89,28 +77,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function updateItem (item: Item, update: DocumentUpdate<Item>) {
|
||||
if (client.getHierarchy().isDerived(_class, core.class.AttachedDoc)) {
|
||||
const adoc: AttachedDoc = item as Doc as AttachedDoc
|
||||
await client.updateCollection(
|
||||
_class,
|
||||
space,
|
||||
adoc._id as Ref<Doc> as Ref<AttachedDoc>,
|
||||
adoc.attachedTo,
|
||||
adoc.attachedToClass,
|
||||
adoc.collection,
|
||||
update
|
||||
)
|
||||
} else {
|
||||
await client.updateDoc(item._class, item.space, item._id, update)
|
||||
}
|
||||
}
|
||||
|
||||
async function move (state: Ref<State>) {
|
||||
const id = dragCard._id
|
||||
let updates: DocumentUpdate<Item> = {}
|
||||
|
||||
if (dragCardInitialState !== state) {
|
||||
if (client.getHierarchy().isDerived(_class, core.class.AttachedDoc)) {
|
||||
const adoc: AttachedDoc = dragCard as Doc as AttachedDoc
|
||||
// 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<Task>(_class, space, id as Ref<Task>, { state })
|
||||
updates = {
|
||||
...updates,
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
if (dragCardInitialPosition !== dragCardEndPosition) {
|
||||
client.updateDoc(task.class.Kanban, space, kanban._id, {
|
||||
$move: {
|
||||
order: {
|
||||
$value: id,
|
||||
$position: dragCardEndPosition
|
||||
}
|
||||
}
|
||||
})
|
||||
const [prev, next] = [objects[dragCardEndPosition - 1], objects[dragCardEndPosition + 1]]
|
||||
|
||||
updates = {
|
||||
...updates,
|
||||
rank: calcRank(prev, next)
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await updateItem(dragCard, updates)
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,25 +132,9 @@
|
||||
}
|
||||
|
||||
const onDone = (state: DoneState) => async () => {
|
||||
if (client.getHierarchy().isDerived(_class, core.class.AttachedDoc)) {
|
||||
const adoc: AttachedDoc = dragCard as Doc as AttachedDoc
|
||||
await client.updateCollection<Doc, Task>(
|
||||
_class,
|
||||
space,
|
||||
adoc._id as Ref<Task>,
|
||||
adoc.attachedTo,
|
||||
adoc.attachedToClass,
|
||||
adoc.collection,
|
||||
{ doneState: state._id }
|
||||
)
|
||||
} else {
|
||||
await client.updateDoc(dragCard._class, dragCard.space, dragCard._id, {
|
||||
doneState: state._id
|
||||
})
|
||||
}
|
||||
|
||||
isDragging = false
|
||||
hoveredDoneState = undefined
|
||||
await updateItem(dragCard, { doneState: state._id })
|
||||
}
|
||||
|
||||
let isDragging = false
|
||||
@ -174,7 +162,7 @@
|
||||
}}>
|
||||
<!-- <KanbanCardEmpty label={'Create new application'} /> -->
|
||||
{#each objects as object, j (object)}
|
||||
{#if object !== undefined && object.state === state._id}
|
||||
{#if object.state === state._id}
|
||||
<div
|
||||
class="step-tb75"
|
||||
on:dragover|preventDefault={(ev) => {
|
||||
@ -226,11 +214,9 @@
|
||||
class="flex-grow flex-center done-item"
|
||||
class:hovered={hoveredDoneState === lostState._id}
|
||||
on:dragenter={() => {
|
||||
console.log('enter')
|
||||
hoveredDoneState = lostState?._id
|
||||
}}
|
||||
on:dragleave={() => {
|
||||
console.log('leave')
|
||||
hoveredDoneState = undefined
|
||||
}}
|
||||
on:dragover|preventDefault={() => {}}
|
||||
|
@ -14,8 +14,7 @@
|
||||
//
|
||||
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import type { AttachedDoc, Class, Client, Data, Doc, Mixin, Ref, Space, TxOperations } from '@anticrm/core'
|
||||
import { Arr } from '@anticrm/core'
|
||||
import { AttachedDoc, Class, Client, Data, Doc, DocWithRank, genRanks, Mixin, Ref, Space, TxOperations } from '@anticrm/core'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
@ -26,7 +25,7 @@ import { ViewletDescriptor } from '@anticrm/view'
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface State extends Doc {
|
||||
export interface State extends DocWithRank {
|
||||
title: string
|
||||
color: string
|
||||
}
|
||||
@ -34,7 +33,7 @@ export interface State extends Doc {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DoneState extends Doc {
|
||||
export interface DoneState extends DocWithRank {
|
||||
title: string
|
||||
}
|
||||
|
||||
@ -51,7 +50,7 @@ export interface LostState extends DoneState {}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Task extends AttachedDoc {
|
||||
export interface Task extends AttachedDoc, DocWithRank {
|
||||
state: Ref<State>
|
||||
doneState: Ref<DoneState> | null
|
||||
number: number
|
||||
@ -95,9 +94,6 @@ export interface KanbanCard extends Class<Doc> {
|
||||
*/
|
||||
export interface Kanban extends Doc {
|
||||
attachedTo: Ref<Space>
|
||||
states: Arr<Ref<State>>
|
||||
doneStates: Arr<Ref<DoneState>>
|
||||
order: Arr<Ref<Doc>>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,8 +129,6 @@ export interface LostStateTemplate extends DoneStateTemplate, LostState {}
|
||||
*/
|
||||
export interface KanbanTemplate extends Doc {
|
||||
title: string
|
||||
states: Arr<Ref<StateTemplate>>
|
||||
doneStates: Arr<Ref<DoneStateTemplate>>
|
||||
statesC: number
|
||||
doneStatesC: number
|
||||
}
|
||||
@ -209,47 +203,56 @@ export async function createProjectKanban (
|
||||
{ color: '#A5D179', name: 'Done' },
|
||||
{ color: '#F28469', name: 'Invalid' }
|
||||
]
|
||||
const ids: Array<Ref<State>> = []
|
||||
const stateRank = genRanks(states.length)
|
||||
for (const st of states) {
|
||||
const rank = stateRank.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
throw Error('Failed to generate rank')
|
||||
}
|
||||
|
||||
const sid = (projectId + '.state.' + st.name.toLowerCase().replace(' ', '_')) as Ref<State>
|
||||
await factory(
|
||||
task.class.State,
|
||||
projectId,
|
||||
{
|
||||
title: st.name,
|
||||
color: st.color
|
||||
color: st.color,
|
||||
rank
|
||||
},
|
||||
sid
|
||||
)
|
||||
ids.push(sid)
|
||||
}
|
||||
|
||||
const rawDoneStates = [
|
||||
const doneStates = [
|
||||
{ class: task.class.WonState, title: 'Won' },
|
||||
{ class: task.class.LostState, title: 'Lost' }
|
||||
]
|
||||
const doneStates: Array<Ref<DoneState>> = []
|
||||
for (const st of rawDoneStates) {
|
||||
const doneStateRank = genRanks(doneStates.length)
|
||||
for (const st of doneStates) {
|
||||
const rank = doneStateRank.next().value
|
||||
|
||||
if (rank === undefined) {
|
||||
throw Error('Failed to generate rank')
|
||||
}
|
||||
|
||||
const sid = (projectId + '.done-state.' + st.title.toLowerCase().replace(' ', '_')) as Ref<DoneState>
|
||||
await factory(
|
||||
st.class,
|
||||
projectId,
|
||||
{
|
||||
title: st.title
|
||||
title: st.title,
|
||||
rank
|
||||
},
|
||||
sid
|
||||
)
|
||||
doneStates.push(sid)
|
||||
}
|
||||
|
||||
await factory(
|
||||
task.class.Kanban,
|
||||
projectId,
|
||||
{
|
||||
attachedTo: projectId,
|
||||
states: ids,
|
||||
doneStates,
|
||||
order: []
|
||||
attachedTo: projectId
|
||||
},
|
||||
(projectId + '.kanban') as Ref<Kanban>
|
||||
)
|
||||
@ -260,18 +263,19 @@ export async function createProjectKanban (
|
||||
*/
|
||||
export async function createKanban (client: Client & TxOperations, attachedTo: Ref<Space>, templateId?: Ref<KanbanTemplate>): Promise<Ref<Kanban>> {
|
||||
if (templateId === undefined) {
|
||||
const ranks = [...genRanks(2)]
|
||||
await Promise.all([
|
||||
client.createDoc(task.class.WonState, attachedTo, {
|
||||
title: 'Won',
|
||||
rank: ranks[0]
|
||||
}),
|
||||
client.createDoc(task.class.LostState, attachedTo, {
|
||||
title: 'Lost',
|
||||
rank: ranks[1]
|
||||
})
|
||||
])
|
||||
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: []
|
||||
attachedTo
|
||||
})
|
||||
}
|
||||
|
||||
@ -282,37 +286,34 @@ export async function createKanban (client: Client & TxOperations, attachedTo: R
|
||||
}
|
||||
|
||||
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 }))
|
||||
)
|
||||
await Promise.all(
|
||||
tmplStates.map(async (state) => await client.createDoc(
|
||||
task.class.State,
|
||||
attachedTo,
|
||||
{
|
||||
color: state.color,
|
||||
title: state.title,
|
||||
rank: state.rank
|
||||
})))
|
||||
|
||||
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)
|
||||
await Promise.all(
|
||||
tmplDoneStates.map(async (state) => {
|
||||
const cl = doneClassMap.get(state._class)
|
||||
|
||||
if (cl === undefined) {
|
||||
return
|
||||
}
|
||||
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(cl, attachedTo, { title: state.title, rank: state.rank })
|
||||
})
|
||||
)
|
||||
|
||||
return await client.createDoc(task.class.Kanban, attachedTo, {
|
||||
attachedTo,
|
||||
states,
|
||||
doneStates,
|
||||
order: []
|
||||
attachedTo
|
||||
})
|
||||
}
|
||||
|
15
rush.json
15
rush.json
@ -736,21 +736,6 @@
|
||||
"projectFolder": "server/recruit-resources",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server-task-resources",
|
||||
"projectFolder": "server/task-resources",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server-task",
|
||||
"projectFolder": "server/task",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/model-server-task",
|
||||
"projectFolder": "models/server-task",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/mongo",
|
||||
"projectFolder": "server/mongo",
|
||||
|
@ -43,8 +43,6 @@
|
||||
"@anticrm/server-recruit": "~0.6.0",
|
||||
"@anticrm/server-recruit-resources": "~0.6.0",
|
||||
"@anticrm/mongo": "~0.6.1",
|
||||
"@anticrm/elastic": "~0.6.0",
|
||||
"@anticrm/server-task": "~0.6.0",
|
||||
"@anticrm/server-task-resources": "~0.6.0"
|
||||
"@anticrm/elastic": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ import type { DbConfiguration, DbAdapter } from '@anticrm/server-core'
|
||||
import { addLocation } from '@anticrm/platform'
|
||||
import { serverChunterId } from '@anticrm/server-chunter'
|
||||
import { serverRecruitId } from '@anticrm/server-recruit'
|
||||
import { serverViewId } from '@anticrm/server-task'
|
||||
|
||||
class NullDbAdapter implements DbAdapter {
|
||||
async init (model: Tx[]): Promise<void> {}
|
||||
@ -42,7 +41,6 @@ async function createNullAdapter (hierarchy: Hierarchy, url: string, db: string,
|
||||
export function start (dbUrl: string, fullTextUrl: string, port: number, host?: string): () => void {
|
||||
addLocation(serverChunterId, () => import('@anticrm/server-chunter-resources'))
|
||||
addLocation(serverRecruitId, () => import('@anticrm/server-recruit-resources'))
|
||||
addLocation(serverViewId, () => import('@anticrm/server-task-resources'))
|
||||
|
||||
return startJsonRpc((workspace: string) => {
|
||||
const conf: DbConfiguration = {
|
||||
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
@ -1,18 +0,0 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "@anticrm/server-task-resources",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.41.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.12",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/server-core": "~0.6.1",
|
||||
"@anticrm/task": "~0.6.0"
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Tx, TxFactory, Doc, TxCreateDoc, TxRemoveDoc, TxCollectionCUD, AttachedDoc } from '@anticrm/core'
|
||||
import type { FindAll } from '@anticrm/server-core'
|
||||
|
||||
import core, { Hierarchy } from '@anticrm/core'
|
||||
import task, { Kanban, Task, State } from '@anticrm/task'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnTask (tx: Tx, txFactory: TxFactory, findAll: FindAll<Doc>, hierarchy: Hierarchy): Promise<Tx[]> {
|
||||
if (tx._class === core.class.TxCollectionCUD) {
|
||||
tx = (tx as TxCollectionCUD<Doc, AttachedDoc>).tx
|
||||
}
|
||||
|
||||
if (hierarchy.isDerived(tx._class, core.class.TxCreateDoc)) {
|
||||
const createTx = tx as TxCreateDoc<Task>
|
||||
if (hierarchy.isDerived(createTx.objectClass, task.class.Task)) {
|
||||
const state = (await (findAll as FindAll<State>)(task.class.State, { space: createTx.objectSpace }))[0] // TODO: make FindAll generic
|
||||
if (state === undefined) {
|
||||
throw new Error('OnTask: state not found')
|
||||
}
|
||||
const kanban = (await (findAll as FindAll<Kanban>)(task.class.Kanban, { attachedTo: createTx.objectSpace }))[0]
|
||||
if (kanban === undefined) {
|
||||
throw new Error('OnTask: kanban not found')
|
||||
}
|
||||
return [
|
||||
txFactory.createTxUpdateDoc(createTx.objectClass, createTx.objectSpace, createTx.objectId, { state: state._id }),
|
||||
txFactory.createTxUpdateDoc(task.class.Kanban, createTx.objectSpace, kanban._id, { $push: { order: createTx.objectId } })
|
||||
]
|
||||
}
|
||||
} else if (tx._class === core.class.TxRemoveDoc) {
|
||||
const removeTx = tx as TxRemoveDoc<Task>
|
||||
if (hierarchy.isDerived(removeTx.objectClass, task.class.Task)) {
|
||||
const kanban = (await (findAll as FindAll<Kanban>)(task.class.Kanban, { attachedTo: removeTx.objectSpace }))[0]
|
||||
if (kanban === undefined) {
|
||||
throw new Error('OnTask: kanban not found')
|
||||
}
|
||||
return [
|
||||
txFactory.createTxUpdateDoc(task.class.Kanban, removeTx.objectSpace, kanban._id, { $pull: { order: removeTx.objectId } })
|
||||
]
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default async () => ({
|
||||
trigger: {
|
||||
OnTask
|
||||
}
|
||||
})
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib"
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
@ -1,18 +0,0 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "@anticrm/server-task",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@types/node": "^16.4.10",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.41.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/server-core": "~0.6.0"
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Resource, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { TriggerFunc } from '@anticrm/server-core'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const serverViewId = 'server-task' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export default plugin(serverViewId, {
|
||||
trigger: {
|
||||
OnTask: '' as Resource<TriggerFunc>
|
||||
}
|
||||
})
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user