mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-01 04:38:50 +02:00
Cards fixes (#8278)
This commit is contained in:
+51
-43
@@ -12,27 +12,19 @@
|
||||
// limitations under the License.
|
||||
|
||||
import activity from '@hcengineering/activity'
|
||||
import {
|
||||
CardEvents,
|
||||
cardId,
|
||||
DOMAIN_CARD,
|
||||
type Card,
|
||||
type MasterTag,
|
||||
type ParentInfo,
|
||||
type Tag
|
||||
} from '@hcengineering/card'
|
||||
import { cardId, DOMAIN_CARD, type Card, type MasterTag, type ParentInfo, type Tag } from '@hcengineering/card'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import core, {
|
||||
AccountRole,
|
||||
type Blobs,
|
||||
ClassifierKind,
|
||||
DOMAIN_MODEL,
|
||||
IndexKind,
|
||||
SortingOrder,
|
||||
type Blobs,
|
||||
type CollectionSize,
|
||||
type MarkupBlobRef,
|
||||
type Rank,
|
||||
type Ref,
|
||||
ClassifierKind,
|
||||
type MarkupBlobRef
|
||||
type Ref
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
Collection,
|
||||
@@ -54,6 +46,7 @@ import workbench from '@hcengineering/model-workbench'
|
||||
import { getEmbeddedLabel, type IntlString } from '@hcengineering/platform'
|
||||
import time, { type ToDo } from '@hcengineering/time'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import { type BuildModelKey } from '@hcengineering/view'
|
||||
import card from './plugin'
|
||||
|
||||
export { cardId } from '@hcengineering/card'
|
||||
@@ -142,6 +135,23 @@ export function createModel (builder: Builder): void {
|
||||
]
|
||||
})
|
||||
|
||||
const listConfig: (BuildModelKey | string)[] = [
|
||||
{ key: '', props: { showParent: true }, displayProps: { fixed: 'left', key: 'card' } },
|
||||
{ key: '_class', displayProps: { fixed: 'left', key: 'type' } },
|
||||
{ key: '', displayProps: { grow: true } },
|
||||
{
|
||||
key: '',
|
||||
presenter: view.component.RolePresenter,
|
||||
label: card.string.Tags,
|
||||
props: { fullSize: true },
|
||||
displayProps: { key: 'tags', fixed: 'right' }
|
||||
},
|
||||
{
|
||||
key: 'modifiedOn',
|
||||
displayProps: { fixed: 'right', dividerBefore: true }
|
||||
}
|
||||
]
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: card.types.File,
|
||||
descriptor: view.viewlet.List,
|
||||
@@ -156,22 +166,7 @@ export function createModel (builder: Builder): void {
|
||||
configOptions: {
|
||||
hiddenKeys: ['content', 'title']
|
||||
},
|
||||
config: [
|
||||
{ key: '', props: { showParent: true } },
|
||||
{ key: '_class', displayProps: { fixed: 'left' } },
|
||||
{
|
||||
key: '',
|
||||
presenter: view.component.RolePresenter,
|
||||
label: card.string.Tags,
|
||||
props: { fullSize: true },
|
||||
displayProps: { fixed: 'left' }
|
||||
},
|
||||
{ key: '', displayProps: { grow: true } },
|
||||
{
|
||||
key: 'modifiedOn',
|
||||
displayProps: { fixed: 'right', dividerBefore: true }
|
||||
}
|
||||
]
|
||||
config: listConfig
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
@@ -276,20 +271,35 @@ export function createModel (builder: Builder): void {
|
||||
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 }
|
||||
}
|
||||
]
|
||||
config: listConfig
|
||||
},
|
||||
card.viewlet.CardList
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: card.class.Card,
|
||||
descriptor: view.viewlet.List,
|
||||
variant: 'child',
|
||||
viewOptions: {
|
||||
groupBy: ['_class', 'createdBy', 'modifiedBy'],
|
||||
orderBy: [
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
['rank', SortingOrder.Ascending]
|
||||
],
|
||||
other: []
|
||||
},
|
||||
configOptions: {
|
||||
strict: true,
|
||||
hiddenKeys: ['content', 'title']
|
||||
},
|
||||
config: listConfig
|
||||
},
|
||||
card.viewlet.CardChildList
|
||||
)
|
||||
|
||||
builder.mixin(card.class.Card, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: card.component.CardPresenter
|
||||
})
|
||||
@@ -389,8 +399,7 @@ export function createModel (builder: Builder): void {
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'remove'
|
||||
},
|
||||
analyticsEvent: CardEvents.MasterTagRemoved
|
||||
}
|
||||
},
|
||||
card.action.DeleteMasterTag
|
||||
)
|
||||
@@ -405,8 +414,7 @@ export function createModel (builder: Builder): void {
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'remove'
|
||||
},
|
||||
analyticsEvent: CardEvents.MasterTagRemoved
|
||||
}
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import { DOMAIN_CARD } from '@hcengineering/card'
|
||||
import { chunterId } from '@hcengineering/chunter'
|
||||
import core, { type Client, type Data, type Doc, TxOperations } from '@hcengineering/core'
|
||||
import core, { TxOperations, type Client, type Data, type Doc } from '@hcengineering/core'
|
||||
import {
|
||||
tryMigrate,
|
||||
tryUpgrade,
|
||||
@@ -39,7 +39,7 @@ export const cardOperation: MigrateOperation = {
|
||||
async upgrade (state: Map<string, Set<string>>, client: () => Promise<MigrationUpgradeClient>, mode): Promise<void> {
|
||||
await tryUpgrade(mode, state, client, chunterId, [
|
||||
{
|
||||
state: 'migrateViewlets',
|
||||
state: 'migrateViewlets-v2',
|
||||
func: migrateViewlets
|
||||
}
|
||||
])
|
||||
@@ -71,23 +71,39 @@ function extractObjectProps<T extends Doc> (doc: T): Data<T> {
|
||||
|
||||
async function migrateViewlets (client: Client): Promise<void> {
|
||||
const txOp = new TxOperations(client, core.account.System)
|
||||
const viewlets = await client.findAll(view.class.Viewlet, { attachTo: card.class.Card })
|
||||
const viewlets = await client.findAll(view.class.Viewlet, { attachTo: card.class.Card, variant: { $exists: false } })
|
||||
const masterTags = await client.findAll(card.class.MasterTag, {})
|
||||
const currentViewlets = await client.findAll(view.class.Viewlet, { attachTo: { $in: masterTags.map((p) => p._id) } })
|
||||
for (const masterTag of masterTags) {
|
||||
for (const viewlet of viewlets) {
|
||||
const base = extractObjectProps(viewlet)
|
||||
const resConfig = base.config
|
||||
let index = -1
|
||||
if (viewlet.descriptor === view.viewlet.List) {
|
||||
index = viewlet.config.findIndex((p) => typeof p !== 'string' && p.displayProps?.grow === true)
|
||||
}
|
||||
const attributes = client.getHierarchy().getOwnAttributes(masterTag._id)
|
||||
for (const attr of attributes) {
|
||||
// let push it after grow for the list
|
||||
if (index !== -1) {
|
||||
resConfig.splice(index + 1, 0, attr[1].name)
|
||||
} else {
|
||||
resConfig.push(attr[1].name)
|
||||
}
|
||||
}
|
||||
const current = currentViewlets.find(
|
||||
(p) => p.attachTo === masterTag._id && p.variant === viewlet.variant && p.descriptor === viewlet.descriptor
|
||||
)
|
||||
if (current === undefined) {
|
||||
await txOp.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
...base,
|
||||
config: resConfig,
|
||||
attachTo: masterTag._id
|
||||
})
|
||||
} else {
|
||||
await txOp.diffUpdate(current, {
|
||||
...base,
|
||||
config: resConfig,
|
||||
attachTo: masterTag._id
|
||||
})
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
const viewletId = card.viewlet.CardList
|
||||
const viewletId = card.viewlet.CardChildList
|
||||
|
||||
let list: List
|
||||
const listProvider = new ListSelectionProvider(
|
||||
|
||||
@@ -13,13 +13,14 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { MasterTag, Tag } from '@hcengineering/card'
|
||||
import { CardEvents, MasterTag, Tag } from '@hcengineering/card'
|
||||
import core, { Class, ClassifierKind, Data, Ref } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { Card, getClient } from '@hcengineering/presentation'
|
||||
import { EditBox, Icon, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import card from '../plugin'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
|
||||
export let parent: MasterTag | Tag | undefined = undefined
|
||||
export let _class: Ref<Class<MasterTag>> | Ref<Class<Tag>>
|
||||
@@ -39,6 +40,7 @@
|
||||
}
|
||||
|
||||
await client.createDoc(_class, core.space.Model, data)
|
||||
Analytics.handleEvent(isMasterTag ? CardEvents.TypeCreated : CardEvents.TagCreated)
|
||||
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
|
||||
{#if classes.length > 0}
|
||||
<div class="antiNav-divider line" />
|
||||
|
||||
<Scroller shrink>
|
||||
<TagHierarchy bind:_class deselect={menuSelection} {classes} {allClasses} on:select />
|
||||
</Scroller>
|
||||
|
||||
@@ -72,6 +72,6 @@
|
||||
/>
|
||||
</NavLink>
|
||||
{#if (descendants.get(clazz._id)?.length ?? 0) > 0}
|
||||
<svelte:self classes={descendants.get(clazz._id) ?? []} {_class} level={level + 1} on:select />
|
||||
<svelte:self classes={descendants.get(clazz._id) ?? []} {_class} {allClasses} level={level + 1} on:select />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import {
|
||||
Icon,
|
||||
IconOpenedArrow,
|
||||
IconWithEmoji,
|
||||
Label,
|
||||
Location,
|
||||
getCurrentResolvedLocation,
|
||||
@@ -28,6 +29,7 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import card from '../../plugin'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
export let categoryName: string
|
||||
|
||||
@@ -64,9 +66,14 @@
|
||||
}}
|
||||
>
|
||||
<div class="hulyTaskNavLink-avatar">
|
||||
{#if tag?.icon}
|
||||
{#if tag.icon}
|
||||
<div class="hulyTaskNavLink-icon">
|
||||
<Icon icon={tag?.icon} size="small" fill="currentColor" />
|
||||
<Icon
|
||||
icon={tag.icon === view.ids.IconWithEmoji ? IconWithEmoji : tag.icon ?? card.icon.MasterTag}
|
||||
iconProps={tag.icon === view.ids.IconWithEmoji ? { icon: tag.color } : {}}
|
||||
size="small"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -3,12 +3,18 @@
|
||||
import { RelationSetting } from '@hcengineering/setting-resources'
|
||||
import contact from '@hcengineering/contact'
|
||||
import card from '../../plugin'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import { CardEvents } from '@hcengineering/card'
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const _classes = [...hierarchy.getDescendants(card.class.Card), contact.class.Contact].filter(
|
||||
(c) => c !== card.class.Card
|
||||
)
|
||||
|
||||
function createHandler (): void {
|
||||
Analytics.handleEvent(CardEvents.RelationCreated)
|
||||
}
|
||||
</script>
|
||||
|
||||
<RelationSetting {_classes} exclude={[]} />
|
||||
<RelationSetting {_classes} exclude={[]} on:create={createHandler} />
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { MasterTag } from '@hcengineering/card'
|
||||
import { CardEvents, MasterTag } from '@hcengineering/card'
|
||||
import core, { Association, Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
@@ -22,6 +22,7 @@
|
||||
import { ButtonIcon, Icon, IconAdd, Label, showPopup } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import CreateRelation from './CreateRelation.svelte'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
|
||||
export let masterTag: MasterTag
|
||||
|
||||
@@ -48,9 +49,18 @@
|
||||
}
|
||||
|
||||
function addRelation (): void {
|
||||
showPopup(CreateRelation, {
|
||||
aClass: masterTag._id
|
||||
})
|
||||
showPopup(
|
||||
CreateRelation,
|
||||
{
|
||||
aClass: masterTag._id
|
||||
},
|
||||
undefined,
|
||||
(res) => {
|
||||
if (res !== undefined) {
|
||||
Analytics.handleEvent(CardEvents.RelationCreated)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const handleSelect = (association: Association): void => {
|
||||
|
||||
@@ -44,7 +44,8 @@ export default mergeIds(cardId, card, {
|
||||
},
|
||||
viewlet: {
|
||||
CardTable: '' as Ref<Viewlet>,
|
||||
CardList: '' as Ref<Viewlet>
|
||||
CardList: '' as Ref<Viewlet>,
|
||||
CardChildList: '' as Ref<Viewlet>
|
||||
},
|
||||
string: {
|
||||
CreateMasterTag: '' as IntlString,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export enum CardEvents {
|
||||
NewCardButtonClicked = 'card.card.NewButtonClicked',
|
||||
MasterTagRemoved = 'card.card.MasterTagRemoved',
|
||||
TypeCreated = 'card.card.TypeCreated',
|
||||
TagCreated = 'card.card.TagCreated',
|
||||
RelationCreated = 'card.card.RelationCreated',
|
||||
CardCreated = 'card.card.Created',
|
||||
CardOpened = 'card.card.Opened'
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@
|
||||
nameA,
|
||||
nameB
|
||||
})
|
||||
dispatch('create')
|
||||
dispatch('close')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,14 +102,14 @@
|
||||
if (classBRef === undefined || classARef === undefined) {
|
||||
return
|
||||
}
|
||||
await client.createDoc(core.class.Association, core.space.Model, {
|
||||
const _id = await client.createDoc(core.class.Association, core.space.Model, {
|
||||
classA: classARef,
|
||||
classB: classBRef,
|
||||
type: mode,
|
||||
nameA,
|
||||
nameB
|
||||
})
|
||||
dispatch('close')
|
||||
dispatch('close', _id)
|
||||
}
|
||||
|
||||
const items: DropdownIntlItem[] = [
|
||||
|
||||
@@ -153,7 +153,8 @@
|
||||
{exclude}
|
||||
{_classes}
|
||||
association={selected}
|
||||
on:close={() => {
|
||||
on:create
|
||||
on:close={(res) => {
|
||||
selected = undefined
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { resolvedLocationStore, Switcher } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import core, { DocumentQuery, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
|
||||
export let viewlet: WithLookup<Viewlet> | undefined
|
||||
@@ -49,7 +50,7 @@
|
||||
) {
|
||||
if (viewlets == null || viewlets.length === 0) return
|
||||
const newViewlet = viewlets.find((viewlet) => viewlet?._id === activeViewlet[key]) ?? viewlets[0]
|
||||
if (viewlet?._id !== newViewlet?._id) {
|
||||
if (viewlet?._id !== newViewlet?._id || !deepEqual(viewlet.config, newViewlet.config)) {
|
||||
viewlet = newViewlet
|
||||
setActiveViewletId(newViewlet._id)
|
||||
dispatch('viewlet', viewlet)
|
||||
|
||||
@@ -35,9 +35,19 @@ async function OnAttribute (ctx: TxCreateDoc<AnyAttribute>[], control: TriggerCo
|
||||
const desc = control.hierarchy.getDescendants(attr.attributeOf)
|
||||
const res: Tx[] = []
|
||||
for (const des of desc) {
|
||||
const viewlets = control.modelDb.findAllSync(view.class.Viewlet, { attachTo: des })
|
||||
const viewlets = control.modelDb.findAllSync(view.class.Viewlet, { attachTo: des, variant: { $exists: false } })
|
||||
for (const viewlet of viewlets) {
|
||||
viewlet.config.push(attr.name)
|
||||
// let push it after grow for the list
|
||||
if (viewlet.descriptor === view.viewlet.List) {
|
||||
const index = viewlet.config.findIndex((p) => typeof p !== 'string' && p.displayProps?.grow === true)
|
||||
if (index !== -1) {
|
||||
viewlet.config.splice(index + 1, 0, attr.name)
|
||||
} else {
|
||||
viewlet.config.push(attr.name)
|
||||
}
|
||||
} else {
|
||||
viewlet.config.push(attr.name)
|
||||
}
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(viewlet._class, viewlet.space, viewlet._id, {
|
||||
config: viewlet.config
|
||||
@@ -45,7 +55,16 @@ async function OnAttribute (ctx: TxCreateDoc<AnyAttribute>[], control: TriggerCo
|
||||
)
|
||||
const prefs = await control.findAll(control.ctx, view.class.ViewletPreference, { attachedTo: viewlet._id })
|
||||
for (const pref of prefs) {
|
||||
pref.config.push(attr.name)
|
||||
if (viewlet.descriptor === view.viewlet.List) {
|
||||
const index = viewlet.config.findIndex((p) => typeof p !== 'string' && p.displayProps?.grow === true)
|
||||
if (index !== -1) {
|
||||
viewlet.config.splice(index + 1, 0, attr.name)
|
||||
} else {
|
||||
viewlet.config.push(attr.name)
|
||||
}
|
||||
} else {
|
||||
viewlet.config.push(attr.name)
|
||||
}
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(pref._class, pref.space, pref._id, {
|
||||
config: pref.config
|
||||
|
||||
Reference in New Issue
Block a user