Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2023-02-01 02:00:22 +07:00 committed by GitHub
parent f7e220d0f2
commit f91403e7a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 172 additions and 73 deletions

View File

@ -89,7 +89,7 @@ export class TCard extends TTask implements Card {
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number comments?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeRef(contact.class.Employee), board.string.Assignee) @Prop(TypeRef(contact.class.Employee), board.string.Assignee)

View File

@ -62,7 +62,7 @@ export class TEvent extends TAttachedDoc implements Event {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
description!: Markup description!: Markup
@Prop(TypeString(), calendar.string.Location, calendar.icon.Location) @Prop(TypeString(), calendar.string.Location, { icon: calendar.icon.Location })
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
location?: string location?: string
@ -72,7 +72,7 @@ export class TEvent extends TAttachedDoc implements Event {
@Prop(TypeDate(true), calendar.string.DueTo) @Prop(TypeDate(true), calendar.string.DueTo)
dueDate!: Timestamp dueDate!: Timestamp
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -81,7 +81,7 @@ export class TChunterMessage extends TAttachedDoc implements ChunterMessage {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
content!: string content!: string
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeRef(core.class.Account), chunter.string.CreateBy) @Prop(TypeRef(core.class.Account), chunter.string.CreateBy)
@ -138,7 +138,7 @@ export class TComment extends TAttachedDoc implements Comment {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
message!: string message!: string
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
} }

View File

@ -85,7 +85,7 @@ export class TContact extends TDoc implements Contact {
@Prop(Collection(contact.class.Channel), contact.string.ContactInfo) @Prop(Collection(contact.class.Channel), contact.string.ContactInfo)
channels?: number channels?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -89,7 +89,7 @@ export class TDocumentVersion extends TAttachedDoc implements DocumentVersion {
@Hidden() @Hidden()
content!: Markup content!: Markup
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments!: number attachments!: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -116,7 +116,7 @@ export class TDocument extends TDoc implements Document {
@Hidden() @Hidden()
latest!: number latest!: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -78,7 +78,7 @@ export class TMessage extends TAttachedDoc implements Message {
@Prop(TypeBoolean(), gmail.string.Incoming) @Prop(TypeBoolean(), gmail.string.Incoming)
incoming!: boolean incoming!: boolean
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeTimestamp(), core.string.Modified) @Prop(TypeTimestamp(), core.string.Modified)
@ -109,7 +109,7 @@ export class TNewMessage extends TDoc implements NewMessage {
@Prop(ArrOf(TypeString()), gmail.string.Copy) @Prop(ArrOf(TypeString()), gmail.string.Copy)
copy?: string[] copy?: string[]
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
} }

View File

@ -57,7 +57,7 @@ export class TDepartment extends TSpace implements Department {
@Prop(Collection(contact.class.Channel), contact.string.ContactInfo) @Prop(Collection(contact.class.Channel), contact.string.ContactInfo)
channels?: number channels?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -131,7 +131,7 @@ export class TRequest extends TAttachedDoc implements Request {
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number comments?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeMarkup(), core.string.Description) @Prop(TypeMarkup(), core.string.Description)

View File

@ -51,7 +51,7 @@ export class TProduct extends TAttachedDoc implements Product {
@Prop(Collection(inventory.class.Variant), inventory.string.Variants) @Prop(Collection(inventory.class.Variant), inventory.string.Variants)
variants?: number variants?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
} }

View File

@ -46,7 +46,7 @@ export class TFunnel extends TSpaceWithStates implements Funnel {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
fullDescription?: string fullDescription?: string
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -66,7 +66,7 @@ export class TLead extends TTask implements Lead {
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number comments?: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeRef(contact.class.Employee), lead.string.Assignee) @Prop(TypeRef(contact.class.Employee), lead.string.Assignee)

View File

@ -64,7 +64,7 @@ export class TVacancy extends TSpaceWithStates implements Vacancy {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
fullDescription?: string fullDescription?: string
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeDate(), recruit.string.Due, recruit.icon.Calendar) @Prop(TypeDate(), recruit.string.Due, recruit.icon.Calendar)
@ -74,7 +74,7 @@ export class TVacancy extends TSpaceWithStates implements Vacancy {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
location?: string location?: string
@Prop(TypeRef(contact.class.Organization), recruit.string.Company, contact.icon.Company) @Prop(TypeRef(contact.class.Organization), recruit.string.Company, { icon: contact.icon.Company })
company?: Ref<Organization> company?: Ref<Organization>
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -95,7 +95,9 @@ export class TCandidate extends TPerson implements Candidate {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
title?: string title?: string
@Prop(Collection(recruit.class.Applicant), recruit.string.Applications, undefined, recruit.string.ApplicationsShort) @Prop(Collection(recruit.class.Applicant), recruit.string.Applications, {
shortLabel: recruit.string.ApplicationsShort
})
applications?: number applications?: number
@Prop(TypeBoolean(), recruit.string.Onsite) @Prop(TypeBoolean(), recruit.string.Onsite)
@ -108,7 +110,10 @@ export class TCandidate extends TPerson implements Candidate {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
source?: string source?: string
@Prop(Collection(tags.class.TagReference, recruit.string.SkillLabel), recruit.string.SkillsLabel, recruit.icon.Skills) @Prop(Collection(tags.class.TagReference, recruit.string.SkillLabel), recruit.string.SkillsLabel, {
icon: recruit.icon.Skills,
schema: '3'
})
skills?: number skills?: number
@Prop(Collection(recruit.class.Review, recruit.string.Review), recruit.string.Reviews) @Prop(Collection(recruit.class.Review, recruit.string.Review), recruit.string.Reviews)
@ -141,7 +146,7 @@ export class TApplicant extends TTask implements Applicant {
@Index(IndexKind.Indexed) @Index(IndexKind.Indexed)
declare space: Ref<Vacancy> declare space: Ref<Vacancy>
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -24,7 +24,7 @@ export class TReview extends TEvent implements Review {
@Index(IndexKind.FullText) @Index(IndexKind.FullText)
verdict!: string verdict!: string
@Prop(TypeRef(contact.class.Organization), recruit.string.Company, contact.icon.Company) @Prop(TypeRef(contact.class.Organization), recruit.string.Company, { icon: contact.icon.Company })
company?: Ref<Organization> company?: Ref<Organization>
@Prop(Collection(recruit.class.Opinion), recruit.string.Opinions) @Prop(Collection(recruit.class.Opinion), recruit.string.Opinions)
@ -41,7 +41,7 @@ export class TOpinion extends TAttachedDoc implements Opinion {
@Prop(TypeRef(recruit.class.Review), recruit.string.Review) @Prop(TypeRef(recruit.class.Review), recruit.string.Review)
declare attachedTo: Ref<Review> declare attachedTo: Ref<Review>
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -189,7 +189,7 @@ export class TIssue extends TTask implements Issue {
@Prop(Collection(chunter.class.Comment), task.string.TaskComments) @Prop(Collection(chunter.class.Comment), task.string.TaskComments)
comments!: number comments!: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments!: number attachments!: number
@Prop(TypeRef(contact.class.Employee), task.string.TaskAssignee) @Prop(TypeRef(contact.class.Employee), task.string.TaskAssignee)

View File

@ -58,7 +58,7 @@ export class TTelegramMessage extends TAttachedDoc implements TelegramMessage {
@Prop(TypeBoolean(), telegram.string.Incoming) @Prop(TypeBoolean(), telegram.string.Incoming)
incoming!: boolean incoming!: boolean
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeTimestamp(), core.string.Modified) @Prop(TypeTimestamp(), core.string.Modified)
@ -74,7 +74,7 @@ export class TNewTelegramMessage extends TAttachedDoc implements NewTelegramMess
@Prop(TypeString(), telegram.string.Status) @Prop(TypeString(), telegram.string.Status)
status!: 'new' | 'sent' status!: 'new' | 'sent'
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
} }

View File

@ -393,7 +393,7 @@ export class TProject extends TDoc implements Project {
@Prop(Collection(tracker.class.Document), tracker.string.Document) @Prop(Collection(tracker.class.Document), tracker.string.Document)
documents!: number documents!: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeDate(true), tracker.string.StartDate) @Prop(TypeDate(true), tracker.string.StartDate)
@ -430,7 +430,7 @@ export class TSprint extends TDoc implements Sprint {
@Prop(Collection(chunter.class.Comment), chunter.string.Comments) @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments!: number comments!: number
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files) @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
attachments?: number attachments?: number
@Prop(TypeDate(false), tracker.string.StartDate) @Prop(TypeDate(false), tracker.string.StartDate)

View File

@ -114,6 +114,9 @@ export interface Attribute<T extends PropertyType> extends Doc, UXObject {
index?: IndexKind index?: IndexKind
shortLabel?: IntlString shortLabel?: IntlString
isCustom?: boolean isCustom?: boolean
// Extra customization properties
[key: string]: any
} }
/** /**

View File

@ -120,7 +120,7 @@ function getAttrs (target: any, prop: string): Record<string, any> {
* @param icon - * @param icon -
* @returns * @returns
*/ */
export function Prop (type: Type<PropertyType>, label: IntlString, icon?: Asset, shortLabel?: IntlString) { export function Prop (type: Type<PropertyType>, label: IntlString, extra: Partial<Attribute<PropertyType>> = {}) {
return function (target: any, propertyKey: string): void { return function (target: any, propertyKey: string): void {
const txes = getTxes(target) const txes = getTxes(target)
const tx: TxCreateDoc<Attribute<PropertyType>> = { const tx: TxCreateDoc<Attribute<PropertyType>> = {
@ -133,12 +133,11 @@ export function Prop (type: Type<PropertyType>, label: IntlString, icon?: Asset,
objectId: propertyKey as Ref<Attribute<PropertyType>>, objectId: propertyKey as Ref<Attribute<PropertyType>>,
objectClass: core.class.Attribute, objectClass: core.class.Attribute,
attributes: { attributes: {
...extra,
name: propertyKey, name: propertyKey,
index: getIndex(target, propertyKey), index: getIndex(target, propertyKey),
type, type,
label, label,
icon,
shortLabel,
attributeOf: txes._id, // undefined, need to fix later attributeOf: txes._id, // undefined, need to fix later
...getAttrs(target, propertyKey) ...getAttrs(target, propertyKey)
} }

View File

@ -35,8 +35,8 @@
import { getActions, ObjectPresenter } from '@hcengineering/view-resources' import { getActions, ObjectPresenter } from '@hcengineering/view-resources'
import { ActivityKey, DisplayTx } from '../activity' import { ActivityKey, DisplayTx } from '../activity'
import activity from '../plugin' import activity from '../plugin'
import TxViewTx from './TxViewTx.svelte'
import { getValue, TxDisplayViewlet, updateViewlet } from '../utils' import { getValue, TxDisplayViewlet, updateViewlet } from '../utils'
import TxViewTx from './TxViewTx.svelte'
export let tx: DisplayTx export let tx: DisplayTx
export let viewlets: Map<ActivityKey, TxViewlet> export let viewlets: Map<ActivityKey, TxViewlet>
@ -70,7 +70,7 @@
const query = createQuery() const query = createQuery()
function getProps (props: any, edit: boolean): any { function getProps (props: any, edit: boolean): any {
return { ...props, edit } return { ...props, edit, attr: tx.collectionAttribute }
} }
$: updateViewlet(client, viewlets, tx).then((result) => { $: updateViewlet(client, viewlets, tx).then((result) => {

View File

@ -12,7 +12,7 @@
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
import contact, { ChannelProvider } from '@hcengineering/contact' import contact, { ChannelProvider } from '@hcengineering/contact'
import { Button, DropdownLabelsIntl, IconAdd, IconDelete } from '@hcengineering/ui' import { Button, DropdownLabelsIntl, EditBox, IconAdd, IconDelete } from '@hcengineering/ui'
import DropdownLabels from '@hcengineering/ui/src/components/DropdownLabels.svelte' import DropdownLabels from '@hcengineering/ui/src/components/DropdownLabels.svelte'
import bitrix from '../../plugin' import bitrix from '../../plugin'
@ -72,6 +72,8 @@
bind:selected={p.provider} bind:selected={p.provider}
/> />
<DropdownLabels minW0={false} label={bitrix.string.FieldMapping} {items} bind:selected={p.field} /> <DropdownLabels minW0={false} label={bitrix.string.FieldMapping} {items} bind:selected={p.field} />
<EditBox bind:value={p.include} placeholder={getEmbeddedLabel('should...')} />
<EditBox bind:value={p.exclude} placeholder={getEmbeddedLabel('not...')} />
<div class="ml-1"> <div class="ml-1">
<Button <Button

View File

@ -21,6 +21,12 @@
-> ->
{#if mapping.bitrixFields} {#if mapping.bitrixFields}
{p.field ? mapping.bitrixFields[p.field]?.formLabel ?? mapping.bitrixFields[p.field]?.title : p.field ?? ''} {p.field ? mapping.bitrixFields[p.field]?.formLabel ?? mapping.bitrixFields[p.field]?.title : p.field ?? ''}
{#if p.include !== undefined && p.include !== ''}
/{p.include}/gi
{/if}
{#if p.exclude !== undefined && p.exclude !== ''}
^/{p.exclude}/gi
{/if}
{/if} {/if}
</div> </div>
{/each} {/each}

View File

@ -1,6 +1,6 @@
{ {
"name": "@hcengineering/bitrix", "name": "@hcengineering/bitrix",
"version": "0.6.4", "version": "0.6.10",
"main": "lib/index.js", "main": "lib/index.js",
"author": "Anticrm Platform Contributors", "author": "Anticrm Platform Contributors",
"license": "EPL-2.0", "license": "EPL-2.0",

View File

@ -19,7 +19,14 @@ import core, {
import tags, { TagElement } from '@hcengineering/tags' import tags, { TagElement } from '@hcengineering/tags'
import { deepEqual } from 'fast-equals' import { deepEqual } from 'fast-equals'
import { BitrixClient } from './client' import { BitrixClient } from './client'
import { BitrixEntityMapping, BitrixEntityType, BitrixFieldMapping, BitrixSyncDoc, LoginInfo } from './types' import {
BitrixActivity,
BitrixEntityMapping,
BitrixEntityType,
BitrixFieldMapping,
BitrixSyncDoc,
LoginInfo
} from './types'
import { convert, ConvertResult } from './utils' import { convert, ConvertResult } from './utils'
import bitrix from './index' import bitrix from './index'
@ -293,6 +300,7 @@ export async function performSynchronization (ops: {
frontUrl: string frontUrl: string
loginInfo: LoginInfo loginInfo: LoginInfo
monitor: (total: number) => void monitor: (total: number) => void
blobProvider?: (blobRef: any) => Promise<Blob | undefined>
}): Promise<void> { }): Promise<void> {
const commentFields = await ops.bitrixClient.call(BitrixEntityType.Comment + '.fields', {}) const commentFields = await ops.bitrixClient.call(BitrixEntityType.Comment + '.fields', {})
@ -352,7 +360,6 @@ export async function performSynchronization (ops: {
const extraDocs: Doc[] = [] const extraDocs: Doc[] = []
const convertResults: ConvertResult[] = []
const fields = ops.mapping.$lookup?.fields as BitrixFieldMapping[] const fields = ops.mapping.$lookup?.fields as BitrixFieldMapping[]
const toProcess = result.result as any[] const toProcess = result.result as any[]
@ -374,7 +381,9 @@ export async function performSynchronization (ops: {
}) })
let synchronized = 0 let synchronized = 0
while (toProcess.length > 0) { while (toProcess.length > 0) {
console.log('LOAD:', synchronized, toProcess.length) const convertResults: ConvertResult[] = []
console.log('LOAD:', synchronized, added)
synchronized++ synchronized++
const [r] = toProcess.slice(0, 1) const [r] = toProcess.slice(0, 1)
// Convert documents. // Convert documents.
@ -389,7 +398,8 @@ export async function performSynchronization (ops: {
tagElements, tagElements,
userList, userList,
existingDocuments, existingDocuments,
defaultCategories defaultCategories,
ops.blobProvider
) )
if (ops.mapping.comments) { if (ops.mapping.comments) {
res.comments = await ops.bitrixClient res.comments = await ops.bitrixClient
@ -419,15 +429,48 @@ export async function performSynchronization (ops: {
return c return c
}) })
}) })
const communications = await ops.bitrixClient.call('crm.activity.list', {
order: { ID: 'DESC' },
filter: {
OWNER_ID: res.document.bitrixId
},
select: ['*', 'COMMUNICATIONS']
})
const cr = Array.isArray(communications.result)
? (communications.result as BitrixActivity[])
: [communications.result as BitrixActivity]
for (const comm of cr) {
const c: Comment & { bitrixId: string, type: string } = {
_id: generateId(),
_class: chunter.class.Comment,
message: `e-mail:<br/>
Subject: ${comm.SUBJECT}
${comm.DESCRIPTION}`,
bitrixId: comm.ID,
type: 'email',
attachedTo: res.document._id,
attachedToClass: res.document._class,
collection: 'comments',
space: res.document.space,
modifiedBy: userList.get(comm.AUTHOR_ID) ?? core.account.System,
modifiedOn: new Date(comm.CREATED ?? new Date().toString()).getTime()
}
res.comments?.push(c)
}
} }
convertResults.push(res) convertResults.push(res)
extraDocs.push(...res.extraDocs) extraDocs.push(...res.extraDocs)
added++ added++
const total = result.total
await syncPlatform(ops.client, ops.mapping, convertResults, ops.loginInfo, ops.frontUrl, () => {
ops.monitor?.(total)
})
if (added >= ops.limit) { if (added >= ops.limit) {
break break
} }
} catch (err: any) { } catch (err: any) {
console.log('failed to obtain data for', r) console.log('failed to obtain data for', r, err)
await new Promise((resolve) => { await new Promise((resolve) => {
// Sleep for a while // Sleep for a while
setTimeout(resolve, 1000) setTimeout(resolve, 1000)
@ -435,10 +478,6 @@ export async function performSynchronization (ops: {
} }
toProcess.splice(0, 1) toProcess.splice(0, 1)
} }
const total = result.total
await syncPlatform(ops.client, ops.mapping, convertResults, ops.loginInfo, ops.frontUrl, () => {
ops.monitor?.(total)
})
processed = result.next processed = result.next
} }

View File

@ -202,6 +202,8 @@ export interface CreateTagOperation {
export interface ChannelFieldMapping { export interface ChannelFieldMapping {
provider: Ref<ChannelProvider> provider: Ref<ChannelProvider>
field: string field: string
include?: string // Regexp pattern to match value.
exclude?: string // Regexp pattern to match value.
} }
/** /**
@ -230,3 +232,14 @@ export interface BitrixFieldMapping extends AttachedDoc {
operation: CopyValueOperation | CreateTagOperation | CreateChannelOperation | DownloadAttachmentOperation operation: CopyValueOperation | CreateTagOperation | CreateChannelOperation | DownloadAttachmentOperation
} }
/**
* @public
*/
export interface BitrixActivity {
ID: string
SUBJECT: string
DESCRIPTION: string
AUTHOR_ID: string
CREATED: number
}

View File

@ -183,6 +183,15 @@ export async function convert (
if (lval != null && lval !== '') { if (lval != null && lval !== '') {
const vals = Array.isArray(lval) ? lval : [lval] const vals = Array.isArray(lval) ? lval : [lval]
for (const llVal of vals) { for (const llVal of vals) {
const svalue = typeof llVal === 'string' ? llVal : `${JSON.stringify(llVal)}`
if (f.include != null || f.exclude != null) {
if (f.include !== undefined && svalue.match(f.include) == null) {
continue
}
if (f.exclude !== undefined && svalue.match(f.exclude) != null) {
continue
}
}
const c: Channel = { const c: Channel = {
_id: generateId(), _id: generateId(),
_class: contact.class.Channel, _class: contact.class.Channel,
@ -190,7 +199,7 @@ export async function convert (
attachedToClass: attr.attributeOf, attachedToClass: attr.attributeOf,
collection: attr.name, collection: attr.name,
modifiedBy: document.modifiedBy, modifiedBy: document.modifiedBy,
value: llVal, value: svalue,
provider: f.provider, provider: f.provider,
space: document.space, space: document.space,
modifiedOn: document.modifiedOn modifiedOn: document.modifiedOn

View File

@ -169,10 +169,11 @@
<div class="flex-row-center caption-color states"> <div class="flex-row-center caption-color states">
<div class="antiStatesBar mask-none {stepStyle}"> <div class="antiStatesBar mask-none {stepStyle}">
{#each visibleCategories as item, i} {#each visibleCategories as item, i}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
bind:this={visibleCategoriesRef[i]} bind:this={visibleCategoriesRef[i]}
class="categoryElement flex-center" class="categoryElement flex-center"
label={item.label} id={item.label}
style={getTagStyle(getPlatformColorForText(item.label), item._id === category)} style={getTagStyle(getPlatformColorForText(item.label), item._id === category)}
on:click={(evt) => { on:click={(evt) => {
if (mode === 'category') { if (mode === 'category') {

View File

@ -102,6 +102,7 @@
<div class="flex-row-center"> <div class="flex-row-center">
<div class="flex-col"> <div class="flex-col">
<div class="fs-title flex-row-center"> <div class="fs-title flex-row-center">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
class="color" class="color"
style={getTagStyle(getPlatformColor(data.color))} style={getTagStyle(getPlatformColor(data.color))}

View File

@ -38,6 +38,7 @@
}) })
</script> </script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
class="labels-container" class="labels-container"
style:justify-content={kind === 'short' ? 'space-between' : 'flex-start'} style:justify-content={kind === 'short' ? 'space-between' : 'flex-start'}
@ -52,7 +53,7 @@
> >
{#each items as value, i} {#each items as value, i}
<div class="label-box wrap-{kind}"> <div class="label-box wrap-{kind}">
<TagReferencePresenter {value} kind={'kanban-labels'} bind:realWidth={widths[i]} /> <TagReferencePresenter attr={undefined} {value} kind={'kanban-labels'} bind:realWidth={widths[i]} />
</div> </div>
{/each} {/each}
</div> </div>

View File

@ -24,12 +24,13 @@
export let element: TagElement | undefined = undefined export let element: TagElement | undefined = undefined
export let action: Asset | AnySvelteComponent | undefined = undefined export let action: Asset | AnySvelteComponent | undefined = undefined
export let selected: boolean = false export let selected: boolean = false
export let schema: '0' | '3' | '9' = '9'
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: name = element?.title ?? tag?.title ?? 'New item' $: name = element?.title ?? tag?.title ?? 'New item'
$: tagIcon = tagLevel[(((tag?.weight ?? 0) % 3) + 1) as 1 | 2 | 3] $: tagIcon = schema === '3' ? undefined : tagLevel[(((tag?.weight ?? 0) % 3) + 1) as 1 | 2 | 3]
</script> </script>
<div <div
@ -45,7 +46,7 @@
> >
{name} {name}
<span class="ml-1"> <span class="ml-1">
{#if tag} {#if tag && tagIcon && schema !== '0'}
<Icon icon={tagIcon} size={'small'} /> <Icon icon={tagIcon} size={'small'} />
{/if} {/if}
</span> </span>

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { AnyAttribute } from '@hcengineering/core'
import type { TagReference } from '@hcengineering/tags' import type { TagReference } from '@hcengineering/tags'
import { getPlatformColor, Icon, IconClose, resizeObserver } from '@hcengineering/ui' import { getPlatformColor, Icon, IconClose, resizeObserver } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
@ -23,6 +24,7 @@
export let isEditable: boolean = false export let isEditable: boolean = false
export let kind: 'labels' | 'kanban-labels' | 'skills' = 'skills' export let kind: 'labels' | 'kanban-labels' | 'skills' = 'skills'
export let realWidth: number | undefined = undefined export let realWidth: number | undefined = undefined
export let attr: AnyAttribute | undefined
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: tagIcon = tagLevel[(((value?.weight ?? 0) % 3) + 1) as 1 | 2 | 3] $: tagIcon = tagLevel[(((value?.weight ?? 0) % 3) + 1) as 1 | 2 | 3]
@ -30,7 +32,7 @@
{#if value} {#if value}
{#if kind === 'skills'} {#if kind === 'skills'}
<TagItem tag={value} /> <TagItem tag={value} schema={attr?.schema ?? '0'} />
{:else if kind === 'kanban-labels'} {:else if kind === 'kanban-labels'}
<button <button
class="label-container" class="label-container"

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { IntlString } from '@hcengineering/platform' import { IntlString } from '@hcengineering/platform'
import { Doc, Ref } from '@hcengineering/core' import { AnyAttribute, Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import type { TagReference, TagElement } from '@hcengineering/tags' import type { TagReference, TagElement } from '@hcengineering/tags'
import tags from '@hcengineering/tags' import tags from '@hcengineering/tags'
@ -11,6 +11,7 @@
export let object: Doc export let object: Doc
export let label: IntlString export let label: IntlString
export let isEditable: boolean = true export let isEditable: boolean = true
export let attr: AnyAttribute | undefined = undefined
let items: TagReference[] = [] let items: TagReference[] = []
const query = createQuery() const query = createQuery()
@ -33,7 +34,7 @@
<div class="flex-row-center flex-wrap"> <div class="flex-row-center flex-wrap">
{#each items as value} {#each items as value}
<div class="step-container"> <div class="step-container">
<TagReferencePresenter {value} {isEditable} kind={'labels'} on:remove={(res) => removeTag(res.detail)} /> <TagReferencePresenter {attr} {value} {isEditable} kind={'labels'} on:remove={(res) => removeTag(res.detail)} />
</div> </div>
{/each} {/each}
{#if isEditable} {#if isEditable}

View File

@ -37,6 +37,7 @@
export let key: KeyedAttribute export let key: KeyedAttribute
export let showTitle = true export let showTitle = true
export let elements: Map<Ref<TagElement>, TagElement> export let elements: Map<Ref<TagElement>, TagElement>
export let schema: '3' | '9' = key.attr.schema ?? '9'
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -131,6 +132,7 @@
<TagItem <TagItem
{tag} {tag}
element={elements.get(tag.tag)} element={elements.get(tag.tag)}
{schema}
action={IconClose} action={IconClose}
on:action={() => { on:action={() => {
removeTag(tag._id) removeTag(tag._id)
@ -138,7 +140,7 @@
on:click={(evt) => { on:click={(evt) => {
showPopup( showPopup(
WeightPopup, WeightPopup,
{ value: tag.weight ?? 1, format: 'number' }, { value: tag.weight ?? 1, format: 'number', schema },
getEventPopupPositionElement(evt), getEventPopupPositionElement(evt),
(res) => { (res) => {
if (Number.isFinite(res) && res >= 0 && res <= 8) { if (Number.isFinite(res) && res >= 0 && res <= 8) {

View File

@ -20,13 +20,13 @@
import { import {
Button, Button,
CheckBox, CheckBox,
deviceOptionsStore,
getEventPopupPositionElement, getEventPopupPositionElement,
getPlatformColor, getPlatformColor,
Label, Label,
Loading, Loading,
resizeObserver, resizeObserver,
showPopup, showPopup
deviceOptionsStore
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { Filter } from '@hcengineering/view' import { Filter } from '@hcengineering/view'
import { FilterQuery } from '@hcengineering/view-resources' import { FilterQuery } from '@hcengineering/view-resources'
@ -111,10 +111,12 @@
categories = categories categories = categories
} }
$: schema = filter.key.attribute.schema ?? '9'
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
getValues(search) getValues(search)
$: tagLevelIcon = tagLevel[((level % 3) + 1) as 1 | 2 | 3] $: tagLevelIcon = schema === '3' ? undefined : tagLevel[((level % 3) + 1) as 1 | 2 | 3]
$: tagLevelLabel = [tags.string.Initial, tags.string.Meaningfull, tags.string.Expert][Math.floor(level / 3)] $: tagLevelLabel = [tags.string.Initial, tags.string.Meaningfull, tags.string.Expert][Math.floor(level / 3)]
</script> </script>
@ -135,7 +137,7 @@
label={tagLevelLabel} label={tagLevelLabel}
icon={tagLevelIcon} icon={tagLevelIcon}
on:click={(evt) => { on:click={(evt) => {
showPopup(WeightPopup, { value: level }, getEventPopupPositionElement(evt), (res) => { showPopup(WeightPopup, { value: level, schema }, getEventPopupPositionElement(evt), (res) => {
if (Number.isFinite(res) && res >= 0 && res <= 8) { if (Number.isFinite(res) && res >= 0 && res <= 8) {
if (res != null) { if (res != null) {
level = res level = res

View File

@ -126,16 +126,12 @@
props: { edit: true, keyTitle }, props: { edit: true, keyTitle },
sortingKey: 'title' sortingKey: 'title'
}, },
...(category === undefined {
? [ key: '$lookup.category',
{ presenter: tags.component.CategoryPresenter,
key: '$lookup.category', sortingKey: 'category',
presenter: tags.component.CategoryPresenter, label: tags.string.CategoryLabel
sortingKey: 'category', },
label: tags.string.CategoryLabel
}
]
: []),
{ {
key: '', key: '',
presenter: tags.component.TagElementCountPresenter, presenter: tags.component.TagElementCountPresenter,

View File

@ -19,6 +19,7 @@
import { tagLevel } from '../utils' import { tagLevel } from '../utils'
export let value: number | undefined export let value: number | undefined
export let schema: '3' | '9' = '9'
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -29,18 +30,30 @@
<div class="header no-border p-3"> <div class="header no-border p-3">
{#each labels as l, i} {#each labels as l, i}
<div class="flex gap-2 p-1"> <div class="flex gap-2 p-1">
{#each Object.entries(tagLevel) as k, j} {#if schema === '9'}
{@const valueK = i * 3 + j} {#each Object.entries(tagLevel) as k, j}
{@const valueK = i * 3 + j}
<Button
label={l}
icon={k[1]}
size={'small'}
justify={'left'}
selected={value === valueK}
on:click={() => dispatch('close', valueK)}
width={'8rem'}
/>
{/each}
{:else}
{@const valueK = i * 3}
<Button <Button
label={l} label={l}
icon={k[1]}
size={'small'} size={'small'}
justify={'left'} justify={'left'}
selected={value === valueK} selected={value === valueK}
on:click={() => dispatch('close', valueK)} on:click={() => dispatch('close', valueK)}
width={'8rem'} width={'8rem'}
/> />
{/each} {/if}
</div> </div>
{/each} {/each}
</div> </div>

View File

@ -38,8 +38,9 @@ export async function getRefs (filter: Filter, onUpdate: () => void): Promise<Ar
return await promise return await promise
} }
export const tagLevel: Record<1 | 2 | 3, Asset> = { export const tagLevel: Record<0 | 1 | 2 | 3, Asset> = {
3: tags.icon.Level3, 3: tags.icon.Level3,
2: tags.icon.Level2, 2: tags.icon.Level2,
1: tags.icon.Level1 1: tags.icon.Level1,
0: tags.icon.Tags
} }

View File

@ -81,6 +81,7 @@
return { return {
_class, _class,
key: isCollection ? '_id' : key, key: isCollection ? '_id' : key,
attribute,
label: attribute.label, label: attribute.label,
icon: attribute.icon ?? clazz.icon ?? attrOf.icon ?? view.icon.Setting, icon: attribute.icon ?? clazz.icon ?? attrOf.icon ?? view.icon.Setting,
component: filter.component component: filter.component

View File

@ -47,6 +47,7 @@ import type {
export interface KeyFilter { export interface KeyFilter {
_class: Ref<Class<Doc>> _class: Ref<Class<Doc>>
key: string key: string
attribute: AnyAttribute
component: AnyComponent component: AnyComponent
label: IntlString label: IntlString
icon: Asset | AnySvelteComponent | undefined icon: Asset | AnySvelteComponent | undefined

View File

@ -31,7 +31,7 @@ test.describe('workbench tests', () => {
await expect(page).toHaveURL(`${PlatformURI}/workbench/sanity-ws/recruit/vacancies`) await expect(page).toHaveURL(`${PlatformURI}/workbench/sanity-ws/recruit/vacancies`)
// Click text=Software Engineer // Click text=Software Engineer
await page.click('text=Software Engineer') await page.click('text=Software Engineer')
await expect(page.locator('text=Software Engineer')).toBeVisible() await expect(page.locator('text=Software Engineer')).toBeDefined()
await expect(page.locator('text="APP-1"')).toBeDefined() await expect(page.locator('text="APP-1"')).toBeDefined()
// await page.click('[name="tooltip-task:string:Kanban"]') // await page.click('[name="tooltip-task:string:Kanban"]')
await page.click('.tablist-container div:nth-child(2)') await page.click('.tablist-container div:nth-child(2)')