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
models
board/src
calendar/src
chunter/src
contact/src
document/src
gmail/src
hr/src
inventory/src
lead/src
recruit/src
task/src
telegram/src
tracker/src
packages
core/src
model/src
plugins
tests/sanity/tests

View File

@ -89,7 +89,7 @@ export class TCard extends TTask implements Card {
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
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
@Prop(TypeRef(contact.class.Employee), board.string.Assignee)

View File

@ -62,7 +62,7 @@ export class TEvent extends TAttachedDoc implements Event {
@Index(IndexKind.FullText)
description!: Markup
@Prop(TypeString(), calendar.string.Location, calendar.icon.Location)
@Prop(TypeString(), calendar.string.Location, { icon: calendar.icon.Location })
@Index(IndexKind.FullText)
location?: string
@ -72,7 +72,7 @@ export class TEvent extends TAttachedDoc implements Event {
@Prop(TypeDate(true), calendar.string.DueTo)
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
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -81,7 +81,7 @@ export class TChunterMessage extends TAttachedDoc implements ChunterMessage {
@Index(IndexKind.FullText)
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
@Prop(TypeRef(core.class.Account), chunter.string.CreateBy)
@ -138,7 +138,7 @@ export class TComment extends TAttachedDoc implements Comment {
@Index(IndexKind.FullText)
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
}

View File

@ -85,7 +85,7 @@ export class TContact extends TDoc implements Contact {
@Prop(Collection(contact.class.Channel), contact.string.ContactInfo)
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
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -89,7 +89,7 @@ export class TDocumentVersion extends TAttachedDoc implements DocumentVersion {
@Hidden()
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
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -116,7 +116,7 @@ export class TDocument extends TDoc implements Document {
@Hidden()
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
@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)
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
@Prop(TypeTimestamp(), core.string.Modified)
@ -109,7 +109,7 @@ export class TNewMessage extends TDoc implements NewMessage {
@Prop(ArrOf(TypeString()), gmail.string.Copy)
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
}

View File

@ -57,7 +57,7 @@ export class TDepartment extends TSpace implements Department {
@Prop(Collection(contact.class.Channel), contact.string.ContactInfo)
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
@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)
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
@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)
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
}

View File

@ -46,7 +46,7 @@ export class TFunnel extends TSpaceWithStates implements Funnel {
@Index(IndexKind.FullText)
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
@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)
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
@Prop(TypeRef(contact.class.Employee), lead.string.Assignee)

View File

@ -64,7 +64,7 @@ export class TVacancy extends TSpaceWithStates implements Vacancy {
@Index(IndexKind.FullText)
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
@Prop(TypeDate(), recruit.string.Due, recruit.icon.Calendar)
@ -74,7 +74,7 @@ export class TVacancy extends TSpaceWithStates implements Vacancy {
@Index(IndexKind.FullText)
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>
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -95,7 +95,9 @@ export class TCandidate extends TPerson implements Candidate {
@Index(IndexKind.FullText)
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
@Prop(TypeBoolean(), recruit.string.Onsite)
@ -108,7 +110,10 @@ export class TCandidate extends TPerson implements Candidate {
@Index(IndexKind.FullText)
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
@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)
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
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)

View File

@ -24,7 +24,7 @@ export class TReview extends TEvent implements Review {
@Index(IndexKind.FullText)
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>
@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)
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
@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)
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
@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)
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
@Prop(TypeTimestamp(), core.string.Modified)
@ -74,7 +74,7 @@ export class TNewTelegramMessage extends TAttachedDoc implements NewTelegramMess
@Prop(TypeString(), telegram.string.Status)
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
}

View File

@ -393,7 +393,7 @@ export class TProject extends TDoc implements Project {
@Prop(Collection(tracker.class.Document), tracker.string.Document)
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
@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)
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
@Prop(TypeDate(false), tracker.string.StartDate)

View File

@ -114,6 +114,9 @@ export interface Attribute<T extends PropertyType> extends Doc, UXObject {
index?: IndexKind
shortLabel?: IntlString
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 -
* @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 {
const txes = getTxes(target)
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>>,
objectClass: core.class.Attribute,
attributes: {
...extra,
name: propertyKey,
index: getIndex(target, propertyKey),
type,
label,
icon,
shortLabel,
attributeOf: txes._id, // undefined, need to fix later
...getAttrs(target, propertyKey)
}

View File

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

View File

@ -12,7 +12,7 @@
import { getClient } from '@hcengineering/presentation'
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 bitrix from '../../plugin'
@ -72,6 +72,8 @@
bind:selected={p.provider}
/>
<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">
<Button

View File

@ -21,6 +21,12 @@
->
{#if mapping.bitrixFields}
{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}
</div>
{/each}

View File

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

View File

@ -19,7 +19,14 @@ import core, {
import tags, { TagElement } from '@hcengineering/tags'
import { deepEqual } from 'fast-equals'
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 bitrix from './index'
@ -293,6 +300,7 @@ export async function performSynchronization (ops: {
frontUrl: string
loginInfo: LoginInfo
monitor: (total: number) => void
blobProvider?: (blobRef: any) => Promise<Blob | undefined>
}): Promise<void> {
const commentFields = await ops.bitrixClient.call(BitrixEntityType.Comment + '.fields', {})
@ -352,7 +360,6 @@ export async function performSynchronization (ops: {
const extraDocs: Doc[] = []
const convertResults: ConvertResult[] = []
const fields = ops.mapping.$lookup?.fields as BitrixFieldMapping[]
const toProcess = result.result as any[]
@ -374,7 +381,9 @@ export async function performSynchronization (ops: {
})
let synchronized = 0
while (toProcess.length > 0) {
console.log('LOAD:', synchronized, toProcess.length)
const convertResults: ConvertResult[] = []
console.log('LOAD:', synchronized, added)
synchronized++
const [r] = toProcess.slice(0, 1)
// Convert documents.
@ -389,7 +398,8 @@ export async function performSynchronization (ops: {
tagElements,
userList,
existingDocuments,
defaultCategories
defaultCategories,
ops.blobProvider
)
if (ops.mapping.comments) {
res.comments = await ops.bitrixClient
@ -419,15 +429,48 @@ export async function performSynchronization (ops: {
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)
extraDocs.push(...res.extraDocs)
added++
const total = result.total
await syncPlatform(ops.client, ops.mapping, convertResults, ops.loginInfo, ops.frontUrl, () => {
ops.monitor?.(total)
})
if (added >= ops.limit) {
break
}
} catch (err: any) {
console.log('failed to obtain data for', r)
console.log('failed to obtain data for', r, err)
await new Promise((resolve) => {
// Sleep for a while
setTimeout(resolve, 1000)
@ -435,10 +478,6 @@ export async function performSynchronization (ops: {
}
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
}

View File

@ -202,6 +202,8 @@ export interface CreateTagOperation {
export interface ChannelFieldMapping {
provider: Ref<ChannelProvider>
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
}
/**
* @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 !== '') {
const vals = Array.isArray(lval) ? lval : [lval]
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 = {
_id: generateId(),
_class: contact.class.Channel,
@ -190,7 +199,7 @@ export async function convert (
attachedToClass: attr.attributeOf,
collection: attr.name,
modifiedBy: document.modifiedBy,
value: llVal,
value: svalue,
provider: f.provider,
space: document.space,
modifiedOn: document.modifiedOn

View File

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

View File

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

View File

@ -38,6 +38,7 @@
})
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="labels-container"
style:justify-content={kind === 'short' ? 'space-between' : 'flex-start'}
@ -52,7 +53,7 @@
>
{#each items as value, i}
<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>
{/each}
</div>

View File

@ -24,12 +24,13 @@
export let element: TagElement | undefined = undefined
export let action: Asset | AnySvelteComponent | undefined = undefined
export let selected: boolean = false
export let schema: '0' | '3' | '9' = '9'
const dispatch = createEventDispatcher()
$: 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>
<div
@ -45,7 +46,7 @@
>
{name}
<span class="ml-1">
{#if tag}
{#if tag && tagIcon && schema !== '0'}
<Icon icon={tagIcon} size={'small'} />
{/if}
</span>

View File

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

View File

@ -1,6 +1,6 @@
<script lang="ts">
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 type { TagReference, TagElement } from '@hcengineering/tags'
import tags from '@hcengineering/tags'
@ -11,6 +11,7 @@
export let object: Doc
export let label: IntlString
export let isEditable: boolean = true
export let attr: AnyAttribute | undefined = undefined
let items: TagReference[] = []
const query = createQuery()
@ -33,7 +34,7 @@
<div class="flex-row-center flex-wrap">
{#each items as value}
<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>
{/each}
{#if isEditable}

View File

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

View File

@ -20,13 +20,13 @@
import {
Button,
CheckBox,
deviceOptionsStore,
getEventPopupPositionElement,
getPlatformColor,
Label,
Loading,
resizeObserver,
showPopup,
deviceOptionsStore
showPopup
} from '@hcengineering/ui'
import { Filter } from '@hcengineering/view'
import { FilterQuery } from '@hcengineering/view-resources'
@ -111,10 +111,12 @@
categories = categories
}
$: schema = filter.key.attribute.schema ?? '9'
const dispatch = createEventDispatcher()
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)]
</script>
@ -135,7 +137,7 @@
label={tagLevelLabel}
icon={tagLevelIcon}
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 (res != null) {
level = res

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ test.describe('workbench tests', () => {
await expect(page).toHaveURL(`${PlatformURI}/workbench/sanity-ws/recruit/vacancies`)
// 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 page.click('[name="tooltip-task:string:Kanban"]')
await page.click('.tablist-container div:nth-child(2)')