diff --git a/models/card/src/index.ts b/models/card/src/index.ts index b59be4050f..266ec64447 100644 --- a/models/card/src/index.ts +++ b/models/card/src/index.ts @@ -12,13 +12,22 @@ // limitations under the License. import activity from '@hcengineering/activity' -import { CardEvents, cardId, DOMAIN_CARD, type Card, type MasterTag, type Tag } from '@hcengineering/card' +import { + CardEvents, + cardId, + DOMAIN_CARD, + type ParentInfo, + type Card, + type MasterTag, + type Tag +} from '@hcengineering/card' import chunter from '@hcengineering/chunter' import contact from '@hcengineering/contact' import core, { AccountRole, DOMAIN_MODEL, IndexKind, + SortingOrder, type CollectionSize, type MarkupBlobRef, type Rank, @@ -32,6 +41,7 @@ import { TypeCollaborativeDoc, TypeRef, TypeString, + UX, type Builder } from '@hcengineering/model' import attachment from '@hcengineering/model-attachment' @@ -54,6 +64,7 @@ export class TMasterTag extends TClass implements MasterTag {} export class TTag extends TMixin implements Tag {} @Model(card.class.Card, core.class.Doc, DOMAIN_CARD) +@UX(card.string.Card, card.icon.Card) export class TCard extends TDoc implements Card { @Prop(TypeRef(card.class.MasterTag), card.string.MasterTag) declare _class: Ref @@ -65,7 +76,8 @@ export class TCard extends TDoc implements Card { @Prop(TypeCollaborativeDoc(), card.string.Content) content!: MarkupBlobRef - parent?: Ref | null + @Prop(TypeRef(card.class.Card), card.string.Parent) + parent?: Ref | null @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) attachments?: number @@ -74,6 +86,10 @@ export class TCard extends TDoc implements Card { @Prop(Collection(time.class.ToDo), getEmbeddedLabel('Action Items')) todos?: CollectionSize + + children?: number + + parentInfo!: ParentInfo[] } @Model(card.class.MasterTagEditorSection, core.class.Doc, DOMAIN_MODEL) @@ -102,6 +118,56 @@ export function createModel (builder: Builder): void { card.app.Card ) + createAction( + builder, + { + action: view.actionImpl.ShowPopup, + actionProps: { + component: card.component.SetParentActionPopup, + element: 'top', + fillProps: { + _objects: 'value' + } + }, + label: card.string.SetParent, + icon: card.icon.MasterTag, + input: 'none', + category: card.category.Card, + target: card.class.Card, + context: { + mode: ['context'], + application: card.app.Card, + group: 'associate' + } + }, + card.action.SetParent + ) + + createAction( + builder, + { + action: view.actionImpl.UpdateDocument, + actionProps: { + key: 'parent', + value: null + }, + query: { + parent: { $ne: null, $exists: true } + }, + label: card.string.UnsetParent, + icon: card.icon.MasterTag, + input: 'none', + category: card.category.Card, + target: card.class.Card, + context: { + mode: ['context'], + application: card.app.Card, + group: 'associate' + } + }, + card.action.UnsetParent + ) + builder.createDoc( view.class.Viewlet, core.space.Model, @@ -109,7 +175,7 @@ export function createModel (builder: Builder): void { attachTo: card.class.Card, descriptor: view.viewlet.Table, configOptions: { - hiddenKeys: ['description', 'title'] + hiddenKeys: ['content', 'title'] }, config: [ '', @@ -121,6 +187,37 @@ export function createModel (builder: Builder): void { card.viewlet.CardTable ) + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: card.class.Card, + descriptor: view.viewlet.List, + viewOptions: { + groupBy: ['_class', 'createdBy', 'modifiedBy'], + orderBy: [ + ['modifiedOn', SortingOrder.Descending], + ['rank', SortingOrder.Ascending] + ], + other: [] + }, + configOptions: { + hiddenKeys: ['content', 'title'] + }, + config: [ + { key: '', props: { showParent: true } }, + '_class', + { key: '', presenter: view.component.RolePresenter, label: card.string.Tags, props: { fullSize: true } }, + { key: '', displayProps: { grow: true } }, + { + key: 'modifiedOn', + displayProps: { fixed: 'right', dividerBefore: true } + } + ] + }, + card.viewlet.CardList + ) + builder.mixin(card.class.Card, core.class.Class, view.mixin.ObjectPresenter, { presenter: card.component.CardPresenter }) diff --git a/models/card/src/plugin.ts b/models/card/src/plugin.ts index f8fc7524d4..6cd690a280 100644 --- a/models/card/src/plugin.ts +++ b/models/card/src/plugin.ts @@ -18,10 +18,9 @@ import card from '@hcengineering/card-resources/src/plugin' import type { Client, Doc, Ref } from '@hcengineering/core' import {} from '@hcengineering/core' import { mergeIds, type Resource } from '@hcengineering/platform' -import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/model-presentation' import { type Location, type ResolvedLocation } from '@hcengineering/ui/src/types' import { type LocationData } from '@hcengineering/workbench' -import { type Action, type ViewAction } from '@hcengineering/view' +import { type ActionCategory, type Action, type ViewAction } from '@hcengineering/view' export default mergeIds(cardId, card, { app: { @@ -31,17 +30,18 @@ export default mergeIds(cardId, card, { DeleteMasterTag: '' as ViewAction }, action: { - DeleteMasterTag: '' as Ref + DeleteMasterTag: '' as Ref, + SetParent: '' as Ref>, + UnsetParent: '' as Ref> + }, + category: { + Card: '' as Ref }, ids: { MasterTags: '' as Ref, ManageMasterTags: '' as Ref, TagRelations: '' as Ref }, - completion: { - CardQuery: '' as Resource, - CardCategory: '' as Ref - }, resolver: { Location: '' as Resource<(loc: Location) => Promise>, LocationData: '' as Resource<(loc: Location) => Promise> diff --git a/models/server-card/src/index.ts b/models/server-card/src/index.ts index 97d76b871c..39b643b81b 100644 --- a/models/server-card/src/index.ts +++ b/models/server-card/src/index.ts @@ -50,6 +50,42 @@ export function createModel (builder: Builder): void { } }) + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverCard.trigger.OnMasterTagCreate, + txMatch: { + _class: core.class.TxCreateDoc, + objectClass: { $in: [card.class.MasterTag, card.class.Tag] } + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverCard.trigger.OnCardRemove, + isAsync: true, + txMatch: { + _class: core.class.TxRemoveDoc, + objectClass: card.class.Card + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverCard.trigger.OnCardCreate, + isAsync: true, + txMatch: { + _class: core.class.TxCreateDoc, + objectClass: card.class.Card + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverCard.trigger.OnCardParentChange, + isAsync: true, + txMatch: { + _class: core.class.TxUpdateDoc, + objectClass: card.class.Card, + operations: { parent: { $exists: true } } + } + }) + builder.mixin(card.class.Card, core.class.Class, serverCore.mixin.SearchPresenter, { searchIcon: card.icon.Card, title: [['title']] diff --git a/plugins/card-assets/lang/cs.json b/plugins/card-assets/lang/cs.json index 602f927f28..2688954a86 100644 --- a/plugins/card-assets/lang/cs.json +++ b/plugins/card-assets/lang/cs.json @@ -19,6 +19,10 @@ "TagRelations": "Vztahy štítků", "DeleteTagConfirm": "Opravdu chcete smazat tento štítek? Všechny související vlastnosti budou smazány", "DeleteMasterTag": "Smazat typ", - "DeleteMasterTagConfirm": "Opravdu chcete smazat tento typ? Všechny objekty související s tímto typem budou smazány" + "DeleteMasterTagConfirm": "Opravdu chcete smazat tento typ? Všechny objekty související s tímto typem budou smazány", + "UnsetParent": "Odebrat rodiče", + "SetParent": "Nastavit rodiče", + "CreateChild": "Vytvořit dítě", + "Children": "Děti" } } \ No newline at end of file diff --git a/plugins/card-assets/lang/de.json b/plugins/card-assets/lang/de.json index 3cda15ada0..409cfede28 100644 --- a/plugins/card-assets/lang/de.json +++ b/plugins/card-assets/lang/de.json @@ -19,6 +19,10 @@ "DeleteTag": "Tag löschen", "DeleteTagConfirm": "Möchten Sie diesen Tag wirklich löschen? Alle zugehörigen Eigenschaften werden gelöscht", "DeleteMasterTag": "Master-Tag löschen", - "DeleteMasterTagConfirm": "Möchten Sie diesen Master-Tag wirklich löschen? Alle mit diesem Master-Tag verbundenen Objekte werden gelöscht" + "DeleteMasterTagConfirm": "Möchten Sie diesen Master-Tag wirklich löschen? Alle mit diesem Master-Tag verbundenen Objekte werden gelöscht", + "UnsetParent": "Elternteil entfernen", + "SetParent": "Elternteil setzen", + "CreateChild": "Kind erstellen", + "Children": "Kinder" } } \ No newline at end of file diff --git a/plugins/card-assets/lang/en.json b/plugins/card-assets/lang/en.json index 7498509bb3..ccd9d8008c 100644 --- a/plugins/card-assets/lang/en.json +++ b/plugins/card-assets/lang/en.json @@ -19,6 +19,10 @@ "TagRelations": "Tag Relations", "DeleteTagConfirm": "Are you sure you want to delete this tag? All related properties will be deleted", "DeleteMasterTag": "Delete Type", - "DeleteMasterTagConfirm": "Are you sure you want to delete this type? All objects related to this type will be deleted" + "DeleteMasterTagConfirm": "Are you sure you want to delete this type? All objects related to this type will be deleted", + "UnsetParent": "Unset parent", + "SetParent": "Set parent", + "CreateChild": "Create child", + "Children": "Children" } } \ No newline at end of file diff --git a/plugins/card-assets/lang/es.json b/plugins/card-assets/lang/es.json index 898c04499c..7af29de873 100644 --- a/plugins/card-assets/lang/es.json +++ b/plugins/card-assets/lang/es.json @@ -19,6 +19,10 @@ "TagRelations": "Relaciones de Etiquetas", "DeleteTagConfirm": "¿Estás seguro de que quieres eliminar esta etiqueta? Todas las propiedades relacionadas serán eliminadas", "DeleteMasterTag": "Eliminar Tipo", - "DeleteMasterTagConfirm": "¿Estás seguro de que quieres eliminar este tipo? Todos los objetos relacionados con este tipo serán eliminados" + "DeleteMasterTagConfirm": "¿Estás seguro de que quieres eliminar este tipo? Todos los objetos relacionados con este tipo serán eliminados", + "UnsetParent": "Desasignar padre", + "SetParent": "Asignar padre", + "CreateChild": "Crear hijo", + "Children": "Hijos" } } \ No newline at end of file diff --git a/plugins/card-assets/lang/fr.json b/plugins/card-assets/lang/fr.json index 0a5b262107..0cc4aeaf55 100644 --- a/plugins/card-assets/lang/fr.json +++ b/plugins/card-assets/lang/fr.json @@ -19,6 +19,10 @@ "TagRelations": "Relations d'étiquettes", "DeleteTagConfirm": "Êtes-vous sûr de vouloir supprimer cette étiquette ? Toutes les propriétés associées seront supprimées", "DeleteMasterTag": "Supprimer le type", - "DeleteMasterTagConfirm": "Êtes-vous sûr de vouloir supprimer ce type ? Tous les objets liés à ce type seront supprimés" + "DeleteMasterTagConfirm": "Êtes-vous sûr de vouloir supprimer ce type ? Tous les objets liés à ce type seront supprimés", + "UnsetParent": "Désassocier le parent", + "SetParent": "Associer un parent", + "CreateChild": "Créer un enfant", + "Children": "Enfants" } } \ No newline at end of file diff --git a/plugins/card-assets/lang/it.json b/plugins/card-assets/lang/it.json index 493561942f..a825314dbc 100644 --- a/plugins/card-assets/lang/it.json +++ b/plugins/card-assets/lang/it.json @@ -19,6 +19,10 @@ "TagRelations": "Relazioni Tag", "DeleteTagConfirm": "Sei sicuro di voler eliminare questo tag? Tutte le proprietà correlate verranno eliminate", "DeleteMasterTag": "Elimina Tipo", - "DeleteMasterTagConfirm": "Sei sicuro di voler eliminare questo tipo? Tutti gli oggetti correlati a questo tipo verranno eliminati" + "DeleteMasterTagConfirm": "Sei sicuro di voler eliminare questo tipo? Tutti gli oggetti correlati a questo tipo verranno eliminati", + "UnsetParent": "Rimuovi genitore", + "SetParent": "Imposta genitore", + "CreateChild": "Crea figlio", + "Children": "Figli" } } \ No newline at end of file diff --git a/plugins/card-assets/lang/pt.json b/plugins/card-assets/lang/pt.json index bf3723951e..fdf1632d2b 100644 --- a/plugins/card-assets/lang/pt.json +++ b/plugins/card-assets/lang/pt.json @@ -19,6 +19,10 @@ "TagRelations": "Relações de Tag", "DeleteTagConfirm": "Tem certeza que deseja excluir esta tag? Todas as propriedades relacionadas serão excluídas", "DeleteMasterTag": "Excluir Tipo", - "DeleteMasterTagConfirm": "Tem certeza que deseja excluir este tipo? Todos os objetos relacionados a este tipo serão excluídos" + "DeleteMasterTagConfirm": "Tem certeza que deseja excluir este tipo? Todos os objetos relacionados a este tipo serão excluídos", + "UnsetParent": "Remover pai", + "SetParent": "Definir pai", + "CreateChild": "Criar filho", + "Children": "Filhos" } } \ No newline at end of file diff --git a/plugins/card-assets/lang/ru.json b/plugins/card-assets/lang/ru.json index a00b2b6855..2be3a415c9 100644 --- a/plugins/card-assets/lang/ru.json +++ b/plugins/card-assets/lang/ru.json @@ -19,6 +19,10 @@ "TagRelations": "Связи тегов", "DeleteTagConfirm": "Вы действительно хотите удалить этот тег? Все связанные свойства будут удалены", "DeleteMasterTag": "Удалить тип", - "DeleteMasterTagConfirm": "Вы действительно хотите удалить этот тип? Все объекты, связанные с этим типом, будут удалены" + "DeleteMasterTagConfirm": "Вы действительно хотите удалить этот тип? Все объекты, связанные с этим типом, будут удалены", + "UnsetParent": "Отменить выбор родителя", + "SetParent": "Выбрать родителя", + "CreateChild": "Создать потомка", + "Children": "Потомки" } } \ No newline at end of file diff --git a/plugins/card-assets/lang/zh.json b/plugins/card-assets/lang/zh.json index 2f811c0005..8cc430e0af 100644 --- a/plugins/card-assets/lang/zh.json +++ b/plugins/card-assets/lang/zh.json @@ -19,6 +19,10 @@ "TagRelations": "标签关系", "DeleteTagConfirm": "您确定要删除此标签吗?所有相关属性都将被删除", "DeleteMasterTag": "删除类型", - "DeleteMasterTagConfirm": "您确定要删除此类型吗?所有与此类型相关的对象都将被删除" + "DeleteMasterTagConfirm": "您确定要删除此类型吗?所有与此类型相关的对象都将被删除", + "UnsetParent": "取消父级", + "SetParent": "设置父级", + "CreateChild": "创建子级", + "Children": "子级" } } \ No newline at end of file diff --git a/plugins/card-resources/src/components/CardPresenter.svelte b/plugins/card-resources/src/components/CardPresenter.svelte index f6a27309b3..c04d5ea19b 100644 --- a/plugins/card-resources/src/components/CardPresenter.svelte +++ b/plugins/card-resources/src/components/CardPresenter.svelte @@ -18,6 +18,7 @@ import { AnySvelteComponent, Icon, tooltip } from '@hcengineering/ui' import { ObjectPresenterType } from '@hcengineering/view' import { DocNavLink, ObjectMention } from '@hcengineering/view-resources' + import ParentNamesPresenter from './ParentNamesPresenter.svelte' import card from '../plugin' export let value: Card | undefined @@ -26,8 +27,9 @@ export let shouldShowAvatar: boolean = false export let noUnderline: boolean = disabled export let colorInherit: boolean = false - export let noSelect: boolean = false + export let noSelect: boolean = true export let inline = false + export let showParent: boolean = false export let kind: 'list' | undefined = undefined export let type: ObjectPresenterType = 'link' export let icon: Asset | AnySvelteComponent | undefined = undefined @@ -48,7 +50,7 @@ component={card.component.EditCard} shrink={0} > - + {#if shouldShowAvatar}
@@ -56,6 +58,9 @@ {/if} {value.title} + {#if showParent} + + {/if} @@ -69,19 +74,11 @@ {/if} diff --git a/plugins/card-resources/src/components/SetParentActionPopup.svelte b/plugins/card-resources/src/components/SetParentActionPopup.svelte new file mode 100644 index 0000000000..c81981f5eb --- /dev/null +++ b/plugins/card-resources/src/components/SetParentActionPopup.svelte @@ -0,0 +1,74 @@ + + + + + +
+ {item.title} +
+
+
diff --git a/plugins/card-resources/src/components/TagAttributes.svelte b/plugins/card-resources/src/components/TagAttributes.svelte index 8f994660ab..eaae446cfd 100644 --- a/plugins/card-resources/src/components/TagAttributes.svelte +++ b/plugins/card-resources/src/components/TagAttributes.svelte @@ -76,7 +76,7 @@ icon={setting.icon.Setting} kind={'link'} size={'medium'} - showTooltip={{ label: setting.string.ClassSetting }} + showTooltip={{ label: setting.string.Setting }} on:click={(ev) => { ev.stopPropagation() const loc = getCurrentResolvedLocation() diff --git a/plugins/card-resources/src/index.ts b/plugins/card-resources/src/index.ts index 81aaf98dc9..6234733a10 100644 --- a/plugins/card-resources/src/index.ts +++ b/plugins/card-resources/src/index.ts @@ -34,6 +34,7 @@ import ProperitiesSection from './components/settings/ProperitiesSection.svelte' import TagsSection from './components/settings/TagsSection.svelte' import RelationsSection from './components/settings/RelationsSection.svelte' import ChildsSection from './components/settings/ChildsSection.svelte' +import SetParentActionPopup from './components/SetParentActionPopup.svelte' export default async (): Promise => ({ component: { @@ -49,7 +50,8 @@ export default async (): Promise => ({ ProperitiesSection, TagsSection, RelationsSection, - ChildsSection + ChildsSection, + SetParentActionPopup }, completion: { CardQuery: queryCard diff --git a/plugins/card-resources/src/plugin.ts b/plugins/card-resources/src/plugin.ts index 8de66b1f4a..375e6fce0a 100644 --- a/plugins/card-resources/src/plugin.ts +++ b/plugins/card-resources/src/plugin.ts @@ -15,7 +15,8 @@ import card, { cardId } from '@hcengineering/card' import { type Ref } from '@hcengineering/core' -import { type IntlString, mergeIds } from '@hcengineering/platform' +import { type IntlString, mergeIds, type Resource } from '@hcengineering/platform' +import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/presentation' import { type AnyComponent } from '@hcengineering/ui/src/types' import { type Viewlet } from '@hcengineering/view' @@ -33,10 +34,16 @@ export default mergeIds(cardId, card, { ProperitiesSection: '' as AnyComponent, TagsSection: '' as AnyComponent, ChildsSection: '' as AnyComponent, - RelationsSection: '' as AnyComponent + RelationsSection: '' as AnyComponent, + SetParentActionPopup: '' as AnyComponent + }, + completion: { + CardQuery: '' as Resource, + CardCategory: '' as Ref }, viewlet: { - CardTable: '' as Ref + CardTable: '' as Ref, + CardList: '' as Ref }, string: { CreateMasterTag: '' as IntlString, @@ -53,6 +60,10 @@ export default mergeIds(cardId, card, { DeleteTagConfirm: '' as IntlString, DeleteMasterTag: '' as IntlString, DeleteMasterTagConfirm: '' as IntlString, - TagRelations: '' as IntlString + TagRelations: '' as IntlString, + UnsetParent: '' as IntlString, + SetParent: '' as IntlString, + CreateChild: '' as IntlString, + Children: '' as IntlString } }) diff --git a/plugins/card/src/index.ts b/plugins/card/src/index.ts index 238ff86b85..6b0cba9b6b 100644 --- a/plugins/card/src/index.ts +++ b/plugins/card/src/index.ts @@ -26,10 +26,18 @@ export interface Card extends Doc { _class: Ref title: string content: MarkupBlobRef + children?: number + parentInfo: ParentInfo[] parent?: Ref | null rank: Rank } +export interface ParentInfo { + _id: Ref + _class: Ref + title: string +} + export interface MasterTagEditorSection extends Doc { id: string label: IntlString diff --git a/plugins/client-resources/src/connection.ts b/plugins/client-resources/src/connection.ts index 8b95b07cd9..f68fbc5b80 100644 --- a/plugins/client-resources/src/connection.ts +++ b/plugins/client-resources/src/connection.ts @@ -784,9 +784,6 @@ class Connection implements ClientConnection { // We need to revert deleted query simple values. // We need to get rid of simple query parameters matched in documents for (const doc of result) { - if (doc._class == null) { - doc._class = _class - } for (const [k, v] of Object.entries(query)) { if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') { if (doc[k] == null) { @@ -794,6 +791,9 @@ class Connection implements ClientConnection { } } } + if (doc._class == null) { + doc._class = _class + } } return result diff --git a/plugins/view-resources/src/components/ClassRefPresenter.svelte b/plugins/view-resources/src/components/ClassRefPresenter.svelte index 2f1c186edd..f40fb674e4 100644 --- a/plugins/view-resources/src/components/ClassRefPresenter.svelte +++ b/plugins/view-resources/src/components/ClassRefPresenter.svelte @@ -24,5 +24,7 @@ {#if _class} -