mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
UBERF-5827: add collaborative description for companies (#4851)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
e003fb29fb
commit
1a1b978f82
@ -40,7 +40,8 @@ import {
|
||||
type Class,
|
||||
type Domain,
|
||||
type Ref,
|
||||
type Timestamp
|
||||
type Timestamp,
|
||||
type Markup
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
Collection,
|
||||
@ -57,7 +58,8 @@ import {
|
||||
TypeString,
|
||||
TypeTimestamp,
|
||||
UX,
|
||||
type Builder
|
||||
type Builder,
|
||||
TypeCollaborativeMarkup
|
||||
} from '@hcengineering/model'
|
||||
import attachment from '@hcengineering/model-attachment'
|
||||
import chunter from '@hcengineering/model-chunter'
|
||||
@ -156,6 +158,10 @@ export class TMember extends TAttachedDoc implements Member {
|
||||
@Model(contact.class.Organization, contact.class.Contact)
|
||||
@UX(contact.string.Organization, contact.icon.Company, 'ORG', 'name')
|
||||
export class TOrganization extends TContact implements Organization {
|
||||
@Prop(TypeCollaborativeMarkup(), core.string.Description)
|
||||
@Index(IndexKind.FullText)
|
||||
description?: Markup
|
||||
|
||||
@Prop(Collection(contact.class.Member), contact.string.Members)
|
||||
members!: number
|
||||
}
|
||||
@ -771,6 +777,29 @@ export function createModel (builder: Builder): void {
|
||||
filters: []
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: contact.component.EditOrganizationPanel
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
label: view.string.Open,
|
||||
icon: view.icon.Open,
|
||||
action: view.actionImpl.ShowPanel,
|
||||
actionProps: {
|
||||
component: contact.component.EditOrganizationPanel,
|
||||
element: 'content'
|
||||
},
|
||||
input: 'focus',
|
||||
category: contact.category.Contact,
|
||||
override: [view.action.Open],
|
||||
keyBinding: ['keyE'],
|
||||
target: contact.class.Organization,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'create'
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Channel, core.class.Class, view.mixin.AttributeFilter, {
|
||||
component: contact.component.ChannelFilter
|
||||
})
|
||||
|
@ -70,10 +70,6 @@ export function createReviewModel (builder: Builder): void {
|
||||
presenter: recruit.component.OpinionPresenter
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Review, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: recruit.component.EditReview
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
|
@ -14,10 +14,13 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Channel, findContacts, Organization } from '@hcengineering/contact'
|
||||
import { AttachedData, fillDefaults, generateId, Ref, TxOperations, WithLookup } from '@hcengineering/core'
|
||||
import core, { AttachedData, fillDefaults, generateId, Ref, TxOperations, WithLookup } from '@hcengineering/core'
|
||||
import { Card, getClient, InlineAttributeBar } from '@hcengineering/presentation'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler, IconInfo, Label } from '@hcengineering/ui'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler, IconAttachment, IconInfo, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { AttachmentPresenter, AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
|
||||
import contact from '../plugin'
|
||||
import ChannelsDropdown from './ChannelsDropdown.svelte'
|
||||
import Company from './icons/Company.svelte'
|
||||
@ -32,7 +35,9 @@
|
||||
const id: Ref<Organization> = generateId()
|
||||
|
||||
const object: Organization = {
|
||||
name: ''
|
||||
name: '',
|
||||
description: '',
|
||||
attachments: 0
|
||||
} as unknown as Organization
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -41,8 +46,10 @@
|
||||
|
||||
fillDefaults(hierarchy, object, contact.class.Organization)
|
||||
|
||||
async function createOrganization () {
|
||||
async function createOrganization (): Promise<void> {
|
||||
await client.createDoc(contact.class.Organization, contact.space.Contacts, object, id)
|
||||
await descriptionBox.createAttachments(id)
|
||||
|
||||
for (const channel of channels) {
|
||||
await client.addCollection(
|
||||
contact.class.Channel,
|
||||
@ -69,10 +76,14 @@
|
||||
|
||||
let matches: WithLookup<Organization>[] = []
|
||||
let matchedChannels: AttachedData<Channel>[] = []
|
||||
$: findContacts(client, contact.class.Organization, object.name, channels).then((p) => {
|
||||
|
||||
$: void findContacts(client, contact.class.Organization, object.name, channels).then((p) => {
|
||||
matches = p.contacts as Organization[]
|
||||
matchedChannels = p.channels
|
||||
})
|
||||
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
@ -80,13 +91,14 @@
|
||||
<Card
|
||||
label={contact.string.CreateOrganization}
|
||||
okAction={createOrganization}
|
||||
hideAttachments={attachments.size === 0}
|
||||
canSave={object.name.length > 0}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
on:changeContent
|
||||
>
|
||||
<div class="flex-row-center clear-mins">
|
||||
<div class="flex-row-center clear-mins mb-3">
|
||||
<div class="mr-3">
|
||||
<Button icon={Company} size={'medium'} kind={'link-bordered'} noFocus />
|
||||
</div>
|
||||
@ -98,6 +110,29 @@
|
||||
focusIndex={1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AttachmentStyledBox
|
||||
bind:this={descriptionBox}
|
||||
objectId={id}
|
||||
_class={contact.class.Organization}
|
||||
space={contact.space.Contacts}
|
||||
alwaysEdit
|
||||
showButtons={false}
|
||||
bind:content={object.description}
|
||||
placeholder={core.string.Description}
|
||||
kind="indented"
|
||||
isScrollable={false}
|
||||
enableBackReferences={true}
|
||||
enableAttachments={false}
|
||||
on:attachments={(ev) => {
|
||||
if (ev.detail.size > 0) attachments = ev.detail.values
|
||||
else if (ev.detail.size === 0 && ev.detail.values != null) {
|
||||
attachments.clear()
|
||||
attachments = attachments
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<svelte:fragment slot="pool">
|
||||
<ChannelsDropdown
|
||||
bind:value={channels}
|
||||
@ -116,7 +151,30 @@
|
||||
extraProps={{ showNavigate: false }}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="attachments">
|
||||
{#if attachments.size > 0}
|
||||
{#each Array.from(attachments.values()) as attachment}
|
||||
<AttachmentPresenter
|
||||
value={attachment}
|
||||
showPreview
|
||||
removable
|
||||
on:remove={(result) => {
|
||||
if (result.detail !== undefined) descriptionBox.removeAttachmentById(result.detail._id)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
icon={IconAttachment}
|
||||
size="large"
|
||||
on:click={() => {
|
||||
descriptionBox.handleAttach()
|
||||
}}
|
||||
/>
|
||||
{#if matches.length > 0}
|
||||
<div class="flex-row-center error-color">
|
||||
<IconInfo size={'small'} />
|
||||
|
@ -0,0 +1,163 @@
|
||||
<script lang="ts">
|
||||
import { AttachmentStyleBoxCollabEditor } from '@hcengineering/attachment-resources'
|
||||
import core, { Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { Panel } from '@hcengineering/panel'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import presentation, {
|
||||
type AttributeCategory,
|
||||
createQuery,
|
||||
getClient,
|
||||
type KeyedAttribute
|
||||
} from '@hcengineering/presentation'
|
||||
import { type AnyComponent, Button, Component, IconMixin, IconMoreH, Label } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import {
|
||||
DocAttributeBar,
|
||||
DocNavLink,
|
||||
getCollectionCounter,
|
||||
getDocAttrsInfo,
|
||||
getDocMixins,
|
||||
showMenu
|
||||
} from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import { Organization } from '@hcengineering/contact'
|
||||
|
||||
import contact from '../plugin'
|
||||
import EditOrganization from './EditOrganization.svelte'
|
||||
|
||||
export let _id: Ref<Organization>
|
||||
export let embedded: boolean = false
|
||||
export let readonly: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const query = createQuery()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const inboxClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
|
||||
|
||||
const ignoreKeys = ['comments', 'name', 'channels', 'description', 'attachments']
|
||||
|
||||
let object: Organization | undefined = undefined
|
||||
let lastId: Ref<Organization> | undefined = undefined
|
||||
|
||||
let mixins: Mixin<Doc>[] = []
|
||||
let editors: Array<{ key: KeyedAttribute, editor: AnyComponent, category: AttributeCategory }> = []
|
||||
|
||||
let showAllMixins = false
|
||||
let saved = false
|
||||
|
||||
$: mixins = object ? getDocMixins(object, showAllMixins) : []
|
||||
|
||||
$: descriptionKey = client.getHierarchy().getAttribute(contact.class.Organization, 'description')
|
||||
|
||||
$: getDocAttrsInfo(mixins, ignoreKeys, contact.class.Organization).then((res) => {
|
||||
editors = res.editors
|
||||
})
|
||||
|
||||
$: updateObject(_id)
|
||||
|
||||
function updateObject (_id: Ref<Organization>): void {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
lastId = _id
|
||||
if (prev !== undefined) {
|
||||
void inboxClient.then((client) => client.readDoc(getClient(), prev))
|
||||
}
|
||||
query.query(contact.class.Organization, { _id }, (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
void inboxClient.then((client) => client.readDoc(getClient(), _id))
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if object}
|
||||
<Panel
|
||||
isHeader={false}
|
||||
isSub={false}
|
||||
isAside={true}
|
||||
{embedded}
|
||||
{object}
|
||||
on:open
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<DocNavLink noUnderline {object}>
|
||||
<div class="title">{object.name}</div>
|
||||
</DocNavLink>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="attributes" let:direction={dir}>
|
||||
{#if dir === 'column'}
|
||||
<DocAttributeBar {object} {mixins} {ignoreKeys} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="pre-utils">
|
||||
{#if saved}
|
||||
<Label label={presentation.string.Saved} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="utils">
|
||||
<Button
|
||||
icon={IconMoreH}
|
||||
iconProps={{ size: 'medium' }}
|
||||
kind={'icon'}
|
||||
on:click={(e) => {
|
||||
showMenu(e, { object, excludedActions: [view.action.Open] })
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={IconMixin}
|
||||
kind={'icon'}
|
||||
iconProps={{ size: 'medium' }}
|
||||
selected={showAllMixins}
|
||||
on:click={() => {
|
||||
showAllMixins = !showAllMixins
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex-col flex-grow flex-no-shrink step-tb-6">
|
||||
<EditOrganization {object} />
|
||||
<div class="flex-col flex-grow w-full mt-6 relative">
|
||||
<AttachmentStyleBoxCollabEditor
|
||||
focusIndex={30}
|
||||
{object}
|
||||
key={{ key: 'description', attr: descriptionKey }}
|
||||
placeholder={core.string.Description}
|
||||
on:saved={(evt) => {
|
||||
saved = evt.detail
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#each editors as editor}
|
||||
{#if editor.editor}
|
||||
<div class="step-tb-6">
|
||||
<Component
|
||||
is={editor.editor}
|
||||
props={{
|
||||
objectId: object._id,
|
||||
_class: editor.key.attr.attributeOf,
|
||||
object,
|
||||
space: object.space,
|
||||
key: editor.key,
|
||||
readonly,
|
||||
[editor.key.key]: getCollectionCounter(hierarchy, object, editor.key)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</Panel>
|
||||
{/if}
|
@ -18,6 +18,8 @@
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
|
||||
import contact from '../plugin'
|
||||
import Company from './icons/Company.svelte'
|
||||
|
||||
export let value: Organization
|
||||
@ -30,9 +32,15 @@
|
||||
|
||||
{#if value}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
|
||||
<ObjectMention
|
||||
object={value}
|
||||
{disabled}
|
||||
{accent}
|
||||
{noUnderline}
|
||||
component={contact.component.EditOrganizationPanel}
|
||||
/>
|
||||
{:else}
|
||||
<DocNavLink {disabled} object={value} {accent} {noUnderline}>
|
||||
<DocNavLink {disabled} object={value} {accent} {noUnderline} component={contact.component.EditOrganizationPanel}>
|
||||
<div class="flex-presenter" style:max-width={maxWidth} use:tooltip={{ label: getEmbeddedLabel(value.name) }}>
|
||||
<div class="icon circle">
|
||||
<Company size={'small'} />
|
||||
|
@ -103,6 +103,7 @@ import UsersList from './components/UsersList.svelte'
|
||||
import SelectUsersPopup from './components/SelectUsersPopup.svelte'
|
||||
import IconAddMember from './components/icons/AddMember.svelte'
|
||||
import UserDetails from './components/UserDetails.svelte'
|
||||
import EditOrganizationPanel from './components/EditOrganizationPanel.svelte'
|
||||
|
||||
import contact from './plugin'
|
||||
import {
|
||||
@ -332,7 +333,8 @@ export default async (): Promise<Resources> => ({
|
||||
PersonAccountFilterValuePresenter,
|
||||
DeleteConfirmationPopup,
|
||||
PersonAccountRefPresenter,
|
||||
PersonIcon
|
||||
PersonIcon,
|
||||
EditOrganizationPanel
|
||||
},
|
||||
completion: {
|
||||
EmployeeQuery: async (
|
||||
|
@ -37,7 +37,8 @@ import {
|
||||
type Timestamp,
|
||||
type TxOperations,
|
||||
getCurrentAccount,
|
||||
toIdMap
|
||||
toIdMap,
|
||||
type Class
|
||||
} from '@hcengineering/core'
|
||||
import notification, { type DocNotifyContext, type InboxNotification } from '@hcengineering/notification'
|
||||
import { getEmbeddedLabel, getResource } from '@hcengineering/platform'
|
||||
@ -268,14 +269,18 @@ async function generateLocation (loc: Location, id: Ref<Contact>): Promise<Resol
|
||||
: client.getHierarchy().isDerived(doc._class, contact.mixin.Employee)
|
||||
? 'employees'
|
||||
: 'persons'
|
||||
|
||||
const objectPanel = client.getHierarchy().classHierarchyMixin(doc._class as Ref<Class<Doc>>, view.mixin.ObjectPanel)
|
||||
const component = objectPanel?.component ?? view.component.EditDoc
|
||||
|
||||
return {
|
||||
loc: {
|
||||
path: [appComponent, workspace],
|
||||
fragment: getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content')
|
||||
fragment: getPanelURI(component, doc._id, doc._class, 'content')
|
||||
},
|
||||
defaultLocation: {
|
||||
path: [appComponent, workspace, contactId, special],
|
||||
fragment: getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content')
|
||||
fragment: getPanelURI(component, doc._id, doc._class, 'content')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ export interface Member extends AttachedDoc {
|
||||
*/
|
||||
export interface Organization extends Contact {
|
||||
members: number
|
||||
description?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,7 +197,8 @@ export const contactPlugin = plugin(contactId, {
|
||||
SpaceMembers: '' as AnyComponent,
|
||||
DeleteConfirmationPopup: '' as AnyComponent,
|
||||
AccountArrayEditor: '' as AnyComponent,
|
||||
PersonIcon: '' as AnyComponent
|
||||
PersonIcon: '' as AnyComponent,
|
||||
EditOrganizationPanel: '' as AnyComponent
|
||||
},
|
||||
channelProvider: {
|
||||
Email: '' as Ref<ChannelProvider>,
|
||||
|
@ -72,7 +72,8 @@ async function generateIdLocation (loc: Location, shortLink: string): Promise<Re
|
||||
}
|
||||
const appComponent = loc.path[0] ?? ''
|
||||
const workspace = loc.path[1] ?? ''
|
||||
const objectPanel = hierarchy.classHierarchyMixin(recruit.mixin.Candidate as Ref<Class<Doc>>, view.mixin.ObjectPanel)
|
||||
const objectPanel = hierarchy.classHierarchyMixin(Hierarchy.mixinOrClass(doc), view.mixin.ObjectPanel)
|
||||
|
||||
const component = objectPanel?.component ?? view.component.EditDoc
|
||||
const special = _class === recruit.mixin.Candidate ? 'talents' : 'organizations'
|
||||
const defaultPath = [appComponent, workspace, recruitId, special]
|
||||
|
@ -21,11 +21,9 @@
|
||||
import {
|
||||
ActionContext,
|
||||
AttributeCategory,
|
||||
AttributeCategoryOrder,
|
||||
AttributesBar,
|
||||
KeyedAttribute,
|
||||
createQuery,
|
||||
getAttributePresenterClass,
|
||||
getClient,
|
||||
hasResource
|
||||
} from '@hcengineering/presentation'
|
||||
@ -33,8 +31,8 @@
|
||||
import view from '@hcengineering/view'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
|
||||
import { DocNavLink, ParentsNavigator, getDocLabel, getDocMixins, showMenu } from '..'
|
||||
import { categorizeFields, getCollectionCounter, getFiltredKeys } from '../utils'
|
||||
import { DocNavLink, ParentsNavigator, getDocLabel, getDocMixins, showMenu, getDocAttrsInfo } from '..'
|
||||
import { getCollectionCounter } from '../utils'
|
||||
import DocAttributeBar from './DocAttributeBar.svelte'
|
||||
|
||||
export let _id: Ref<Doc>
|
||||
@ -96,8 +94,6 @@
|
||||
let mixins: Array<Mixin<Doc>> = []
|
||||
let showAllMixins = false
|
||||
|
||||
$: mixins = getDocMixins(object, showAllMixins, ignoreMixins, realObjectClass)
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let ignoreKeys: string[] = []
|
||||
@ -107,33 +103,14 @@
|
||||
let inplaceAttributes: string[] = []
|
||||
let ignoreMixins: Set<Ref<Mixin<Doc>>> = new Set<Ref<Mixin<Doc>>>()
|
||||
|
||||
$: mixins = getDocMixins(object, showAllMixins, ignoreMixins, realObjectClass)
|
||||
|
||||
async function updateKeys (): Promise<void> {
|
||||
const keysMap = new Map(getFiltredKeys(hierarchy, realObjectClass, ignoreKeys).map((p) => [p.attr._id, p]))
|
||||
for (const m of mixins) {
|
||||
const mkeys = getFiltredKeys(hierarchy, m._id, ignoreKeys)
|
||||
for (const key of mkeys) {
|
||||
keysMap.set(key.attr._id, key)
|
||||
}
|
||||
}
|
||||
const filtredKeys = Array.from(keysMap.values())
|
||||
const { attributes, collections } = categorizeFields(hierarchy, filtredKeys, collectionArrays, allowedCollections)
|
||||
const info = await getDocAttrsInfo(mixins, ignoreKeys, realObjectClass, allowedCollections, collectionArrays)
|
||||
|
||||
keys = attributes.map((it) => it.key)
|
||||
|
||||
const editors: Array<{ key: KeyedAttribute, editor: AnyComponent, category: AttributeCategory }> = []
|
||||
const newInplaceAttributes: string[] = []
|
||||
|
||||
for (const k of collections) {
|
||||
if (allowedCollections.includes(k.key.key)) continue
|
||||
const editor = await getFieldEditor(k.key)
|
||||
if (editor === undefined) continue
|
||||
if (k.category === 'inplace') {
|
||||
newInplaceAttributes.push(k.key.key)
|
||||
}
|
||||
editors.push({ key: k.key, editor, category: k.category })
|
||||
}
|
||||
inplaceAttributes = newInplaceAttributes
|
||||
fieldEditors = editors.sort((a, b) => AttributeCategoryOrder[a.category] - AttributeCategoryOrder[b.category])
|
||||
keys = info.keys
|
||||
inplaceAttributes = info.inplaceAttributes
|
||||
fieldEditors = info.editors
|
||||
}
|
||||
|
||||
interface MixinEditor {
|
||||
@ -166,37 +143,18 @@
|
||||
|
||||
$: editorFooter = getEditorFooter(_class, object)
|
||||
|
||||
$: getEditorOrDefault(realObjectClass, _id)
|
||||
$: void getEditorOrDefault(realObjectClass, _id)
|
||||
|
||||
async function getEditorOrDefault (_class: Ref<Class<Doc>>, _id: Ref<Doc>): Promise<void> {
|
||||
await updateKeys()
|
||||
mainEditor = getEditor(_class)
|
||||
}
|
||||
|
||||
async function getFieldEditor (key: KeyedAttribute): Promise<AnyComponent | undefined> {
|
||||
const attrClass = getAttributePresenterClass(hierarchy, key.attr)
|
||||
const clazz = hierarchy.getClass(attrClass.attrClass)
|
||||
const mix = {
|
||||
array: view.mixin.ArrayEditor,
|
||||
collection: view.mixin.CollectionEditor,
|
||||
inplace: view.mixin.InlineAttributEditor,
|
||||
attribute: view.mixin.AttributeEditor,
|
||||
object: undefined
|
||||
}
|
||||
const mixinRef = mix[attrClass.category]
|
||||
if (mixinRef) {
|
||||
const editorMixin = hierarchy.as(clazz, mixinRef)
|
||||
return (editorMixin as any).editor
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
let title: string | undefined = undefined
|
||||
let rawTitle: string = ''
|
||||
|
||||
$: if (object !== undefined) {
|
||||
getDocLabel(pClient, object).then((t) => {
|
||||
void getDocLabel(pClient, object).then((t) => {
|
||||
if (t) {
|
||||
rawTitle = t
|
||||
}
|
||||
@ -216,7 +174,7 @@
|
||||
let headerLoading = false
|
||||
$: {
|
||||
headerLoading = true
|
||||
getHeaderEditor(realObjectClass).then((r) => {
|
||||
void getHeaderEditor(realObjectClass).then((r) => {
|
||||
headerEditor = r
|
||||
headerLoading = false
|
||||
})
|
||||
@ -236,7 +194,7 @@
|
||||
collectionArrays = ev.detail.collectionArrays ?? []
|
||||
title = ev.detail.title
|
||||
mixins = getDocMixins(object, showAllMixins, ignoreMixins, realObjectClass)
|
||||
updateKeys()
|
||||
void updateKeys()
|
||||
}
|
||||
|
||||
$: finalTitle = title ?? rawTitle
|
||||
|
@ -53,12 +53,14 @@ import { type Restrictions } from '@hcengineering/guest'
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { getResource, translate } from '@hcengineering/platform'
|
||||
import {
|
||||
type AttributeCategory,
|
||||
AttributeCategoryOrder,
|
||||
getAttributePresenterClass,
|
||||
getClient,
|
||||
hasResource,
|
||||
isAdminUser,
|
||||
type AttributeCategory,
|
||||
type KeyedAttribute
|
||||
type KeyedAttribute,
|
||||
getFiltredKeys,
|
||||
isAdminUser
|
||||
} from '@hcengineering/presentation'
|
||||
import {
|
||||
ErrorPresenter,
|
||||
@ -1266,3 +1268,67 @@ export const restrictionStore = writable<Restrictions>({
|
||||
disableNavigation: false,
|
||||
disableActions: false
|
||||
})
|
||||
|
||||
export async function getDocAttrsInfo (
|
||||
mixins: Array<Mixin<Doc>>,
|
||||
ignoreKeys: string[],
|
||||
_class: Ref<Class<Doc>>,
|
||||
allowedCollections: string[] = [],
|
||||
collectionArrays: string[] = []
|
||||
): Promise<{
|
||||
keys: KeyedAttribute[]
|
||||
inplaceAttributes: string[]
|
||||
editors: Array<{ key: KeyedAttribute, editor: AnyComponent, category: AttributeCategory }>
|
||||
}> {
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
const keysMap = new Map(getFiltredKeys(hierarchy, _class, ignoreKeys).map((p) => [p.attr._id, p]))
|
||||
for (const m of mixins) {
|
||||
const mkeys = getFiltredKeys(hierarchy, m._id, ignoreKeys)
|
||||
for (const key of mkeys) {
|
||||
keysMap.set(key.attr._id, key)
|
||||
}
|
||||
}
|
||||
const filteredKeys = Array.from(keysMap.values())
|
||||
const { attributes, collections } = categorizeFields(hierarchy, filteredKeys, collectionArrays, allowedCollections)
|
||||
|
||||
const keys = attributes.map((it) => it.key)
|
||||
const editors: Array<{ key: KeyedAttribute, editor: AnyComponent, category: AttributeCategory }> = []
|
||||
const inplaceAttributes: string[] = []
|
||||
|
||||
for (const k of collections) {
|
||||
if (allowedCollections.includes(k.key.key)) continue
|
||||
const editor = await getAttrEditor(k.key, hierarchy)
|
||||
if (editor === undefined) continue
|
||||
if (k.category === 'inplace') {
|
||||
inplaceAttributes.push(k.key.key)
|
||||
}
|
||||
editors.push({ key: k.key, editor, category: k.category })
|
||||
}
|
||||
|
||||
return {
|
||||
keys,
|
||||
inplaceAttributes,
|
||||
editors: editors.sort((a, b) => AttributeCategoryOrder[a.category] - AttributeCategoryOrder[b.category])
|
||||
}
|
||||
}
|
||||
|
||||
async function getAttrEditor (key: KeyedAttribute, hierarchy: Hierarchy): Promise<AnyComponent | undefined> {
|
||||
const attrClass = getAttributePresenterClass(hierarchy, key.attr)
|
||||
const clazz = hierarchy.getClass(attrClass.attrClass)
|
||||
const mix = {
|
||||
array: view.mixin.ArrayEditor,
|
||||
collection: view.mixin.CollectionEditor,
|
||||
inplace: view.mixin.InlineAttributEditor,
|
||||
attribute: view.mixin.AttributeEditor,
|
||||
object: undefined as any
|
||||
}
|
||||
const mixinRef = mix[attrClass.category]
|
||||
if (mixinRef !== undefined) {
|
||||
const editorMixin = hierarchy.as(clazz, mixinRef)
|
||||
return (editorMixin as any).editor
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user