mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-06 23:46:24 +00:00
parent
bff122ee8c
commit
9c278d46e4
@ -14,8 +14,8 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { Builder, Model, Prop, UX, TypeString, TypeTimestamp } from '@anticrm/model'
|
import { Builder, Model, Prop, UX, TypeString, TypeTimestamp, Index } from '@anticrm/model'
|
||||||
import type { Domain } from '@anticrm/core'
|
import { Domain, IndexKind } from '@anticrm/core'
|
||||||
import core, { TAttachedDoc } from '@anticrm/model-core'
|
import core, { TAttachedDoc } from '@anticrm/model-core'
|
||||||
import type { Attachment, Photo } from '@anticrm/attachment'
|
import type { Attachment, Photo } from '@anticrm/attachment'
|
||||||
import activity from '@anticrm/activity'
|
import activity from '@anticrm/activity'
|
||||||
@ -31,6 +31,7 @@ export const DOMAIN_ATTACHMENT = 'attachment' as Domain
|
|||||||
@UX('File' as IntlString)
|
@UX('File' as IntlString)
|
||||||
export class TAttachment extends TAttachedDoc implements Attachment {
|
export class TAttachment extends TAttachedDoc implements Attachment {
|
||||||
@Prop(TypeString(), 'Name' as IntlString)
|
@Prop(TypeString(), 'Name' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
name!: string
|
name!: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'File' as IntlString)
|
@Prop(TypeString(), 'File' as IntlString)
|
||||||
@ -40,6 +41,7 @@ export class TAttachment extends TAttachedDoc implements Attachment {
|
|||||||
size!: number
|
size!: number
|
||||||
|
|
||||||
@Prop(TypeString(), 'Type' as IntlString)
|
@Prop(TypeString(), 'Type' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
type!: string
|
type!: string
|
||||||
|
|
||||||
@Prop(TypeTimestamp(), 'Date' as IntlString)
|
@Prop(TypeTimestamp(), 'Date' as IntlString)
|
||||||
|
@ -59,6 +59,7 @@ export class TContact extends TDoc implements Contact {
|
|||||||
comments?: number
|
comments?: number
|
||||||
|
|
||||||
@Prop(TypeString(), 'Location' as IntlString)
|
@Prop(TypeString(), 'Location' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
city!: string
|
city!: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +70,7 @@ export class TChannel extends TAttachedDoc implements Channel {
|
|||||||
provider!: Ref<ChannelProvider>
|
provider!: Ref<ChannelProvider>
|
||||||
|
|
||||||
@Prop(TypeString(), 'Value' as IntlString)
|
@Prop(TypeString(), 'Value' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
value!: string
|
value!: string
|
||||||
|
|
||||||
items?: number
|
items?: number
|
||||||
|
@ -13,9 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { Account, Arr, Ref, Space } from '@anticrm/core'
|
import { Account, Arr, DOMAIN_MODEL, IndexKind, Ref, Space } from '@anticrm/core'
|
||||||
import { DOMAIN_MODEL } from '@anticrm/core'
|
import { Index, Model, Prop, TypeBoolean, TypeString } from '@anticrm/model'
|
||||||
import { Model, Prop, TypeBoolean, TypeString } from '@anticrm/model'
|
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import core from './component'
|
import core from './component'
|
||||||
import { TDoc } from './core'
|
import { TDoc } from './core'
|
||||||
@ -25,9 +24,11 @@ import { TDoc } from './core'
|
|||||||
@Model(core.class.Space, core.class.Doc, DOMAIN_MODEL)
|
@Model(core.class.Space, core.class.Doc, DOMAIN_MODEL)
|
||||||
export class TSpace extends TDoc implements Space {
|
export class TSpace extends TDoc implements Space {
|
||||||
@Prop(TypeString(), 'Name' as IntlString)
|
@Prop(TypeString(), 'Name' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
name!: string
|
name!: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'Description' as IntlString)
|
@Prop(TypeString(), 'Description' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
description!: string
|
description!: string
|
||||||
|
|
||||||
@Prop(TypeBoolean(), 'Private' as IntlString)
|
@Prop(TypeBoolean(), 'Private' as IntlString)
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { Builder, Model, TypeString, Prop, ArrOf, TypeBoolean } from '@anticrm/model'
|
import { Builder, Model, TypeString, Prop, ArrOf, TypeBoolean, Index } from '@anticrm/model'
|
||||||
import core, { TAttachedDoc } from '@anticrm/model-core'
|
import core, { TAttachedDoc } from '@anticrm/model-core'
|
||||||
import contact from '@anticrm/model-contact'
|
import contact from '@anticrm/model-contact'
|
||||||
import gmail from './plugin'
|
import gmail from './plugin'
|
||||||
import type { Message, SharedMessage, SharedMessages } from '@anticrm/gmail'
|
import type { Message, SharedMessage, SharedMessages } from '@anticrm/gmail'
|
||||||
import type { Domain, Type } from '@anticrm/core'
|
import { Domain, IndexKind, Type } from '@anticrm/core'
|
||||||
import setting from '@anticrm/setting'
|
import setting from '@anticrm/setting'
|
||||||
import activity from '@anticrm/activity'
|
import activity from '@anticrm/activity'
|
||||||
|
|
||||||
@ -36,24 +36,31 @@ export class TMessage extends TAttachedDoc implements Message {
|
|||||||
messageId!: string
|
messageId!: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'ReplyTo' as IntlString)
|
@Prop(TypeString(), 'ReplyTo' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
replyTo?: string
|
replyTo?: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'From' as IntlString)
|
@Prop(TypeString(), 'From' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
from!: string
|
from!: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'To' as IntlString)
|
@Prop(TypeString(), 'To' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
to!: string
|
to!: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'Contact' as IntlString)
|
@Prop(TypeString(), 'Contact' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
contact!: string
|
contact!: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'Subject' as IntlString)
|
@Prop(TypeString(), 'Subject' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
subject!: string
|
subject!: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'Message' as IntlString)
|
@Prop(TypeString(), 'Message' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
content!: string
|
content!: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'Message' as IntlString)
|
@Prop(TypeString(), 'Message' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
textContent!: string
|
textContent!: string
|
||||||
|
|
||||||
@Prop(ArrOf(TypeString()), 'Copy' as IntlString)
|
@Prop(ArrOf(TypeString()), 'Copy' as IntlString)
|
||||||
|
@ -14,9 +14,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Doc, Domain, FindOptions, Ref } from '@anticrm/core'
|
import { Doc, Domain, FindOptions, IndexKind, Ref } from '@anticrm/core'
|
||||||
import type { Category, Product, Variant } from '@anticrm/inventory'
|
import type { Category, Product, Variant } from '@anticrm/inventory'
|
||||||
import { Builder, Collection, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
import { Builder, Collection, Index, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||||
import attachment from '@anticrm/model-attachment'
|
import attachment from '@anticrm/model-attachment'
|
||||||
import core, { TAttachedDoc } from '@anticrm/model-core'
|
import core, { TAttachedDoc } from '@anticrm/model-core'
|
||||||
import workbench from '@anticrm/model-workbench'
|
import workbench from '@anticrm/model-workbench'
|
||||||
@ -30,6 +30,7 @@ export const DOMAIN_INVENTORY = 'inventory' as Domain
|
|||||||
@UX(inventory.string.Category, inventory.icon.Categories, undefined, 'name')
|
@UX(inventory.string.Category, inventory.icon.Categories, undefined, 'name')
|
||||||
export class TCategory extends TAttachedDoc implements Category {
|
export class TCategory extends TAttachedDoc implements Category {
|
||||||
@Prop(TypeString(), 'Name' as IntlString)
|
@Prop(TypeString(), 'Name' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
name!: string
|
name!: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ export class TProduct extends TAttachedDoc implements Product {
|
|||||||
declare attachedTo: Ref<Category>
|
declare attachedTo: Ref<Category>
|
||||||
|
|
||||||
@Prop(TypeString(), 'Name' as IntlString)
|
@Prop(TypeString(), 'Name' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
name!: string
|
name!: string
|
||||||
|
|
||||||
@Prop(Collection(attachment.class.Photo), attachment.string.Photos)
|
@Prop(Collection(attachment.class.Photo), attachment.string.Photos)
|
||||||
@ -61,9 +63,11 @@ export class TVariant extends TAttachedDoc implements Variant {
|
|||||||
declare attachedTo: Ref<Product>
|
declare attachedTo: Ref<Product>
|
||||||
|
|
||||||
@Prop(TypeString(), 'Name' as IntlString)
|
@Prop(TypeString(), 'Name' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
name!: string
|
name!: string
|
||||||
|
|
||||||
@Prop(TypeString(), inventory.string.SKU)
|
@Prop(TypeString(), inventory.string.SKU)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
sku!: string
|
sku!: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
// To help typescript locate view plugin properly
|
// To help typescript locate view plugin properly
|
||||||
import type { Employee } from '@anticrm/contact'
|
import type { Employee } from '@anticrm/contact'
|
||||||
import type { Doc, FindOptions, Lookup, Ref } from '@anticrm/core'
|
import { Doc, FindOptions, IndexKind, Lookup, Ref } from '@anticrm/core'
|
||||||
import type { Customer, Funnel, Lead } from '@anticrm/lead'
|
import type { Customer, Funnel, Lead } from '@anticrm/lead'
|
||||||
import { Builder, Collection, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
import { Builder, Collection, Index, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||||
import attachment from '@anticrm/model-attachment'
|
import attachment from '@anticrm/model-attachment'
|
||||||
import chunter from '@anticrm/model-chunter'
|
import chunter from '@anticrm/model-chunter'
|
||||||
import contact, { TPerson } from '@anticrm/model-contact'
|
import contact, { TPerson } from '@anticrm/model-contact'
|
||||||
@ -41,6 +41,7 @@ export class TLead extends TTask implements Lead {
|
|||||||
declare attachedTo: Ref<Customer>
|
declare attachedTo: Ref<Customer>
|
||||||
|
|
||||||
@Prop(TypeString(), 'Title' as IntlString)
|
@Prop(TypeString(), 'Title' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
title!: string
|
title!: string
|
||||||
|
|
||||||
@Prop(Collection(chunter.class.Comment), 'Comments' as IntlString)
|
@Prop(Collection(chunter.class.Comment), 'Comments' as IntlString)
|
||||||
@ -60,6 +61,7 @@ export class TCustomer extends TPerson implements Customer {
|
|||||||
leads?: number
|
leads?: number
|
||||||
|
|
||||||
@Prop(TypeString(), 'Description' as IntlString)
|
@Prop(TypeString(), 'Description' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
description!: string
|
description!: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,24 +14,25 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { Employee } from '@anticrm/contact'
|
import type { Employee } from '@anticrm/contact'
|
||||||
import { Doc, FindOptions, Lookup, Ref, Timestamp } from '@anticrm/core'
|
import { Doc, FindOptions, IndexKind, Lookup, Ref, Timestamp } from '@anticrm/core'
|
||||||
import { Builder, Collection, Mixin, Model, Prop, TypeBoolean, TypeDate, TypeMarkup, TypeRef, TypeString, UX } from '@anticrm/model'
|
import { Builder, Collection, Index, Mixin, Model, Prop, TypeBoolean, TypeDate, TypeMarkup, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||||
import attachment from '@anticrm/model-attachment'
|
import attachment from '@anticrm/model-attachment'
|
||||||
import chunter from '@anticrm/model-chunter'
|
import chunter from '@anticrm/model-chunter'
|
||||||
import contact, { TPerson } from '@anticrm/model-contact'
|
import contact, { TPerson } from '@anticrm/model-contact'
|
||||||
import core, { TSpace } from '@anticrm/model-core'
|
import core, { TSpace } from '@anticrm/model-core'
|
||||||
|
import presentation from '@anticrm/model-presentation'
|
||||||
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
||||||
import view from '@anticrm/model-view'
|
import view from '@anticrm/model-view'
|
||||||
import workbench from '@anticrm/model-workbench'
|
import workbench from '@anticrm/model-workbench'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { Applicant, Candidate, Candidates, Vacancy } from '@anticrm/recruit'
|
import { Applicant, Candidate, Candidates, Vacancy } from '@anticrm/recruit'
|
||||||
import recruit from './plugin'
|
import recruit from './plugin'
|
||||||
import presentation from '@anticrm/model-presentation'
|
|
||||||
|
|
||||||
@Model(recruit.class.Vacancy, task.class.SpaceWithStates)
|
@Model(recruit.class.Vacancy, task.class.SpaceWithStates)
|
||||||
@UX(recruit.string.Vacancy, recruit.icon.Vacancy)
|
@UX(recruit.string.Vacancy, recruit.icon.Vacancy)
|
||||||
export class TVacancy extends TSpaceWithStates implements Vacancy {
|
export class TVacancy extends TSpaceWithStates implements Vacancy {
|
||||||
@Prop(TypeMarkup(), 'Full description' as IntlString)
|
@Prop(TypeMarkup(), 'Full description' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
fullDescription?: string
|
fullDescription?: string
|
||||||
|
|
||||||
@Prop(Collection(attachment.class.Attachment), 'Attachments' as IntlString)
|
@Prop(Collection(attachment.class.Attachment), 'Attachments' as IntlString)
|
||||||
@ -41,9 +42,11 @@ export class TVacancy extends TSpaceWithStates implements Vacancy {
|
|||||||
dueTo?: Timestamp
|
dueTo?: Timestamp
|
||||||
|
|
||||||
@Prop(TypeString(), 'Location' as IntlString, recruit.icon.Location)
|
@Prop(TypeString(), 'Location' as IntlString, recruit.icon.Location)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
location?: string
|
location?: string
|
||||||
|
|
||||||
@Prop(TypeString(), 'Company' as IntlString, contact.icon.Company)
|
@Prop(TypeString(), 'Company' as IntlString, contact.icon.Company)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
company?: string
|
company?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +58,7 @@ export class TCandidates extends TSpace implements Candidates {}
|
|||||||
@UX('Candidate' as IntlString, recruit.icon.RecruitApplication)
|
@UX('Candidate' as IntlString, recruit.icon.RecruitApplication)
|
||||||
export class TCandidate extends TPerson implements Candidate {
|
export class TCandidate extends TPerson implements Candidate {
|
||||||
@Prop(TypeString(), 'Title' as IntlString)
|
@Prop(TypeString(), 'Title' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
title?: string
|
title?: string
|
||||||
|
|
||||||
@Prop(Collection(recruit.class.Applicant), 'Applications' as IntlString)
|
@Prop(Collection(recruit.class.Applicant), 'Applications' as IntlString)
|
||||||
@ -67,6 +71,7 @@ export class TCandidate extends TPerson implements Candidate {
|
|||||||
remote?: boolean
|
remote?: boolean
|
||||||
|
|
||||||
@Prop(TypeString(), 'Source' as IntlString)
|
@Prop(TypeString(), 'Source' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
source?: string
|
source?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,6 +318,6 @@ export function createModel (builder: Builder): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export { default } from './plugin'
|
|
||||||
export { recruitOperation } from './migration'
|
|
||||||
export { createDeps } from './creation'
|
export { createDeps } from './creation'
|
||||||
|
export { recruitOperation } from './migration'
|
||||||
|
export { default } from './plugin'
|
||||||
|
@ -15,10 +15,11 @@
|
|||||||
|
|
||||||
import type { Employee } from '@anticrm/contact'
|
import type { Employee } from '@anticrm/contact'
|
||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
import { Arr, Class, Doc, Domain, DOMAIN_MODEL, FindOptions, Ref, Space, Timestamp } from '@anticrm/core'
|
import { Arr, Class, Doc, Domain, DOMAIN_MODEL, FindOptions, IndexKind, Ref, Space, Timestamp } from '@anticrm/core'
|
||||||
import {
|
import {
|
||||||
Builder,
|
Builder,
|
||||||
Collection, Hidden, Implements,
|
Collection, Hidden, Implements,
|
||||||
|
Index,
|
||||||
Mixin,
|
Mixin,
|
||||||
Model,
|
Model,
|
||||||
Prop, TypeBoolean,
|
Prop, TypeBoolean,
|
||||||
@ -106,6 +107,7 @@ export class TTask extends TAttachedDoc implements Task {
|
|||||||
@UX(task.string.Todo)
|
@UX(task.string.Todo)
|
||||||
export class TTodoItem extends TAttachedDoc implements TodoItem {
|
export class TTodoItem extends TAttachedDoc implements TodoItem {
|
||||||
@Prop(TypeString(), task.string.TodoName, task.icon.Task)
|
@Prop(TypeString(), task.string.TodoName, task.icon.Task)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
name!: string
|
name!: string
|
||||||
|
|
||||||
@Prop(TypeBoolean(), task.string.TaskDone)
|
@Prop(TypeBoolean(), task.string.TaskDone)
|
||||||
@ -130,9 +132,11 @@ export class TIssue extends TTask implements Issue {
|
|||||||
declare attachedTo: Ref<Doc>
|
declare attachedTo: Ref<Doc>
|
||||||
|
|
||||||
@Prop(TypeString(), task.string.IssueName)
|
@Prop(TypeString(), task.string.IssueName)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
name!: string
|
name!: string
|
||||||
|
|
||||||
@Prop(TypeMarkup(), task.string.TaskDescription)
|
@Prop(TypeMarkup(), task.string.TaskDescription)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
description!: string
|
description!: string
|
||||||
|
|
||||||
@Prop(Collection(chunter.class.Comment), task.string.TaskComments)
|
@Prop(Collection(chunter.class.Comment), task.string.TaskComments)
|
||||||
@ -142,6 +146,7 @@ export class TIssue extends TTask implements Issue {
|
|||||||
attachments!: number
|
attachments!: number
|
||||||
|
|
||||||
@Prop(TypeString(), task.string.TaskLabels)
|
@Prop(TypeString(), task.string.TaskLabels)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
labels!: string
|
labels!: string
|
||||||
|
|
||||||
@Prop(TypeRef(contact.class.Employee), task.string.TaskAssignee)
|
@Prop(TypeRef(contact.class.Employee), task.string.TaskAssignee)
|
||||||
@ -193,6 +198,7 @@ export class TLostStateTemplate extends TDoneStateTemplate implements LostStateT
|
|||||||
@Model(task.class.KanbanTemplate, core.class.Doc, DOMAIN_KANBAN)
|
@Model(task.class.KanbanTemplate, core.class.Doc, DOMAIN_KANBAN)
|
||||||
export class TKanbanTemplate extends TDoc implements KanbanTemplate {
|
export class TKanbanTemplate extends TDoc implements KanbanTemplate {
|
||||||
@Prop(TypeString(), task.string.KanbanTemplateTitle)
|
@Prop(TypeString(), task.string.KanbanTemplateTitle)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
title!: string
|
title!: string
|
||||||
|
|
||||||
@Prop(Collection(task.class.StateTemplate), task.string.States)
|
@Prop(Collection(task.class.StateTemplate), task.string.States)
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { Builder, Model, TypeString, TypeBoolean, Prop, ArrOf } from '@anticrm/model'
|
import { Builder, Model, TypeString, TypeBoolean, Prop, ArrOf, Index } from '@anticrm/model'
|
||||||
import core, { TAttachedDoc } from '@anticrm/model-core'
|
import core, { TAttachedDoc } from '@anticrm/model-core'
|
||||||
import contact from '@anticrm/model-contact'
|
import contact from '@anticrm/model-contact'
|
||||||
import telegram from './plugin'
|
import telegram from './plugin'
|
||||||
import type { TelegramMessage, SharedTelegramMessage, SharedTelegramMessages } from '@anticrm/telegram'
|
import type { TelegramMessage, SharedTelegramMessage, SharedTelegramMessages } from '@anticrm/telegram'
|
||||||
import type { Domain, Type } from '@anticrm/core'
|
import { Domain, IndexKind, Type } from '@anticrm/core'
|
||||||
import setting from '@anticrm/setting'
|
import setting from '@anticrm/setting'
|
||||||
import activity from '@anticrm/activity'
|
import activity from '@anticrm/activity'
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ function TypeSharedMessage (): Type<SharedTelegramMessage> {
|
|||||||
@Model(telegram.class.Message, core.class.AttachedDoc, DOMAIN_TELEGRAM)
|
@Model(telegram.class.Message, core.class.AttachedDoc, DOMAIN_TELEGRAM)
|
||||||
export class TTelegramMessage extends TAttachedDoc implements TelegramMessage {
|
export class TTelegramMessage extends TAttachedDoc implements TelegramMessage {
|
||||||
@Prop(TypeString(), 'Content' as IntlString)
|
@Prop(TypeString(), 'Content' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
content!: string
|
content!: string
|
||||||
|
|
||||||
@Prop(TypeBoolean(), 'Incoming' as IntlString)
|
@Prop(TypeBoolean(), 'Incoming' as IntlString)
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { Domain } from '@anticrm/core'
|
import { Domain, IndexKind } from '@anticrm/core'
|
||||||
import { Builder, Model, Prop, TypeString } from '@anticrm/model'
|
import { Builder, Index, Model, Prop, TypeString } from '@anticrm/model'
|
||||||
import core, { TDoc } from '@anticrm/model-core'
|
import core, { TDoc } from '@anticrm/model-core'
|
||||||
import textEditor from '@anticrm/model-text-editor'
|
import textEditor from '@anticrm/model-text-editor'
|
||||||
import { IntlString } from '@anticrm/platform'
|
import { IntlString } from '@anticrm/platform'
|
||||||
@ -28,9 +28,11 @@ export const DOMAIN_TEMPLATES = 'templates' as Domain
|
|||||||
@Model(templates.class.MessageTemplate, core.class.Doc, DOMAIN_TEMPLATES)
|
@Model(templates.class.MessageTemplate, core.class.Doc, DOMAIN_TEMPLATES)
|
||||||
export class TMessageTemplate extends TDoc implements MessageTemplate {
|
export class TMessageTemplate extends TDoc implements MessageTemplate {
|
||||||
@Prop(TypeString(), 'Title' as IntlString)
|
@Prop(TypeString(), 'Title' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
title!: string;
|
title!: string;
|
||||||
|
|
||||||
@Prop(TypeString(), 'Message' as IntlString)
|
@Prop(TypeString(), 'Message' as IntlString)
|
||||||
|
@Index(IndexKind.FullText)
|
||||||
message!: string;
|
message!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
import { AttributesBar, createQuery, getClient } from '@anticrm/presentation'
|
import { AttributesBar, createQuery, getClient } from '@anticrm/presentation'
|
||||||
import { Vacancy } from '@anticrm/recruit'
|
import { Vacancy } from '@anticrm/recruit'
|
||||||
import { StyledTextBox } from '@anticrm/text-editor'
|
import { StyledTextBox } from '@anticrm/text-editor'
|
||||||
import { Component, EditBox, Grid, Icon, IconClose, Label, ToggleWithLabel, ActionIcon, Scroller } from '@anticrm/ui'
|
import { ActionIcon, Component, EditBox, Grid, Icon, IconClose, Label, Scroller, ToggleWithLabel } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import recruit from '../plugin'
|
import recruit from '../plugin'
|
||||||
|
|
||||||
@ -108,7 +108,7 @@
|
|||||||
<ToggleWithLabel label={recruit.string.ThisVacancyIsPrivate} description={recruit.string.MakePrivateDescription}/>
|
<ToggleWithLabel label={recruit.string.ThisVacancyIsPrivate} description={recruit.string.MakePrivateDescription}/>
|
||||||
</Scroller>
|
</Scroller>
|
||||||
{:else if selected === 2}
|
{:else if selected === 2}
|
||||||
<Component is={activity.component.Activity} props={{object, transparent: true}} />
|
<Component is={activity.component.Activity} props={{ object, transparent: true }} />
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,34 +15,38 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import core, {
|
import core, {
|
||||||
AttachedDoc, Class, ClassifierKind, Doc, Obj, Ref, TxCreateDoc, TxResult, TxUpdateDoc,
|
|
||||||
AnyAttribute,
|
AnyAttribute,
|
||||||
|
AttachedDoc,
|
||||||
|
Class,
|
||||||
|
ClassifierKind,
|
||||||
Collection,
|
Collection,
|
||||||
|
Doc,
|
||||||
DocumentQuery,
|
DocumentQuery,
|
||||||
FindOptions,
|
FindOptions,
|
||||||
FindResult,
|
FindResult,
|
||||||
Hierarchy,
|
Hierarchy,
|
||||||
|
IndexKind,
|
||||||
MeasureContext,
|
MeasureContext,
|
||||||
|
Obj,
|
||||||
PropertyType,
|
PropertyType,
|
||||||
|
Ref,
|
||||||
Tx,
|
Tx,
|
||||||
TxBulkWrite,
|
TxBulkWrite,
|
||||||
TxCollectionCUD,
|
TxCollectionCUD,
|
||||||
|
TxCreateDoc,
|
||||||
TxMixin,
|
TxMixin,
|
||||||
TxProcessor,
|
TxProcessor,
|
||||||
TxPutBag,
|
TxPutBag,
|
||||||
TxRemoveDoc
|
TxRemoveDoc,
|
||||||
|
TxResult,
|
||||||
|
TxUpdateDoc
|
||||||
} from '@anticrm/core'
|
} from '@anticrm/core'
|
||||||
import type { FullTextAdapter, IndexedDoc, WithFind } from './types'
|
import type { FullTextAdapter, IndexedDoc, WithFind } from './types'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
const NO_INDEX = [] as AnyAttribute[]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export class FullTextIndex implements WithFind {
|
export class FullTextIndex implements WithFind {
|
||||||
private readonly indexes = new Map<Ref<Class<Obj>>, AnyAttribute[]>()
|
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private readonly hierarchy: Hierarchy,
|
private readonly hierarchy: Hierarchy,
|
||||||
private readonly adapter: FullTextAdapter,
|
private readonly adapter: FullTextAdapter,
|
||||||
@ -138,7 +142,7 @@ export class FullTextIndex implements WithFind {
|
|||||||
const { _id, $search, ...mainQuery } = query
|
const { _id, $search, ...mainQuery } = query
|
||||||
if ($search === undefined) return []
|
if ($search === undefined) return []
|
||||||
const docs = await this.adapter.search(_class, query, options?.limit)
|
const docs = await this.adapter.search(_class, query, options?.limit)
|
||||||
const ids: Set<Ref<Doc>> = new Set<Ref<Doc>>(docs.map(p => p.id))
|
const ids: Set<Ref<Doc>> = new Set<Ref<Doc>>(docs.map((p) => p.id))
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
if (doc.attachedTo !== undefined) {
|
if (doc.attachedTo !== undefined) {
|
||||||
ids.add(doc.attachedTo)
|
ids.add(doc.attachedTo)
|
||||||
@ -147,25 +151,29 @@ export class FullTextIndex implements WithFind {
|
|||||||
return await this.dbStorage.findAll(ctx, _class, { _id: { $in: Array.from(ids) as any }, ...mainQuery }, options) // TODO: remove `as any`
|
return await this.dbStorage.findAll(ctx, _class, { _id: { $in: Array.from(ids) as any }, ...mainQuery }, options) // TODO: remove `as any`
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFullTextAttributes (clazz: Ref<Class<Obj>>): AnyAttribute[] | undefined {
|
private getFullTextAttributes (clazz: Ref<Class<Obj>>, parentDoc?: Doc): AnyAttribute[] {
|
||||||
const attributes = this.indexes.get(clazz)
|
const allAttributes = this.hierarchy.getAllAttributes(clazz)
|
||||||
if (attributes === undefined) {
|
const result: AnyAttribute[] = []
|
||||||
const allAttributes = this.hierarchy.getAllAttributes(clazz)
|
for (const [, attr] of allAttributes) {
|
||||||
const result: AnyAttribute[] = []
|
if (isFullTextAttribute(attr)) {
|
||||||
for (const [, attr] of allAttributes) {
|
result.push(attr)
|
||||||
if (attr.type._class === core.class.TypeString || attr.type._class === core.class.TypeMarkup) {
|
|
||||||
result.push(attr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (result.length > 0) {
|
|
||||||
this.indexes.set(clazz, result)
|
|
||||||
return result
|
|
||||||
} else {
|
|
||||||
this.indexes.set(clazz, NO_INDEX)
|
|
||||||
}
|
|
||||||
} else if (attributes !== NO_INDEX) {
|
|
||||||
return attributes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We also neex to add all mixin attribues if parent is specified.
|
||||||
|
if (parentDoc !== undefined) {
|
||||||
|
this.hierarchy
|
||||||
|
.getDescendants(clazz)
|
||||||
|
.filter((m) => this.hierarchy.getClass(m).kind === ClassifierKind.MIXIN)
|
||||||
|
.forEach((m) => {
|
||||||
|
for (const [, v] of this.hierarchy.getAllAttributes(m, clazz)) {
|
||||||
|
if (isFullTextAttribute(v) && this.hierarchy.hasMixin(parentDoc, m)) {
|
||||||
|
result.push(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async txCreateDoc (ctx: MeasureContext, tx: TxCreateDoc<Doc>): Promise<TxResult> {
|
protected async txCreateDoc (ctx: MeasureContext, tx: TxCreateDoc<Doc>): Promise<TxResult> {
|
||||||
@ -174,15 +182,20 @@ export class FullTextIndex implements WithFind {
|
|||||||
let parentContent: Record<string, string> = {}
|
let parentContent: Record<string, string> = {}
|
||||||
if (this.hierarchy.isDerived(doc._class, core.class.AttachedDoc)) {
|
if (this.hierarchy.isDerived(doc._class, core.class.AttachedDoc)) {
|
||||||
const attachedDoc = doc as AttachedDoc
|
const attachedDoc = doc as AttachedDoc
|
||||||
const parentDoc = (
|
if (attachedDoc.attachedToClass !== undefined && attachedDoc.attachedTo !== undefined) {
|
||||||
await this.dbStorage.findAll(ctx, attachedDoc.attachedToClass, { _id: attachedDoc.attachedTo }, { limit: 1 })
|
const parentDoc = (
|
||||||
)[0]
|
await this.dbStorage.findAll(ctx, attachedDoc.attachedToClass, { _id: attachedDoc.attachedTo }, { limit: 1 })
|
||||||
if (parentDoc !== undefined) {
|
)[0]
|
||||||
const parentAttributes = this.getFullTextAttributes(parentDoc._class)
|
if (parentDoc !== undefined) {
|
||||||
parentContent = this.getContent(parentAttributes, parentDoc)
|
const parentAttributes = this.getFullTextAttributes(parentDoc._class, parentDoc)
|
||||||
|
|
||||||
|
if (parentAttributes.length > 0) {
|
||||||
|
parentContent = this.getContent(parentAttributes, parentDoc)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attributes === undefined && Object.keys(parentContent).length === 0) return {}
|
if (attributes.length === 0 && Object.keys(parentContent).length === 0) return {}
|
||||||
|
|
||||||
let content = this.getContent(attributes, doc)
|
let content = this.getContent(attributes, doc)
|
||||||
content = { ...parentContent, ...content }
|
content = { ...parentContent, ...content }
|
||||||
@ -201,7 +214,7 @@ export class FullTextIndex implements WithFind {
|
|||||||
protected async txUpdateDoc (ctx: MeasureContext, tx: TxUpdateDoc<Doc>): Promise<TxResult> {
|
protected async txUpdateDoc (ctx: MeasureContext, tx: TxUpdateDoc<Doc>): Promise<TxResult> {
|
||||||
const attributes = this.getFullTextAttributes(tx.objectClass)
|
const attributes = this.getFullTextAttributes(tx.objectClass)
|
||||||
let result = {}
|
let result = {}
|
||||||
if (attributes === undefined) return result
|
if (attributes.length === 0) return result
|
||||||
const ops: any = tx.operations
|
const ops: any = tx.operations
|
||||||
const update: any = {}
|
const update: any = {}
|
||||||
let shouldUpdate = false
|
let shouldUpdate = false
|
||||||
@ -224,22 +237,33 @@ export class FullTextIndex implements WithFind {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private getContent (attributes: AnyAttribute[] | undefined, doc: Doc): Record<string, string> {
|
private getContent (attributes: AnyAttribute[], doc: Doc): Record<string, string> {
|
||||||
const attrs: Record<string, string> = {}
|
const attrs: Record<string, string> = {}
|
||||||
|
|
||||||
for (const attr of attributes ?? []) {
|
for (const attr of attributes) {
|
||||||
attrs[attr.name] = (doc as any)[attr.name]?.toString() ?? ''
|
const isMixinAttr = this.hierarchy.isMixin(attr.attributeOf)
|
||||||
|
if (isMixinAttr) {
|
||||||
|
attrs[(attr.attributeOf as string) + '.' + attr.name] =
|
||||||
|
((doc as any)[attr.attributeOf] ?? {})[attr.name]?.toString() ?? ''
|
||||||
|
} else {
|
||||||
|
attrs[attr.name] = (doc as any)[attr.name]?.toString() ?? ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return attrs
|
return attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateAttachedDocs (ctx: MeasureContext, tx: {objectId: Ref<Doc>, objectClass: Ref<Class<Doc>>}, update: any): Promise<void> {
|
private async updateAttachedDocs (
|
||||||
|
ctx: MeasureContext,
|
||||||
|
tx: { objectId: Ref<Doc>, objectClass: Ref<Class<Doc>> },
|
||||||
|
update: any
|
||||||
|
): Promise<void> {
|
||||||
const doc = (await this.dbStorage.findAll(ctx, tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0]
|
const doc = (await this.dbStorage.findAll(ctx, tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0]
|
||||||
if (doc === undefined) return
|
if (doc === undefined) return
|
||||||
const attributes = this.hierarchy.getAllAttributes(doc._class)
|
const attributes = this.hierarchy.getAllAttributes(doc._class)
|
||||||
|
|
||||||
// Find all mixin atttibutes for document.
|
// Find all mixin atttibutes for document.
|
||||||
this.hierarchy.getDescendants(doc._class)
|
this.hierarchy
|
||||||
|
.getDescendants(doc._class)
|
||||||
.filter((m) => this.hierarchy.getClass(m).kind === ClassifierKind.MIXIN && this.hierarchy.hasMixin(doc, m))
|
.filter((m) => this.hierarchy.getClass(m).kind === ClassifierKind.MIXIN && this.hierarchy.hasMixin(doc, m))
|
||||||
.forEach((m) => {
|
.forEach((m) => {
|
||||||
for (const [k, v] of this.hierarchy.getAllAttributes(m, doc._class)) {
|
for (const [k, v] of this.hierarchy.getAllAttributes(m, doc._class)) {
|
||||||
@ -261,7 +285,14 @@ export class FullTextIndex implements WithFind {
|
|||||||
await this.adapter.update(attached._id, docUpdate)
|
await this.adapter.update(attached._id, docUpdate)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (((err.message as string) ?? '').includes('document_missing_exception:')) {
|
if (((err.message as string) ?? '').includes('document_missing_exception:')) {
|
||||||
console.error('missing document in elastic for', tx.objectId, 'attached', attached._id, 'collection', attached.collection)
|
console.error(
|
||||||
|
'missing document in elastic for',
|
||||||
|
tx.objectId,
|
||||||
|
'attached',
|
||||||
|
attached._id,
|
||||||
|
'collection',
|
||||||
|
attached.collection
|
||||||
|
)
|
||||||
// We have no document for attached object, so ignore for now. it is probable rebuild of elastic DB.
|
// We have no document for attached object, so ignore for now. it is probable rebuild of elastic DB.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -272,3 +303,9 @@ export class FullTextIndex implements WithFind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function isFullTextAttribute (attr: AnyAttribute): boolean {
|
||||||
|
return (
|
||||||
|
attr.index === IndexKind.FullText &&
|
||||||
|
(attr.type._class === core.class.TypeString || attr.type._class === core.class.TypeMarkup)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"format": "prettier --write src && eslint --fix src",
|
"format": "prettier --write src && eslint --fix src",
|
||||||
"ci": "playwright install --with-deps chromium",
|
"ci": "playwright install --with-deps chromium",
|
||||||
"test": "",
|
"test": "",
|
||||||
"uitest": "playwright test --browser chromium --reporter list,html"
|
"uitest": "playwright test --browser chromium --reporter list,html -c ./tests/playwright.config.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@anticrm/platform-rig": "~0.6.0",
|
"@anticrm/platform-rig": "~0.6.0",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { openWorkbench } from './utils'
|
import { openWorkbench } from './utils'
|
||||||
|
|
||||||
test.describe('contact tests', () => {
|
test.describe('contact tests', () => {
|
||||||
@ -22,4 +22,25 @@ test.describe('contact tests', () => {
|
|||||||
|
|
||||||
await page.locator('.card-container').locator('button:has-text("Create")').click()
|
await page.locator('.card-container').locator('button:has-text("Create")').click()
|
||||||
})
|
})
|
||||||
|
test('contact-search', async ({ page }) => {
|
||||||
|
// Create user and workspace
|
||||||
|
await openWorkbench(page)
|
||||||
|
|
||||||
|
await page.locator('[id="app-contact\\:string\\:Contacts"]').click()
|
||||||
|
|
||||||
|
await expect(page.locator('text=Elton John')).toBeVisible()
|
||||||
|
expect(await page.locator('.tr-body').count()).toBeGreaterThan(5)
|
||||||
|
|
||||||
|
const searchBox = page.locator('[placeholder="Search"]')
|
||||||
|
await searchBox.fill('Elton')
|
||||||
|
await searchBox.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.tr-body')).toHaveCount(1)
|
||||||
|
|
||||||
|
await searchBox.fill('')
|
||||||
|
await searchBox.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('text=Rosamund Chen')).toBeVisible()
|
||||||
|
expect(await page.locator('.tr-body').count()).toBeGreaterThan(5)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
7
tests/sanity/tests/playwright.config.ts
Normal file
7
tests/sanity/tests/playwright.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { PlaywrightTestConfig } from '@playwright/test'
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
use: {
|
||||||
|
screenshot: 'only-on-failure'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default config
|
@ -95,4 +95,28 @@ test.describe('recruit tests', () => {
|
|||||||
await expect(page.locator('text=John Multiseed').first()).toBeVisible()
|
await expect(page.locator('text=John Multiseed').first()).toBeVisible()
|
||||||
await expect(page.locator('text=Alex P.').first()).toBeVisible()
|
await expect(page.locator('text=Alex P.').first()).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('application-search', async ({ page }) => {
|
||||||
|
// Create user and workspace
|
||||||
|
await openWorkbench(page)
|
||||||
|
|
||||||
|
await page.locator('[id="app-recruit\\:string\\:RecruitApplication"]').click()
|
||||||
|
|
||||||
|
await page.click('text=Software Engineer')
|
||||||
|
|
||||||
|
await expect(page.locator('text=Andrey P.')).toBeVisible()
|
||||||
|
expect(await page.locator('.tr-body').count()).toBeGreaterThan(2)
|
||||||
|
|
||||||
|
const searchBox = page.locator('[placeholder="Search"]')
|
||||||
|
await searchBox.fill('Frontend Engineer')
|
||||||
|
await searchBox.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.tr-body')).toHaveCount(1)
|
||||||
|
|
||||||
|
await searchBox.fill('')
|
||||||
|
await searchBox.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('text=Andrey P.')).toBeVisible()
|
||||||
|
expect(await page.locator('.tr-body').count()).toBeGreaterThan(2)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user