Relation fixes (#8167)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2025-03-07 14:14:17 +05:00 committed by GitHub
parent 655c46ef9a
commit 7bfd8403a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 98 additions and 24 deletions

View File

@ -693,6 +693,25 @@ export function createModel (builder: Builder): void {
view.action.Delete
)
createAction(
builder,
{
action: view.actionImpl.Delete,
actionProps: {
confirmation: view.string.RemoveRelationConfirmation
},
label: view.string.RemoveRelation,
icon: view.icon.Delete,
keyBinding: ['Meta + Backspace'],
category: view.category.General,
override: [view.action.Delete],
input: 'any',
target: core.class.Relation,
context: { mode: ['context', 'browser'], group: 'remove' }
},
view.action.RemoveRelation
)
createAction(
builder,
{

View File

@ -40,7 +40,7 @@
let allClasses: MasterTag[] = []
function fillClasses (tags: MasterTag[]): void {
classes = tags.filter((it) => it.extends === card.class.Card)
classes = tags.filter((it) => it.extends === card.class.Card).sort((a, b) => a.label.localeCompare(b.label))
}
const query = createQuery()

View File

@ -41,7 +41,7 @@
result.push(cls)
}
}
return result
return result.sort((a, b) => a.label.localeCompare(b.label))
}
function fillDescendants (classes: MasterTag[]): void {

View File

@ -47,7 +47,7 @@
_class: card.class.MasterTag
},
(result) => {
tags = result
tags = result.sort((a, b) => a.label.localeCompare(b.label))
}
)

View File

@ -50,6 +50,7 @@
function addRelation (): void {
showPopup(setting.component.CreateRelation, {
aClass: masterTag._id,
exclude: [],
_classes: [card.class.Card, contact.class.Contact]
})

View File

@ -22,11 +22,14 @@
import { createEventDispatcher } from 'svelte'
import { personRefByPersonIdStore } from '../utils'
import { PersonRefPresenter } from '..'
import { IntlString } from '@hcengineering/platform'
export let object: Doc | Doc[]
export let deleteAction: () => void | Promise<void>
export let skipCheck: boolean = false
export let canDeleteExtra: boolean = true
export let confirmation: IntlString | undefined = undefined
const me = getCurrentEmployee()
const objectArray = Array.isArray(object) ? object : [object]
const dispatch = createEventDispatcher()
@ -57,7 +60,7 @@
<div class="flex-grow flex-col">
{#if canDelete}
<div class="mb-2">
<Label label={view.string.DeleteObjectConfirm} params={{ count: objectArray.length }} />
<Label label={confirmation ?? view.string.DeleteObjectConfirm} params={{ count: objectArray.length }} />
<div class="mt-2">
{#if objectArray.length === 1}
<ObjectPresenter _class={objectArray[0]._class} objectId={objectArray[0]._id} value={objectArray[0]} />

View File

@ -23,6 +23,7 @@
import { createEventDispatcher } from 'svelte'
import setting from '../plugin'
export let aClass: Ref<Class<Doc>> | undefined = undefined
export let _classes: Ref<Class<Doc>>[] = [core.class.Doc]
export let exclude: Ref<Class<Doc>>[] = [cardPlugin.class.Card]
@ -79,7 +80,10 @@
for (const [key, value] of base) {
try {
const clazz = hierarchy.getClass(key)
result.push([{ id: key, label: clazz.label }, value.map((it) => ({ id: it._id, label: it.label }))])
result.push([
{ id: key, label: clazz.label, icon: clazz.icon },
value.map((it) => ({ id: it._id, label: it.label, icon: it.icon }))
])
} catch {}
}
return result
@ -87,7 +91,7 @@
const classes = filterClasses(descendants, viewlets, exclude)
let classARef: Ref<Class<Doc>> | undefined = undefined
let classARef: Ref<Class<Doc>> | undefined = aClass
let classBRef: Ref<Class<Doc>> | undefined = undefined
let nameA: string = ''
let nameB: string = ''
@ -122,6 +126,20 @@
]
let mode: '1:1' | '1:N' | 'N:N' = 'N:N' as '1:1' | '1:N' | 'N:N'
$: classA = getAClass(aClass)
function getAClass (aClass: Ref<Class<Doc>> | undefined): DropdownIntlItem | undefined {
if (aClass === undefined) {
return undefined
}
const clazz = hierarchy.getClass(aClass)
return {
id: clazz._id,
label: clazz.label,
icon: clazz.icon
}
}
</script>
<Card
@ -138,12 +156,16 @@
<EditBox bind:value={nameA} placeholder={core.string.Name} kind={'default'} />
</div>
<div>
<NestedDropdown
items={classes}
on:selected={(e) => {
classARef = e.detail
}}
/>
{#if classA !== undefined}
<DropdownLabelsIntl items={[classA]} selected={classA.id} disabled />
{:else}
<NestedDropdown
items={classes}
on:selected={(e) => {
classARef = e.detail
}}
/>
{/if}
</div>
</div>

View File

@ -127,6 +127,8 @@
"Join": "Připojit se",
"Copied": "Zkopírováno",
"Title": "Název",
"HideArchived": "Skrýt archivované"
"HideArchived": "Skrýt archivované",
"RemoveRelation": "Odstranit vztah",
"RemoveRelationConfirmation": "Chcete odstranit vztah?"
}
}

View File

@ -127,6 +127,8 @@
"Join": "Beitreten",
"Copied": "Kopiert",
"Title": "Titel",
"HideArchived": "Archivierte ausblenden"
"HideArchived": "Archivierte ausblenden",
"RemoveRelation": "Beziehung entfernen",
"RemoveRelationConfirmation": "Möchten Sie die Beziehung entfernen?"
}
}

View File

@ -127,6 +127,8 @@
"Join": "Join",
"Copied": "Copied",
"Title": "Title",
"HideArchived": "Hide archived"
"HideArchived": "Hide archived",
"RemoveRelation": "Remove relation",
"RemoveRelationConfirmation": "Do you want to remove relation?"
}
}

View File

@ -122,6 +122,8 @@
"Join": "Unirse",
"Copied": "Copiado",
"Title": "Título",
"HideArchived": "Ocultar archivadas"
"HideArchived": "Ocultar archivadas",
"RemoveRelation": "Eliminar relación",
"RemoveRelationConfirmation": "¿Quieres eliminar la relación?"
}
}

View File

@ -122,6 +122,8 @@
"Join": "Rejoindre",
"Copied": "Copié",
"Title": "Titre",
"HideArchived": "Masquer les archives"
"HideArchived": "Masquer les archives",
"RemoveRelation": "Supprimer la relation",
"RemoveRelationConfirmation": "Voulez-vous supprimer la relation ?"
}
}

View File

@ -122,6 +122,8 @@
"Join": "Unisciti",
"Copied": "Copiato",
"Title": "Titolo",
"HideArchived": "Nascondi archiviato"
"HideArchived": "Nascondi archiviato",
"RemoveRelation": "Rimuovere relazione",
"RemoveRelationConfirmation": "Vuoi rimuovere la relazione?"
}
}

View File

@ -122,6 +122,8 @@
"Join": "Ingressar",
"Copied": "Copiado",
"Title": "Título",
"HideArchived": "Ocultar arquivado"
"HideArchived": "Ocultar arquivado",
"RemoveRelation": "Remover relação",
"RemoveRelationConfirmation": "Deseja remover a relação?"
}
}

View File

@ -124,6 +124,8 @@
"Join": "Присоединиться",
"Copied": "Скопировано",
"Title": "Заголовок",
"HideArchived": "Скрыть архивные"
"HideArchived": "Скрыть архивные",
"RemoveRelation": "Удалить связь",
"RemoveRelationConfirmation": "Вы хотите удалить связь?"
}
}

View File

@ -127,6 +127,8 @@
"Join": "加入",
"Copied": "已复制",
"Title": "标题",
"HideArchived": "隱藏已存檔"
"HideArchived": "隱藏已存檔",
"RemoveRelation": "移除关系",
"RemoveRelationConfirmation": "您要移除关系吗?"
}
}

View File

@ -92,6 +92,7 @@ function Delete (
props?: {
skipCheck?: boolean
afterDelete?: () => Promise<void>
confirmation?: IntlString
}
): void {
const skipCheck = props?.skipCheck ?? false
@ -100,6 +101,7 @@ function Delete (
{
object,
skipCheck,
confirmation: props?.confirmation,
deleteAction: async () => {
try {
const objs = Array.isArray(object) ? object : [object]

View File

@ -93,7 +93,7 @@
: { docA: doc._id, docB: object._id, association: association._id }
const relation = await client.findOne(core.class.Relation, q)
if (relation !== undefined) {
showMenu(ev, { object: relation, includedActions: [view.action.Delete] })
showMenu(ev, { object: relation, includedActions: [view.action.RemoveRelation] })
}
}

View File

@ -150,7 +150,8 @@ const view = plugin(viewId, {
// Edit document
Open: '' as Ref<Action>,
OpenInNewTab: '' as Ref<Action>
OpenInNewTab: '' as Ref<Action>,
RemoveRelation: '' as Ref<Action>
},
viewlet: {
Table: '' as Ref<ViewletDescriptor>,
@ -219,7 +220,9 @@ const view = plugin(viewId, {
And: '' as IntlString,
Title: '' as IntlString,
DeleteObject: '' as IntlString,
DeleteObjectConfirm: '' as IntlString
DeleteObjectConfirm: '' as IntlString,
RemoveRelationConfirmation: '' as IntlString,
RemoveRelation: '' as IntlString
},
icon: {
Table: '' as Asset,

View File

@ -101,6 +101,11 @@ const relationSchema: Schema = {
type: 'text',
notNull: true,
index: true
},
association: {
type: 'text',
notNull: true,
index: true
}
}

View File

@ -1449,6 +1449,7 @@ abstract class PostgresAdapterBase implements DbAdapter {
ON relation."${keyB}" = assoc."_id"
AND relation."workspaceId" = ${wsId}
WHERE relation."${keyA}" = ${translateDomain(baseDomain)}."_id"
AND relation.association = '${_id}'
AND assoc."workspaceId" = ${wsId}) AS assoc_${tagetDomain}_${association[0]}`
)
}