diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 7f4ccdf779..4ab346c3ec 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -12518,11 +12518,12 @@ packages: dev: false file:projects/model-recruit.tgz_typescript@4.5.4: - resolution: {integrity: sha512-8jDIXUx3KFtzeCO7AChSlND5+RBTLNG83eAO8syMdJVAmOA5UpEhFQvpPZCRLhYEzj3VG2zgJg7USd+CZKEPhA==, tarball: file:projects/model-recruit.tgz} + resolution: {integrity: sha512-e8rfK0JjHSG0Zy0OzesYS38vL7VPzaKOUx5FC9PjeckdklAGYFbPUc1PZnLkeisogVcd3tDR7UkjIL0GvohJ+w==, tarball: file:projects/model-recruit.tgz} id: file:projects/model-recruit.tgz name: '@rush-temp/model-recruit' version: 0.0.0 dependencies: + '@anticrm/skillset': 0.6.0 '@rushstack/heft': 0.41.8 '@types/heft-jest': 1.0.2 '@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237 @@ -12768,7 +12769,7 @@ packages: dev: false file:projects/model-tags.tgz_typescript@4.5.4: - resolution: {integrity: sha512-M9oDcJc4rUtXqhC4FcPW+Z62RTGfstzLFi6DnorfVhtEANY/2VTUPiyjwLM+ml6sCHVdG49kaAx1gLHyEbAF7Q==, tarball: file:projects/model-tags.tgz} + resolution: {integrity: sha512-M+xHdfGituHUoH7YgkHc5lw2TXsvV3jvf3g73EpjJgGl4NFclNd1VkBtyEQ9Czsjy3N/jff1B2FePFu7u2p6FA==, tarball: file:projects/model-tags.tgz} id: file:projects/model-tags.tgz name: '@rush-temp/model-tags' version: 0.0.0 diff --git a/models/recruit/package.json b/models/recruit/package.json index 4067137c4e..4f1b44b3b8 100644 --- a/models/recruit/package.json +++ b/models/recruit/package.json @@ -44,6 +44,7 @@ "@anticrm/model-task": "~0.6.0", "@anticrm/workbench": "~0.6.1", "@anticrm/model-presentation": "~0.6.0", - "@anticrm/model-tags": "~0.6.0" + "@anticrm/model-tags": "~0.6.0", + "@anticrm/skillset": "^0.6.0" } } diff --git a/models/recruit/src/migration.ts b/models/recruit/src/migration.ts index c94d61b8b4..1217608846 100644 --- a/models/recruit/src/migration.ts +++ b/models/recruit/src/migration.ts @@ -14,13 +14,16 @@ // import { Person } from '@anticrm/contact' -import core, { AttachedDoc, Class, Doc, DOMAIN_TX, MixinData, Ref, TxCollectionCUD, TxCreateDoc, TxMixin, TxUpdateDoc } from '@anticrm/core' -import { MigrateOperation, MigrationClient, MigrationResult, MigrationUpgradeClient } from '@anticrm/model' +import core, { AttachedDoc, Class, Doc, DocumentQuery, DOMAIN_TX, MixinData, Ref, TxCollectionCUD, TxCreateDoc, TxMixin, TxOperations, TxUpdateDoc } from '@anticrm/core' +import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model' import { DOMAIN_ATTACHMENT } from '@anticrm/model-attachment' import { DOMAIN_COMMENT } from '@anticrm/model-chunter' import contact, { DOMAIN_CONTACT } from '@anticrm/model-contact' +import tags, { DOMAIN_TAGS, TagCategory, TagElement } from '@anticrm/model-tags' import { DOMAIN_TASK } from '@anticrm/model-task' -import recruit, { Candidate } from '@anticrm/recruit' +import { Candidate } from '@anticrm/recruit' +import recruit from './plugin' +import { getCategories } from '@anticrm/skillset' function toCandidateData (c: Pick | undefined): MixinData { if (c === undefined) { @@ -37,12 +40,15 @@ function toCandidateData (c: Pick return result } -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function logInfo (msg: string, result: MigrationResult): void { - if (result.updated > 0) { - console.log(`Recruit: Migrate ${msg} ${result.updated}`) +function findTagCategory (title: string, categories: TagCategory[]): Ref { + for (const c of categories) { + if (c.tags.findIndex((it) => it.toLowerCase() === title.toLowerCase()) !== -1) { + return c._id + } } + return recruit.category.Other } + export const recruitOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { // Move all candidates to mixins. @@ -128,8 +134,88 @@ export const recruitOperation: MigrateOperation = { }, { objectClass: contact.class.Person }) + + // Rename other + const categories = await client.find(DOMAIN_TAGS, { _class: tags.class.TagCategory }) + let prefix = 'tags:category:Category' + for (const c of categories) { + if (c._id.startsWith(prefix) || c._id === 'tags:category:Other') { + let newCID = c._id.replace(prefix, recruit.category.Category + '.') as Ref + if (c._id === 'tags:category:Other') { + newCID = recruit.category.Other + } + await client.delete(DOMAIN_TAGS, c._id) + await client.create(DOMAIN_TAGS, { ...c, _id: newCID, targetClass: recruit.mixin.Candidate }) + await client.update(DOMAIN_TAGS, { _class: tags.class.TagElement, category: c._id }, { + category: newCID, + targetClass: recruit.mixin.Candidate + }) + } + } + + prefix = 'recruit:category:Category' + for (const c of categories) { + if ((c._id.startsWith(prefix) && !c._id.startsWith(prefix + '.')) || c._id === 'tags:category:Other') { + let newCID = c._id.replace(prefix, recruit.category.Category + '.') as Ref + if (c._id === 'tags:category:Other') { + newCID = recruit.category.Other + } + await client.delete(DOMAIN_TAGS, c._id) + try { + await client.create(DOMAIN_TAGS, { ...c, _id: newCID, targetClass: recruit.mixin.Candidate }) + } catch (err: any) { + // Ignore + } + await client.update(DOMAIN_TAGS, { _class: tags.class.TagElement, category: c._id }, { + category: newCID, + targetClass: recruit.mixin.Candidate + }) + } + } }, - async upgrade (client: MigrationUpgradeClient): Promise {} + async upgrade (client: MigrationUpgradeClient): Promise { + const tx = new TxOperations(client, core.account.System) + + await createOrUpdate( + tx, + tags.class.TagCategory, + tags.space.Tags, + { + icon: tags.icon.Tags, + label: 'Other', + targetClass: recruit.mixin.Candidate, + tags: [], + default: true + }, + recruit.category.Other + ) + + for (const c of getCategories()) { + await createOrUpdate( + tx, + tags.class.TagCategory, + tags.space.Tags, + { + icon: tags.icon.Tags, + label: c.label, + targetClass: recruit.mixin.Candidate, + tags: c.skills, + default: false + }, + (recruit.category.Category + '.' + c.id) as Ref + ) + } + + const categories = await tx.findAll(tags.class.TagCategory, { targetClass: recruit.mixin.Candidate }) + // Find all existing TagElement and update category based on skillset + const tagElements = await tx.findAll(tags.class.TagElement, { category: null } as unknown as DocumentQuery) + for (const t of tagElements) { + if (t.category == null) { + const category = findTagCategory(t.title, categories) + await tx.update(t, { category: category }) + } + } + } } async function migrateUpdateCandidateToPersonAndMixin (client: MigrationClient): Promise { const updateCandidates = await client.find(DOMAIN_TX, { diff --git a/models/tags/package.json b/models/tags/package.json index 407b2403c9..11e2947736 100644 --- a/models/tags/package.json +++ b/models/tags/package.json @@ -33,7 +33,6 @@ "@anticrm/tags-resources": "~0.6.0", "@anticrm/ui": "~0.6.0", "@anticrm/model-core": "~0.6.0", - "@anticrm/model-view": "~0.6.0", - "@anticrm/skillset": "^0.6.0" + "@anticrm/model-view": "~0.6.0" } } diff --git a/models/tags/src/index.ts b/models/tags/src/index.ts index ac130c0cf9..bb4ca99d1f 100644 --- a/models/tags/src/index.ts +++ b/models/tags/src/index.ts @@ -22,6 +22,7 @@ import tags from './plugin' export { tagsOperation } from './migration' export { tags as default } +export { TagElement, TagReference, TagCategory } from '@anticrm/tags' export const DOMAIN_TAGS = 'tags' as Domain @@ -75,6 +76,9 @@ export class TTagCategory extends TDoc implements TagCategory { @Prop(Collection(core.class.TypeString), tags.string.CategoryTagsLabel) tags!: string[] + + @Prop(TypeString(), tags.string.DefaultLabel) + default!: boolean } export function createModel (builder: Builder): void { diff --git a/models/tags/src/migration.ts b/models/tags/src/migration.ts index 2e481ee57c..152318d857 100644 --- a/models/tags/src/migration.ts +++ b/models/tags/src/migration.ts @@ -1,50 +1,7 @@ -import core, { DocumentQuery, Ref, TxOperations } from '@anticrm/core' -import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model' -import { getCategories } from '@anticrm/skillset' -import { findTagCategory, TagCategory, TagElement } from '@anticrm/tags' -import tags from './plugin' +import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model' export const tagsOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise {}, async upgrade (client: MigrationUpgradeClient): Promise { - const tx = new TxOperations(client, core.account.System) - - await createOrUpdate( - tx, - tags.class.TagCategory, - tags.space.Tags, - { - icon: tags.icon.Tags, - label: 'Other', - targetClass: core.class.Doc, - tags: [''] - }, - tags.category.Other - ) - - for (const c of getCategories()) { - await createOrUpdate( - tx, - tags.class.TagCategory, - tags.space.Tags, - { - icon: tags.icon.Tags, - label: c.label, - targetClass: core.class.Doc, - tags: c.skills - }, - (tags.category.Category + c.id) as Ref - ) - } - - const categories = await tx.findAll(tags.class.TagCategory, {}) - // Find all existing TagElement and update category based on skillset - const tagElements = await tx.findAll(tags.class.TagElement, { category: null } as unknown as DocumentQuery) - for (const t of tagElements) { - if (t.category == null) { - const category = findTagCategory(t.title, categories) - await tx.update(t, { category: category }) - } - } } } diff --git a/models/tags/src/plugin.ts b/models/tags/src/plugin.ts index 58ed4ca8a1..632e7002a1 100644 --- a/models/tags/src/plugin.ts +++ b/models/tags/src/plugin.ts @@ -39,7 +39,8 @@ export default mergeIds(tagsId, tags, { TagReference: '' as IntlString, AssetLabel: '' as IntlString, CategoryTargetClass: '' as IntlString, - CategoryTagsLabel: '' as IntlString + CategoryTagsLabel: '' as IntlString, + DefaultLabel: '' as IntlString }, dd: { DeleteTagElement: '' as Resource<(doc: Doc, client: TxOperations) => Promise> diff --git a/packages/model/src/utils.ts b/packages/model/src/utils.ts index febc5ebf3b..ebdda7bc5c 100644 --- a/packages/model/src/utils.ts +++ b/packages/model/src/utils.ts @@ -28,15 +28,18 @@ function diffAttributes (doc: Data, newDoc: Data): DocumentUpdate } /** + * Create or update document if modified only by system account. * @public */ export async function createOrUpdate (client: TxOperations, _class: Ref>, space: Ref, data: Data, _id: Ref): Promise { const existingDoc = await client.findOne(_class, { _id }) if (existingDoc !== undefined) { const { _class: _oldClass, _id, space: _oldSpace, modifiedBy, modifiedOn, ...oldData } = existingDoc - const updateOp = diffAttributes(oldData, data) - if (Object.keys(updateOp).length > 0) { - await client.update(existingDoc, updateOp) + if (modifiedBy === client.txFactory.account) { + const updateOp = diffAttributes(oldData, data) + if (Object.keys(updateOp).length > 0) { + await client.update(existingDoc, updateOp) + } } } else { await client.createDoc(_class, space, data, _id) diff --git a/plugins/recruit-resources/src/components/Candidates.svelte b/plugins/recruit-resources/src/components/Candidates.svelte index 3bc24d0750..61a5b9d9b2 100644 --- a/plugins/recruit-resources/src/components/Candidates.svelte +++ b/plugins/recruit-resources/src/components/Candidates.svelte @@ -55,7 +55,6 @@ $: updateResultQuery(search, documentIds) function updateCategory (detail: {category: Ref | null, elements: TagElement[] }) { - console.log(detail) category = detail.category ?? undefined selectedTagElements.set(Array.from(detail.elements ?? []).map(it => it._id)) } diff --git a/plugins/recruit-resources/src/components/CreateCandidate.svelte b/plugins/recruit-resources/src/components/CreateCandidate.svelte index 79898d076c..f8d2341a00 100644 --- a/plugins/recruit-resources/src/components/CreateCandidate.svelte +++ b/plugins/recruit-resources/src/components/CreateCandidate.svelte @@ -154,7 +154,7 @@ ) } - const categories = await client.findAll(tags.class.TagCategory, {}) + const categories = await client.findAll(tags.class.TagCategory, { targetClass: recruit.mixin.Candidate }) // Tag elements const skillTagElements = new Map((await client.findAll(tags.class.TagElement, { _id: { $in: skills.map(it => it.tag) } })).map(it => ([it._id, it]))) for (const skill of skills) { @@ -246,7 +246,7 @@ // Create skills await elementsPromise - const categories = await client.findAll(tags.class.TagCategory, {}) + const categories = await client.findAll(tags.class.TagCategory, { targetClass: recruit.mixin.Candidate }) const categoriesMap = new Map(Array.from(categories.map(it => ([it._id, it])))) const newSkills:TagReference[] = [] diff --git a/plugins/recruit-resources/src/index.ts b/plugins/recruit-resources/src/index.ts index 3e59296aa1..448f55552c 100644 --- a/plugins/recruit-resources/src/index.ts +++ b/plugins/recruit-resources/src/index.ts @@ -14,9 +14,12 @@ // import type { Client, Doc } from '@anticrm/core' -import { getMetadata, IntlString, OK, Resources, Severity, Status, translate } from '@anticrm/platform' -import { Applicant, Vacancy } from '@anticrm/recruit' +import { IntlString, OK, Resources, Severity, Status, translate } from '@anticrm/platform' +import { ObjectSearchResult } from '@anticrm/presentation' +import { Applicant } from '@anticrm/recruit' +import task from '@anticrm/task' import { showPopup } from '@anticrm/ui' +import ApplicationItem from './components/ApplicationItem.svelte' import ApplicationPresenter from './components/ApplicationPresenter.svelte' import Applications from './components/Applications.svelte' import ApplicationsPresenter from './components/ApplicationsPresenter.svelte' @@ -27,16 +30,10 @@ import CreateVacancy from './components/CreateVacancy.svelte' import EditApplication from './components/EditApplication.svelte' import EditVacancy from './components/EditVacancy.svelte' import KanbanCard from './components/KanbanCard.svelte' -import TemplatesIcon from './components/TemplatesIcon.svelte' -import recruit from './plugin' -import { ObjectSearchResult } from '@anticrm/presentation' -import task from '@anticrm/task' -import ApplicationItem from './components/ApplicationItem.svelte' -import VacancyPresenter from './components/VacancyPresenter.svelte' import SkillsView from './components/SkillsView.svelte' -import login from '@anticrm/login' -import workbench from '@anticrm/workbench' -import view from '@anticrm/view' +import TemplatesIcon from './components/TemplatesIcon.svelte' +import VacancyPresenter from './components/VacancyPresenter.svelte' +import recruit from './plugin' async function createApplication (object: Doc): Promise { showPopup(CreateApplication, { candidate: object._id, preserveCandidate: true }) diff --git a/plugins/recruit-resources/src/plugin.ts b/plugins/recruit-resources/src/plugin.ts index bf3d10a743..b75e4c60a4 100644 --- a/plugins/recruit-resources/src/plugin.ts +++ b/plugins/recruit-resources/src/plugin.ts @@ -17,6 +17,7 @@ import { Ref, Space } from '@anticrm/core' import type { IntlString, StatusCode } from '@anticrm/platform' import { mergeIds } from '@anticrm/platform' import recruit, { recruitId } from '@anticrm/recruit' +import { TagCategory } from '@anticrm/tags' export default mergeIds(recruitId, recruit, { status: { @@ -69,5 +70,9 @@ export default mergeIds(recruitId, recruit, { }, space: { CandidatesPublic: '' as Ref + }, + category: { + Other: '' as Ref, + Category: '' as Ref } }) diff --git a/plugins/tags-assets/lang/en.json b/plugins/tags-assets/lang/en.json index 81db527d16..2d93adadbc 100644 --- a/plugins/tags-assets/lang/en.json +++ b/plugins/tags-assets/lang/en.json @@ -29,6 +29,7 @@ "CategoryTargetClass": "Category", "CategoryTagsLabel": "Category Items", "OtherCategoryLabel": "Other", - "AllCategories": "All Categories" + "AllCategories": "All Categories", + "DefaultLabel": "Default category" } } \ No newline at end of file diff --git a/plugins/tags-resources/src/components/CategoryBar.svelte b/plugins/tags-resources/src/components/CategoryBar.svelte index f370c443f8..811d9dd788 100644 --- a/plugins/tags-resources/src/components/CategoryBar.svelte +++ b/plugins/tags-resources/src/components/CategoryBar.svelte @@ -14,11 +14,11 @@ -->