Fix old card UI (#10386)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2026-01-10 22:12:51 +05:00
committed by GitHub
parent 0e89060f53
commit 4751ae8bcd
17 changed files with 186 additions and 341 deletions
+7 -42
View File
@@ -6815,6 +6815,9 @@ importers:
'@hcengineering/chunter':
specifier: workspace:^0.7.0
version: link:../../plugins/chunter
'@hcengineering/communication':
specifier: workspace:^0.7.0
version: link:../../plugins/communication
'@hcengineering/contact':
specifier: workspace:^0.7.0
version: link:../../plugins/contact
@@ -17320,6 +17323,9 @@ importers:
'@hcengineering/account-client':
specifier: workspace:^0.7.21
version: link:../../foundations/core/packages/account-client
'@hcengineering/activity':
specifier: workspace:^0.7.0
version: link:../activity
'@hcengineering/analytics':
specifier: workspace:^0.7.17
version: link:../../foundations/core/packages/analytics
@@ -61252,7 +61258,7 @@ snapshots:
node-loader@2.0.0(webpack@5.102.1):
dependencies:
loader-utils: 2.0.4
webpack: 5.102.1
webpack: 5.102.1(@swc/core@1.15.1)(esbuild@0.25.12)(webpack-cli@5.1.4)
node-localstorage@2.2.1:
dependencies:
@@ -63283,15 +63289,6 @@ snapshots:
optionalDependencies:
'@swc/core': 1.15.1
terser-webpack-plugin@5.3.14(webpack@5.102.1):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
schema-utils: 4.3.3
serialize-javascript: 6.0.2
terser: 5.44.1
webpack: 5.102.1
terser@5.44.1:
dependencies:
'@jridgewell/source-map': 0.3.11
@@ -63958,38 +63955,6 @@ snapshots:
webpack-sources@3.3.3: {}
webpack@5.102.1:
dependencies:
'@types/eslint-scope': 3.7.7
'@types/estree': 1.0.8
'@types/json-schema': 7.0.15
'@webassemblyjs/ast': 1.14.1
'@webassemblyjs/wasm-edit': 1.14.1
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.15.0
acorn-import-phases: 1.0.4(acorn@8.15.0)
browserslist: 4.28.1
chrome-trace-event: 1.0.4
enhanced-resolve: 5.18.3
es-module-lexer: 1.7.0
eslint-scope: 5.1.1
events: 3.3.0
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
json-parse-even-better-errors: 2.3.1
loader-runner: 4.3.1
mime-types: 2.1.35
neo-async: 2.6.2
schema-utils: 4.3.3
tapable: 2.3.0
terser-webpack-plugin: 5.3.14(webpack@5.102.1)
watchpack: 2.4.4
webpack-sources: 3.3.3
transitivePeerDependencies:
- '@swc/core'
- esbuild
- uglify-js
webpack@5.102.1(@swc/core@1.15.1):
dependencies:
'@types/eslint-scope': 3.7.7
-1
View File
@@ -488,7 +488,6 @@ export async function configurePlatform() {
setMetadata(textEditor.metadata.Collaborator, config.COLLABORATOR)
setMetadata(communication.metadata.Enabled, config.COMMUNICATION_API_ENABLED === 'true')
if (config.MODEL_VERSION != null) {
console.log('Minimal Model version requirement', config.MODEL_VERSION)
setMetadata(presentation.metadata.ModelVersion, config.MODEL_VERSION)
+1
View File
@@ -36,6 +36,7 @@
"dependencies": {
"@hcengineering/activity": "workspace:^0.7.0",
"@hcengineering/contact": "workspace:^0.7.0",
"@hcengineering/communication": "workspace:^0.7.0",
"@hcengineering/core": "workspace:^0.7.24",
"@hcengineering/model": "workspace:^0.7.17",
"@hcengineering/chunter": "workspace:^0.7.0",
+27
View File
@@ -12,6 +12,7 @@
// limitations under the License.
import activity from '@hcengineering/activity'
import communication from '@hcengineering/communication'
import {
type CanCreateCardResource,
type Card,
@@ -923,6 +924,32 @@ function defineTabs (builder: Builder): void {
card.section.Relations
)
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: activity.string.Messages,
component: card.sectionComponent.OldMessagesSection,
order: 1000,
navigation: [],
checkVisibility: card.function.CheckOldMessagesSectionVisibility
},
card.section.OldMessages
)
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: activity.string.Messages,
component: card.sectionComponent.CommunicationMessagesSection,
order: 1000,
navigation: [],
checkVisibility: card.function.CheckCommunicationMessagesSectionVisibility
},
communication.ids.CardMessagesSection
)
builder.createDoc<Viewlet>(view.class.Viewlet, core.space.Model, {
attachTo: card.class.FavoriteCard,
descriptor: view.viewlet.Table,
+3 -1
View File
@@ -57,6 +57,8 @@ export default mergeIds(cardId, card, {
GetCardLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
CardCustomLinkMatch: '' as Resource<(doc: Doc) => boolean>,
CardCustomLinkEncode: '' as Resource<(doc: Doc) => Location>,
CheckRelationsSectionVisibility: '' as Resource<(doc: Card) => Promise<boolean>>
CheckRelationsSectionVisibility: '' as Resource<(doc: Card) => Promise<boolean>>,
CheckOldMessagesSectionVisibility: '' as Resource<(doc: Card) => Promise<boolean>>,
CheckCommunicationMessagesSectionVisibility: '' as Resource<(doc: Card) => Promise<boolean>>
}
})
+1
View File
@@ -43,6 +43,7 @@
"@hcengineering/presentation": "workspace:^0.7.0",
"@hcengineering/communication-types": "workspace:^0.7.12",
"@hcengineering/communication-resources": "workspace:^0.7.0",
"@hcengineering/activity": "workspace:^0.7.0",
"@hcengineering/core": "workspace:^0.7.24",
"@hcengineering/ui": "workspace:^0.7.0",
"@hcengineering/setting": "workspace:^0.7.0",
+9 -7
View File
@@ -13,7 +13,7 @@
import { getClient } from '@hcengineering/presentation'
import cardPlugin, { type Card, type CardSection } from '@hcengineering/card'
import { getResource } from '@hcengineering/platform'
import { getMetadata, getResource } from '@hcengineering/platform'
import { type Heading } from '@hcengineering/text-editor'
import communication from '@hcengineering/communication'
@@ -69,12 +69,14 @@ export function getCardToc (sections: CardSection[], tocBySection: Record<string
}
}
newToc.push({
id: communication.ids.CardMessagesSection,
titleIntl: communication.string.Messages,
level: 0,
group: communication.ids.CardMessagesSection
})
if (getMetadata(communication.metadata.Enabled) === true) {
newToc.push({
id: communication.ids.CardMessagesSection,
titleIntl: communication.string.Messages,
level: 0,
group: communication.ids.CardMessagesSection
})
}
return newToc
}
@@ -15,15 +15,12 @@
//
-->
<script lang="ts">
import { Analytics } from '@hcengineering/analytics'
import { Card, CardEvents } from '@hcengineering/card'
import { Ref } from '@hcengineering/core'
import { onMount } from 'svelte'
import { Analytics } from '@hcengineering/analytics'
import communication from '@hcengineering/communication'
import { getMetadata } from '@hcengineering/platform'
import EditCardNew from './EditCardNew.svelte'
import EditCardOld from './EditCardOld.svelte'
export let _id: Ref<Card>
export let readonly: boolean = false
@@ -39,8 +36,4 @@
})
</script>
{#if getMetadata(communication.metadata.Enabled) === true}
<EditCardNew {_id} {readonly} {embedded} {allowClose} on:close on:open />
{:else}
<EditCardOld {_id} {readonly} {embedded} on:close on:open />
{/if}
<EditCardNew {_id} {readonly} {embedded} {allowClose} on:close on:open />
@@ -15,12 +15,14 @@
<script lang="ts">
import { Card } from '@hcengineering/card'
import { resizeObserver } from '@hcengineering/ui'
import communication from '@hcengineering/communication'
import { NotificationContext } from '@hcengineering/communication-types'
import { Ref } from '@hcengineering/core'
import { resizeObserver } from '@hcengineering/ui'
import EditCardTableOfContents from './EditCardTableOfContents.svelte'
import { MessageInput } from '@hcengineering/communication-resources'
import { getMetadata } from '@hcengineering/platform'
import EditCardTableOfContents from './EditCardTableOfContents.svelte'
export let _id: Ref<Card>
export let doc: Card
@@ -55,7 +57,7 @@
<EditCardTableOfContents bind:this={content} bind:scrollDiv {doc} {readonly} {context} {isContextLoaded} />
{/key}
{/if}
{#if !readonly}
{#if !readonly && getMetadata(communication.metadata.Enabled) === true}
<div class="message-input" use:resizeObserver={onInputResize}>
<MessageInput
card={doc}
@@ -1,238 +0,0 @@
<!--
//
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
-->
<script lang="ts">
import { Attachments } from '@hcengineering/attachment-resources'
import { Card } from '@hcengineering/card'
import { Doc, Mixin, Ref, WithLookup } from '@hcengineering/core'
import notification from '@hcengineering/notification'
import { Panel } from '@hcengineering/panel'
import { getResource } from '@hcengineering/platform'
import { ComponentExtensions, createQuery, getClient } from '@hcengineering/presentation'
import {
Button,
EditBox,
FocusHandler,
IconMoreH,
createFocusManager,
getCurrentLocation,
navigate
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import {
ParentsNavigator,
RelationsEditor,
canChangeDoc,
getDocMixins,
showMenu
} from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy } from 'svelte'
import card from '../plugin'
import CardAttributeEditor from './CardAttributeEditor.svelte'
import CardIcon from './CardIcon.svelte'
import CardPresenter from './CardPresenter.svelte'
import Childs from './Childs.svelte'
import Content from './Content.svelte'
import TagsEditor from './TagsEditor.svelte'
import { permissionsStore } from '@hcengineering/contact-resources'
import CardVersionSelector from './CardVersionSelector.svelte'
export let _id: Ref<Card>
export let readonly: boolean = false
export let embedded: boolean = false
let useMaxWidth = getUseMaxWidth()
$: saveUseMaxWidth(useMaxWidth)
let lastId: Ref<Doc> = _id
const query = createQuery()
const dispatch = createEventDispatcher()
const client = getClient()
let doc: WithLookup<Card> | undefined
let title = ''
let innerWidth: number
const notificationClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
$: read(_id)
function read (_id: Ref<Doc>): void {
if (lastId !== _id) {
const prev = lastId
lastId = _id
void notificationClient.then((client) => client.readDoc(prev))
}
}
onDestroy(async () => {
void notificationClient.then((client) => client.readDoc(_id))
})
$: _id !== undefined &&
query.query(card.class.Card, { _id }, async (result) => {
if (result.length > 0) {
;[doc] = result
title = doc?.title ?? ''
} else {
const loc = getCurrentLocation()
loc.path.length = 3
navigate(loc)
}
})
$: _readonly = (readonly || doc?.readonly) ?? false
$: updatePermissionForbidden = doc && !canChangeDoc(doc?._class, doc?.space, $permissionsStore)
$: canSave = title.trim().length > 0 && !_readonly
async function saveTitle (ev: Event): Promise<void> {
ev.preventDefault()
if (doc === undefined || !canSave) {
return
}
const nameTrimmed = title.trim()
if (nameTrimmed.length > 0 && nameTrimmed !== doc.title) {
await client.update(doc, { title: nameTrimmed })
}
}
function getUseMaxWidth (): boolean {
const useMaxWidth = localStorage.getItem('document.useMaxWidth')
return useMaxWidth === 'true'
}
function saveUseMaxWidth (useMaxWidth: boolean): void {
localStorage.setItem('document.useMaxWidth', useMaxWidth.toString())
}
let content: HTMLElement
const manager = createFocusManager()
let mixins: Array<Mixin<Doc>> = []
$: mixins = doc !== undefined ? getDocMixins(doc) : []
</script>
<FocusHandler {manager} />
{#if doc !== undefined}
<Panel
object={doc}
allowClose={!embedded}
isAside={false}
isHeader={false}
isSub={false}
bind:useMaxWidth
printHeader={false}
{embedded}
adaptive={'default'}
bind:content
bind:innerWidth
floatAside={false}
on:open
on:close={() => dispatch('close')}
>
<svelte:fragment slot="beforeTitle">
<CardIcon value={doc} />
</svelte:fragment>
<svelte:fragment slot="title">
<ParentsNavigator element={doc} />
<CardPresenter value={doc} noUnderline />
</svelte:fragment>
<svelte:fragment slot="extra">
<ComponentExtensions
extension={card.extensions.EditCardHeaderExtension}
props={{
card: doc
}}
/>
<slot name="extra" />
</svelte:fragment>
<div class="container">
<div class="title flex-row-center">
<EditBox
focusIndex={1}
bind:value={title}
disabled={_readonly || updatePermissionForbidden}
placeholder={card.string.Card}
on:blur={(evt) => saveTitle(evt)}
shrink
/>
</div>
<CardVersionSelector bind:value={doc} />
<TagsEditor {doc} readonly={_readonly} />
<CardAttributeEditor value={doc} {mixins} readonly={_readonly} ignoreKeys={['title', 'content', 'parent']} />
<Content {doc} readonly={_readonly || updatePermissionForbidden} bind:content />
</div>
<ComponentExtensions
extension={card.extensions.EditCardExtension}
props={{
card: doc
}}
/>
<Childs object={doc} readonly={_readonly} />
<RelationsEditor object={doc} readonly={_readonly} />
<Attachments
objectId={doc._id}
_class={doc._class}
space={doc.space}
readonly={_readonly}
attachments={doc.attachments ?? 0}
/>
<svelte:fragment slot="utils">
{#if !_readonly}
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
dataId={'btnMoreActions'}
on:click={(e) => {
showMenu(e, { object: doc, excludedActions: [view.action.Open] })
}}
/>
{/if}
</svelte:fragment>
</Panel>
{/if}
<style lang="scss">
.container {
display: flex;
flex-direction: column;
width: 100%;
margin: auto;
}
.title {
font-size: 2.25rem;
margin-top: 1.75rem;
margin-bottom: 1rem;
}
</style>
@@ -15,18 +15,18 @@
<script lang="ts">
import card, { Card, CardSection, CardViewDefaults } from '@hcengineering/card'
import { Component, Loading, Scroller, ModernButton } from '@hcengineering/ui'
import { getClient } from '@hcengineering/presentation'
import communication from '@hcengineering/communication'
import { NotificationContext } from '@hcengineering/communication-types'
import { Ref } from '@hcengineering/core'
import { SvelteComponent, tick } from 'svelte'
import { TableOfContents } from '@hcengineering/text-editor-resources'
import { getClient } from '@hcengineering/presentation'
import { Heading } from '@hcengineering/text-editor'
import communication from '@hcengineering/communication'
import { MessagesSection } from '@hcengineering/communication-resources'
import { TableOfContents } from '@hcengineering/text-editor-resources'
import { Component, Loading, ModernButton, Scroller } from '@hcengineering/ui'
import { SvelteComponent, tick } from 'svelte'
import { CardSectionAction } from '../types'
import { getMetadata } from '@hcengineering/platform'
import { getCardSections, getCardToc } from '../card'
import { CardSectionAction } from '../types'
export let doc: Card
export let context: NotificationContext | undefined = undefined
@@ -216,6 +216,12 @@
$: updateToc(sections, subTocBySection)
$: showOverlay = !isScrollInitialized || Object.values(sectionOverlays).some((it) => it)
const onRenderTopChange = (active: boolean): void => {
renderTopSections = active
}
const bottomPadding = getMetadata(communication.metadata.Enabled) === true ? 'var(--spacing-3)' : undefined
</script>
<div class="hulyComponent-content__container columns relative">
@@ -240,7 +246,7 @@
<Scroller
padding="0"
{hideBar}
bottomPadding="var(--spacing-3)"
{bottomPadding}
disablePointerEventsOnScroll
disableOverscroll
bind:divScroll={scrollDiv}
@@ -259,7 +265,12 @@
scrollDiv,
contentDiv: sectionElement[section._id],
navigation: selectedToc?.id,
hidden: !renderTopSections
hidden: !renderTopSections,
isDefault: defaults?.defaultSection === section._id,
active: selectedToc?.group === section._id,
context,
isContextLoaded,
onRenderTopChange
}}
on:loaded={() => {
handleSectionLoaded(section._id)
@@ -273,34 +284,6 @@
/>
</div>
{/each}
<div id={messagesId} bind:this={sectionElement[messagesId]} class="section">
<MessagesSection
bind:this={sectionRef[messagesId]}
{doc}
{readonly}
{isContextLoaded}
{scrollDiv}
{context}
contentDiv={sectionElement[messagesId]}
active={selectedToc?.group === messagesId}
isDefault={defaults?.defaultSection === messagesId}
on:top-loaded={() => {
renderTopSections = true
}}
on:top-hidden={() => {
renderTopSections = false
}}
on:loaded={() => {
handleSectionLoaded(messagesId)
}}
on:change={(ev) => {
handleChangeNavigation(messagesId, ev.detail)
}}
on:action={(ev) => {
handleAction(messagesId, ev.detail)
}}
/>
</div>
</div>
</Scroller>
{#if toc.length > 0 && (bottomOffset > 400 || canScrollDown()) && selectedToc?.group === messagesId}
@@ -0,0 +1,50 @@
<!--
// Copyright © 2026 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { MessagesSection } from '@hcengineering/communication-resources'
import { NotificationContext } from '@hcengineering/communication-types'
export let doc: Card
export let readonly: boolean = false
export let scrollDiv: HTMLDivElement | undefined | null = undefined
export let context: NotificationContext | undefined = undefined
export let contentDiv: HTMLDivElement
export let isContextLoaded: boolean = false
export let active: boolean = false
export let isDefault: boolean = false
export let onRenderTopChange: (active: boolean) => void
</script>
<MessagesSection
{doc}
{readonly}
{isContextLoaded}
{scrollDiv}
{context}
{contentDiv}
{active}
{isDefault}
on:top-loaded={() => {
onRenderTopChange(true)
}}
on:top-hidden={() => {
onRenderTopChange(false)
}}
on:loaded
on:change
on:action
/>
@@ -0,0 +1,36 @@
<!--
// Copyright © 2026 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Component } from '@hcengineering/ui'
import activity from '@hcengineering/activity'
import { Card } from '@hcengineering/card'
export let doc: Card
export let readonly: boolean = false
export let contentDiv: HTMLDivElement
</script>
<div class="flex-col w-full px-4">
<Component
is={activity.component.Activity}
props={{
object: doc,
showCommenInput: !readonly,
focusIndex: 1000,
boundary: contentDiv
}}
/>
</div>
+10 -1
View File
@@ -30,6 +30,8 @@ import {
cardCustomLinkMatch,
openCardInSidebar,
checkRelationsSectionVisibility,
checkOldMessagesSectionVisibility,
checkCommunicationMessagesSectionVisibility,
getSpaceAccessPublicLink,
canGetSpaceAccessPublicLink,
cardFactory,
@@ -68,6 +70,9 @@ import ChildrenCardSection from './components/sections/ChildrenSection.svelte'
import ContentCardSection from './components/sections/ContentSection.svelte'
import PropertiesCardSection from './components/sections/PropertiesSection.svelte'
import RelationsCardSection from './components/sections/RelationsSection.svelte'
import CardCommunicatiomMessages from './components/sections/CardCommunicatiomMessages.svelte'
import OldMessagesCardSection from './components/sections/OldMessagesCardSection.svelte'
import FavoriteCardPresenter from './components/FavoriteCardPresenter.svelte'
import CardTagsColored from './components/CardTagsColored.svelte'
import CardTagColored from './components/CardTagColored.svelte'
@@ -137,7 +142,9 @@ export default async (): Promise<Resources> => ({
ChildrenSection: ChildrenCardSection,
ContentSection: ContentCardSection,
PropertiesSection: PropertiesCardSection,
RelationsSection: RelationsCardSection
RelationsSection: RelationsCardSection,
OldMessagesSection: OldMessagesCardSection,
CommunicationMessagesSection: CardCommunicatiomMessages
},
completion: {
CardQuery: queryCard
@@ -158,6 +165,8 @@ export default async (): Promise<Resources> => ({
CardCustomLinkEncode: cardCustomLinkEncode,
OpenCardInSidebar: openCardInSidebar,
CheckRelationsSectionVisibility: checkRelationsSectionVisibility,
CheckOldMessagesSectionVisibility: checkOldMessagesSectionVisibility,
CheckCommunicationMessagesSectionVisibility: checkCommunicationMessagesSectionVisibility,
GetSpaceAccessPublicLink: getSpaceAccessPublicLink,
CanGetSpaceAccessPublicLink: canGetSpaceAccessPublicLink,
CardFactory: cardFactory
+3 -1
View File
@@ -76,7 +76,9 @@ export default mergeIds(cardId, card, {
ChildrenSection: '' as AnyComponent,
ContentSection: '' as AnyComponent,
PropertiesSection: '' as AnyComponent,
RelationsSection: '' as AnyComponent
RelationsSection: '' as AnyComponent,
CommunicationMessagesSection: '' as AnyComponent,
OldMessagesSection: '' as AnyComponent
},
completion: {
CardQuery: '' as Resource<ObjectSearchFactory>,
+9
View File
@@ -13,6 +13,7 @@
import { type AccountClient, getClient as getAccountClientRaw } from '@hcengineering/account-client'
import { Analytics } from '@hcengineering/analytics'
import communication from '@hcengineering/communication'
import { type Card, CardEvents, cardId, type CardSpace, type MasterTag, type Tag } from '@hcengineering/card'
import core, {
AccountRole,
@@ -615,6 +616,14 @@ export function cardCustomLinkEncode (doc: Card): Location {
return loc
}
export async function checkOldMessagesSectionVisibility (doc: Card): Promise<boolean> {
return getMetadata(communication.metadata.Enabled) !== true
}
export async function checkCommunicationMessagesSectionVisibility (doc: Card): Promise<boolean> {
return getMetadata(communication.metadata.Enabled) === true
}
export async function checkRelationsSectionVisibility (doc: Card): Promise<boolean> {
const client = getClient()
const h = client.getHierarchy()
+3 -1
View File
@@ -211,7 +211,9 @@ const cardPlugin = plugin(cardId, {
Children: '' as Ref<CardSection>,
Content: '' as Ref<CardSection>,
Properties: '' as Ref<CardSection>,
Relations: '' as Ref<CardSection>
Relations: '' as Ref<CardSection>,
OldMessages: '' as Ref<CardSection>,
CommunicationMessages: '' as Ref<CardSection>
},
ids: {
CardWidget: '' as Ref<Doc>