Card fixes (#8136)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2025-03-05 21:03:39 +07:00
committed by GitHub
parent c92fdd07a4
commit efdbf4f2a2
22 changed files with 119 additions and 87 deletions
+1 -4
View File
@@ -12,7 +12,7 @@
// limitations under the License.
import activity from '@hcengineering/activity'
import { CardEvents, cardId, type Card, type MasterTag, type Tag } from '@hcengineering/card'
import { CardEvents, cardId, DOMAIN_CARD, type Card, type MasterTag, type Tag } from '@hcengineering/card'
import chunter from '@hcengineering/chunter'
import contact from '@hcengineering/contact'
import core, {
@@ -20,7 +20,6 @@ import core, {
DOMAIN_MODEL,
IndexKind,
type CollectionSize,
type Domain,
type MarkupBlobRef,
type Rank,
type Ref
@@ -48,8 +47,6 @@ import card from './plugin'
export { cardId } from '@hcengineering/card'
const DOMAIN_CARD = 'card' as Domain
@Model(card.class.MasterTag, core.class.Class)
export class TMasterTag extends TClass implements MasterTag {}
+1 -1
View File
@@ -46,7 +46,7 @@ export function createModel (builder: Builder): void {
isAsync: true,
txMatch: {
_class: core.class.TxRemoveDoc,
objectClass: card.class.MasterTag
objectClass: { $in: [card.class.MasterTag, card.class.Tag] }
}
})
+4 -4
View File
@@ -23,17 +23,17 @@
"Archived": "Archivováno",
"ClassLabel": "Typ",
"ClassPropertyLabel": "Štítek",
"String": "Řetězec",
"String": "Text",
"Markup": "Značení",
"Number": "Číslo",
"Boolean": "Boolean",
"Boolean": "Zaškrtávací políčko",
"Timestamp": "Časové razítko",
"Date": "Datum",
"IntlString": "Mezinárodní řetězec",
"Ref": "Odkaz",
"Collection": "Kolekce",
"Array": "Pole",
"Enum": "Výčet",
"Array": "Vícenásobný výběr",
"Enum": "Výběr",
"Members": "Členové",
"Hyperlink": "URL",
"Collaborative": "Spolupráce",
+3 -3
View File
@@ -26,14 +26,14 @@
"String": "Text",
"Markup": "Formatierter Text",
"Number": "Zahl",
"Boolean": "Boolesch",
"Boolean": "Kontrollkästchen",
"Timestamp": "Zeitstempel",
"Date": "Datum",
"IntlString": "Internationaler Text",
"Ref": "Referenz",
"Collection": "Sammlung",
"Array": "Liste",
"Enum": "Aufzählung",
"Array": "Mehrfachauswahl",
"Enum": "Auswahl",
"Members": "Mitglieder",
"Hyperlink": "URL",
"MarkupBlobRef": "Kollaborativ",
+5 -5
View File
@@ -23,17 +23,17 @@
"Archived": "Archived",
"ClassLabel": "Type",
"ClassPropertyLabel": "Label",
"String": "String",
"String": "Text",
"Markup": "Markup",
"Number": "Number",
"Boolean": "Boolean",
"Boolean": "Checkbox",
"Timestamp": "Timestamp",
"Date": "Date",
"IntlString": "IntlString",
"Ref": "Ref",
"Ref": "Reference",
"Collection": "Collection",
"Array": "Array",
"Enum": "Enum",
"Array": "Multi-Select",
"Enum": "Select",
"Members": "Members",
"Hyperlink": "URL",
"MarkupBlobRef": "Collaborative",
+5 -5
View File
@@ -16,17 +16,17 @@
"Archived": "Archivado",
"ClassLabel": "Tipo",
"ClassPropertyLabel": "Etiqueta",
"String": "Cadena de texto",
"String": "Texto",
"Markup": "Marcado",
"Number": "Número",
"Boolean": "Booleano",
"Boolean": "Casilla de verificación",
"Timestamp": "Marca de tiempo",
"Date": "Fecha",
"IntlString": "Cadena de texto internacionalizada",
"Ref": "Ref.",
"Ref": "Referencia",
"Collection": "Colección",
"Array": "Arreglo",
"Enum": "Enum.",
"Array": "Selección múltiple",
"Enum": "Selección",
"Members": "Miembros",
"Hyperlink": "Enlace",
"MarkupBlobRef": "Colaborativo",
+5 -5
View File
@@ -23,17 +23,17 @@
"Archived": "Archivé",
"ClassLabel": "Type",
"ClassPropertyLabel": "Label",
"String": "Chaîne",
"String": "Texte",
"Markup": "Balise",
"Number": "Nombre",
"Boolean": "Booléen",
"Boolean": "Case à cocher",
"Timestamp": "Horodatage",
"Date": "Date",
"IntlString": "Chaîne internationale",
"Ref": "Réf",
"Ref": "Référence",
"Collection": "Collection",
"Array": "Tableau",
"Enum": "Énumération",
"Array": "Sélection multiple",
"Enum": "Sélection",
"Members": "Membres",
"Hyperlink": "URL",
"MarkupBlobRef": "Collaboratif",
+4 -4
View File
@@ -23,17 +23,17 @@
"Archived": "Archiviato",
"ClassLabel": "Tipo",
"ClassPropertyLabel": "Etichetta",
"String": "Stringa",
"String": "Testo",
"Markup": "Markup",
"Number": "Numero",
"Boolean": "Booleano",
"Boolean": "Casella di controllo",
"Timestamp": "Timestamp",
"Date": "Data",
"IntlString": "StringaIntl",
"Ref": "Riferimento",
"Collection": "Collezione",
"Array": "Array",
"Enum": "Enumeratore",
"Array": "Selezione multipla",
"Enum": "Selezione",
"Members": "Membri",
"Hyperlink": "Hyperlink",
"Collaborative": "Collaborativo",
+5 -5
View File
@@ -16,17 +16,17 @@
"Archived": "Arquivado",
"ClassLabel": "Tipo",
"ClassPropertyLabel": "Rótulo",
"String": "Cadeia de texto",
"String": "Texto",
"Markup": "Marcação",
"Number": "Número",
"Boolean": "Booleano",
"Boolean": "Caixa de seleção",
"Timestamp": "Marca de data/hora",
"Date": "Data",
"IntlString": "Cadeia de texto internacionalizada",
"Ref": "Ref.",
"Ref": "Referência",
"Collection": "Coleção",
"Array": "Matriz",
"Enum": "Enumeração",
"Array": "Seleção múltipla",
"Enum": "Seleção",
"Members": "Membros",
"Hyperlink": "URL",
"MarkupBlobRef": "Colaborativo",
+4 -4
View File
@@ -23,17 +23,17 @@
"Archived": "Архивный",
"ClassLabel": "Тип",
"ClassPropertyLabel": "Название",
"String": "Строка",
"String": "Текст",
"Markup": "Разметка",
"Number": "Число",
"Boolean": "Логическое",
"Boolean": "Чекбокс",
"Timestamp": "Временная отметка",
"Date": "Дата",
"IntlString": "Интернационализированная строка",
"Ref": "Ссылка",
"Collection": "Коллекция",
"Array": "Массив",
"Enum": "Справочник",
"Array": "Множественный выбор",
"Enum": "Выбор",
"Members": "Участники",
"Hyperlink": "URL",
"MarkupBlobRef": "Коллаборативный",
+4 -4
View File
@@ -23,17 +23,17 @@
"Archived": "已归档",
"ClassLabel": "类型",
"ClassPropertyLabel": "标签",
"String": "字符串",
"String": "字",
"Markup": "标记",
"Number": "数字",
"Boolean": "布尔值",
"Boolean": "复选框",
"Timestamp": "时间戳",
"Date": "日期",
"IntlString": "国际字符串",
"Ref": "引用",
"Collection": "集合",
"Array": "数组",
"Enum": "枚举",
"Array": "多选",
"Enum": "选择",
"Members": "成员",
"Hyperlink": "URL",
"MarkupBlobRef": "协作",
@@ -45,13 +45,13 @@
return
}
if (multiselect && Array.isArray(selected)) {
const index = selected.indexOf(item.id)
if (index !== -1) {
selected.splice(index, 1)
selected = selected
const set = new Set(selected)
if (set.has(item.id)) {
set.delete(item.id)
} else {
selected = selected === undefined ? [item.id] : [...selected, item.id]
set.add(item.id)
}
selected = Array.from(set)
dispatch('update', selected)
} else {
dispatch('close', item.id)
@@ -50,7 +50,7 @@
async function handleDelete (): Promise<void> {
await deleteMasterTag(masterTag, () => {
const loc = getCurrentLocation()
if (masterTag.extends !== card.class.MasterTag && masterTag.extends !== undefined) {
if (masterTag.extends !== card.class.Card && masterTag.extends !== undefined) {
loc.path[4] = masterTag.extends
} else {
loc.path.length = 3
@@ -34,12 +34,18 @@
const query = createQuery()
query.query(core.class.Association, {}, (res) => {
console.log(res)
associations = res
})
function getClassLabel (_class: Ref<Class<Doc>>): IntlString {
const _classLabel = hierarchy.getClass(_class)
return _classLabel.label
try {
const _classLabel = hierarchy.getClass(_class)
return _classLabel.label
} catch (err) {
console.error(err)
return core.string.Class
}
}
function addRelation (): void {
+3 -1
View File
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Class, Mixin, Doc, Ref, MarkupBlobRef, Rank } from '@hcengineering/core'
import { Class, Mixin, Doc, Ref, MarkupBlobRef, Rank, Domain } from '@hcengineering/core'
import { Asset, IntlString, plugin, Plugin } from '@hcengineering/platform'
import type { AnyComponent } from '@hcengineering/ui'
@@ -42,6 +42,8 @@ export interface MasterTagEditorSection extends Doc {
*/
export const cardId = 'card' as Plugin
export const DOMAIN_CARD = 'card' as Domain
/**
* @public
*/
@@ -57,18 +57,22 @@
if (added.has(_id) || ignore.has(_id)) continue
const _class = hierarchy.getClass(_id)
if (_class.label === undefined) continue
if (viewlets.has(hierarchy.getBaseClass(_id))) {
added.add(_id)
const descendants = hierarchy.getDescendants(_id)
const toAdd: Class<Doc>[] = []
for (const desc of descendants) {
if (added.has(desc) || ignore.has(desc)) continue
const _class = hierarchy.getClass(desc)
if (_class.label === undefined) continue
added.add(desc)
toAdd.push(_class)
try {
if (viewlets.has(hierarchy.getBaseClass(_id))) {
added.add(_id)
const descendants = hierarchy.getDescendants(_id)
const toAdd: Class<Doc>[] = []
for (const desc of descendants) {
if (added.has(desc) || ignore.has(desc)) continue
const _class = hierarchy.getClass(desc)
if (_class.label === undefined) continue
added.add(desc)
toAdd.push(_class)
}
base.set(_id, toAdd)
}
base.set(_id, toAdd)
} catch (err) {
ignore.add(_id)
}
}
const result: [DropdownIntlItem, DropdownIntlItem[]][] = []
@@ -85,6 +85,7 @@
is={getComponent(selected)}
props={{
type: type?.of,
nested: true,
editable,
kind,
size
@@ -26,6 +26,7 @@
export let type: EnumOf | undefined
export let editable: boolean = true
export let value: Enum | undefined
export let nested: boolean = false
export let defaultValue: string | undefined
export let kind: ButtonKind = 'no-border'
export let size: ButtonSize = 'small'
@@ -66,7 +67,9 @@
{#if editable}
<EnumSelect label={core.string.Enum} bind:value {create} {kind} {size} />
{:else if value}
{value.name}
<div>
{value.name}
</div>
{/if}
{#if value}
<Button
@@ -79,7 +82,7 @@
{/if}
</div>
</div>
{#if value && type}
{#if value && type && !nested}
<div class="hulyModal-content__settingsSet-line">
<span class="label">
<Label label={setting.string.DefaultValue} />
@@ -2,7 +2,7 @@
import core, { Association, Doc, WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Button, IconAdd, Label, Section, showPopup } from '@hcengineering/ui'
import { Button, IconAdd, Label, Scroller, Section, showPopup } from '@hcengineering/ui'
import { showMenu } from '../actions'
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
import DocTable from './DocTable.svelte'
@@ -116,16 +116,18 @@
</svelte:fragment>
<svelte:fragment slot="content">
<div class="antiSection-empty solid flex-col mt-3">
{#if docs?.length > 0 && config != null}
{#if docs?.length > 0 && config != null}
<Scroller horizontal>
<DocTable objects={docs} {_class} {config} {onContextMenu} />
{:else if !readonly}
</Scroller>
{:else if !readonly}
<div class="antiSection-empty solid clear-mins mt-3">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<span class="over-underline content-color" on:click={add}>
<Label label={core.string.AddRelation} />
</span>
{/if}
</div>
</div>
{/if}
</svelte:fragment>
</Section>
+18 -3
View File
@@ -13,8 +13,8 @@
// limitations under the License.
//
import core, { AnyAttribute, Tx, TxCreateDoc, TxProcessor, TxRemoveDoc } from '@hcengineering/core'
import card, { MasterTag } from '@hcengineering/card'
import core, { AnyAttribute, Class, Doc, Ref, Tx, TxCreateDoc, TxProcessor, TxRemoveDoc } from '@hcengineering/core'
import card, { DOMAIN_CARD, MasterTag } from '@hcengineering/card'
import view from '@hcengineering/view'
import { TriggerControl } from '@hcengineering/server-core'
@@ -86,7 +86,7 @@ async function OnMasterTagRemove (ctx: TxRemoveDoc<MasterTag>[], control: Trigge
const res: Tx[] = []
// should remove objects if masterTag
if (removedTag._class === card.class.MasterTag) {
const cards = await control.findAll(control.ctx, removeTx.objectId, {})
const cards = await control.lowLevel.rawFindAll(DOMAIN_CARD, { _class: removedTag._id as Ref<Class<Doc>> })
for (const card of cards) {
res.push(control.txFactory.createTxRemoveDoc(card._class, card.space, card._id))
}
@@ -96,6 +96,21 @@ async function OnMasterTagRemove (ctx: TxRemoveDoc<MasterTag>[], control: Trigge
if (des === removeTx.objectId) continue
res.push(control.txFactory.createTxRemoveDoc(card.class.MasterTag, core.space.Model, des))
}
const removedRelation = new Set()
const relationsA = control.modelDb.findAllSync(core.class.Association, {
classA: removeTx.objectId
})
for (const rel of relationsA) {
removedRelation.add(rel._id)
res.push(control.txFactory.createTxRemoveDoc(core.class.Association, core.space.Model, rel._id))
}
const relationsB = control.modelDb.findAllSync(core.class.Association, {
classB: removeTx.objectId
})
for (const rel of relationsB) {
if (removedRelation.has(rel._id)) continue
res.push(control.txFactory.createTxRemoveDoc(core.class.Association, core.space.Model, rel._id))
}
return res
}
+9 -7
View File
@@ -51,13 +51,15 @@ export class TxMiddleware extends BaseMiddleware implements Middleware {
async tx (ctx: MeasureContext, txes: Tx[]): Promise<TxMiddlewareResult> {
const txToStore: Tx[] = []
for (const tx of txes) {
if (tx.space !== core.space.DerivedTx && TxProcessor.isExtendsCUD(tx._class)) {
const objectClass = (tx as TxCUD<Doc>).objectClass
if (
objectClass !== core.class.BenchmarkDoc &&
this.context.hierarchy.findDomain(objectClass) !== DOMAIN_TRANSIENT
) {
txToStore.push(tx)
if (TxProcessor.isExtendsCUD(tx._class)) {
if (tx.space !== core.space.DerivedTx || tx.objectSpace === core.space.Model) {
const objectClass = (tx as TxCUD<Doc>).objectClass
if (
objectClass !== core.class.BenchmarkDoc &&
this.context.hierarchy.findDomain(objectClass) !== DOMAIN_TRANSIENT
) {
txToStore.push(tx)
}
}
}
}
@@ -19,13 +19,13 @@ export class ClassProperties {
}
url = (): Locator => this.page.getByRole('button', { name: 'URL' })
string = (): Locator => this.page.getByRole('button', { name: 'String' })
boolean = (): Locator => this.page.getByRole('button', { name: 'Boolean' })
string = (): Locator => this.page.getByRole('button', { name: 'Text', exact: true })
boolean = (): Locator => this.page.getByRole('button', { name: 'Checkbox' })
number = (): Locator => this.page.getByRole('button', { name: 'Number' })
date = (): Locator => this.page.getByRole('button', { name: 'Date' })
ref = (): Locator => this.page.getByRole('button', { name: 'Ref' })
array = (): Locator => this.page.getByRole('button', { name: 'Array' })
enum = (): Locator => this.page.getByRole('button', { name: 'Enum', exact: true })
ref = (): Locator => this.page.getByRole('button', { name: 'Reference' })
array = (): Locator => this.page.getByRole('button', { name: 'Multi-Select' })
enum = (): Locator => this.page.getByRole('button', { name: 'Select', exact: true })
addedCustomAttribute = (attribute: string): Locator => this.page.getByRole('button', { name: attribute })
inputName = (): Locator => this.page.getByPlaceholder('Name')