mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
UBERF-5594: render mentions before object is loaded (#4738)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
e454342dac
commit
a85db1b78a
@ -277,6 +277,14 @@ export function createModel (builder: Builder): void {
|
||||
component: contact.component.CreateOrganization
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Contact, core.class.Class, view.mixin.ObjectIdentifier, {
|
||||
provider: contact.function.ContactTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Person, core.class.Class, view.mixin.ObjectTooltip, {
|
||||
provider: contact.function.PersonTooltipProvider
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
|
@ -102,6 +102,10 @@ export function createModel (builder: Builder): void {
|
||||
presenter: inventory.component.CategoryPresenter
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Category, core.class.Class, view.mixin.ObjectIdentifier, {
|
||||
provider: inventory.function.CategoryIdProvider
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Category, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: inventory.component.CategoryRefPresenter
|
||||
})
|
||||
@ -110,6 +114,10 @@ export function createModel (builder: Builder): void {
|
||||
presenter: inventory.component.ProductPresenter
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.ObjectIdentifier, {
|
||||
provider: inventory.function.ProductIdProvider
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Variant, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: inventory.component.VariantPresenter
|
||||
})
|
||||
|
@ -15,10 +15,10 @@
|
||||
//
|
||||
|
||||
import { type ChatMessageViewlet } from '@hcengineering/chunter'
|
||||
import type { Ref } from '@hcengineering/core'
|
||||
import type { Client, Doc, Ref } from '@hcengineering/core'
|
||||
import { inventoryId } from '@hcengineering/inventory'
|
||||
import inventory from '@hcengineering/inventory-resources/src/plugin'
|
||||
import { type IntlString, mergeIds } from '@hcengineering/platform'
|
||||
import { type IntlString, mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import type { AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import { type Action, type ActionCategory, type ViewAction, type Viewlet } from '@hcengineering/view'
|
||||
export default mergeIds(inventoryId, inventory, {
|
||||
@ -51,5 +51,9 @@ export default mergeIds(inventoryId, inventory, {
|
||||
ids: {
|
||||
ProductChatMessageViewlet: '' as Ref<ChatMessageViewlet>,
|
||||
CategoryChatMessageViewlet: '' as Ref<ChatMessageViewlet>
|
||||
},
|
||||
function: {
|
||||
ProductIdProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||
CategoryIdProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>
|
||||
}
|
||||
})
|
||||
|
@ -573,6 +573,10 @@ export function createModel (builder: Builder): void {
|
||||
titleProvider: lead.function.LeadTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(lead.class.Lead, core.class.Class, view.mixin.ObjectIdentifier, {
|
||||
provider: lead.function.LeadIdProvider
|
||||
})
|
||||
|
||||
builder.mixin(lead.class.Lead, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: ['attachedTo']
|
||||
})
|
||||
|
@ -1132,6 +1132,10 @@ export function createModel (builder: Builder): void {
|
||||
titleProvider: recruit.function.VacTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Vacancy, core.class.Class, view.mixin.ObjectIdentifier, {
|
||||
provider: recruit.function.VacTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.LinkProvider, {
|
||||
encode: recruit.function.GetObjectLinkFragment
|
||||
})
|
||||
|
@ -209,6 +209,10 @@ function defineFilters (builder: Builder): void {
|
||||
titleProvider: tracker.function.MilestoneTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Milestone, core.class.Class, view.mixin.ObjectIdentifier, {
|
||||
provider: tracker.function.MilestoneTitleProvider
|
||||
})
|
||||
|
||||
//
|
||||
// Project
|
||||
//
|
||||
@ -234,6 +238,10 @@ function defineFilters (builder: Builder): void {
|
||||
titleProvider: tracker.function.ComponentTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Component, core.class.Class, view.mixin.ObjectIdentifier, {
|
||||
provider: tracker.function.ComponentTitleProvider
|
||||
})
|
||||
|
||||
//
|
||||
// Type Milestone Status
|
||||
//
|
||||
|
@ -30,7 +30,7 @@ import core, { TClass, TDoc } from '@hcengineering/model-core'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import { type Asset, type IntlString, type Resource, type Status } from '@hcengineering/platform'
|
||||
import { type AnyComponent, type Location } from '@hcengineering/ui/src/types'
|
||||
import { type AnyComponent, type LabelAndProps, type Location } from '@hcengineering/ui/src/types'
|
||||
import {
|
||||
type Action,
|
||||
type ActionCategory,
|
||||
@ -86,7 +86,8 @@ import {
|
||||
type ViewletDescriptor,
|
||||
type ViewletPreference,
|
||||
type ObjectIdentifier,
|
||||
type ObjectIcon
|
||||
type ObjectIcon,
|
||||
type ObjectTooltip
|
||||
} from '@hcengineering/view'
|
||||
|
||||
import view from './plugin'
|
||||
@ -270,6 +271,11 @@ export class TObjectIdentifier extends TClass implements ObjectIdentifier {
|
||||
provider!: Resource<<T extends Doc>(client: Client, ref: Ref<T>, doc?: T) => Promise<string>>
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ObjectTooltip, core.class.Class)
|
||||
export class TObjectTooltip extends TClass implements ObjectTooltip {
|
||||
provider!: Resource<(client: Client, doc?: Doc | null) => Promise<LabelAndProps | undefined>>
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ListHeaderExtra, core.class.Class)
|
||||
export class TListHeaderExtra extends TClass implements ListHeaderExtra {
|
||||
presenters!: AnyComponent[]
|
||||
@ -459,6 +465,7 @@ export function createModel (builder: Builder): void {
|
||||
TAggregation,
|
||||
TGroupping,
|
||||
TObjectIdentifier,
|
||||
TObjectTooltip,
|
||||
TObjectIcon
|
||||
)
|
||||
|
||||
|
@ -14,12 +14,12 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { CheckBox, Component, navigate, parseLocation } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
import { CheckBox, navigate, parseLocation } from '@hcengineering/ui'
|
||||
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
|
||||
import presentation from '../../plugin'
|
||||
import ObjectNode from './ObjectNode.svelte'
|
||||
|
||||
export let nodes: NodeListOf<any>
|
||||
|
||||
@ -48,11 +48,11 @@
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
function correctClass (clName: string): string {
|
||||
function correctClass (clName: string): Ref<Class<Doc>> {
|
||||
if (clName === 'contact:class:Employee') {
|
||||
return 'contact:mixin:Employee'
|
||||
return 'contact:mixin:Employee' as Ref<Class<Doc>>
|
||||
}
|
||||
return clName
|
||||
return clName as Ref<Class<Doc>>
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -126,17 +126,11 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else if node.nodeName === 'SPAN'}
|
||||
{#if node.getAttribute('data-objectclass') !== undefined && node.getAttribute('data-id') !== undefined}
|
||||
<Component
|
||||
is={view.component.ObjectPresenter}
|
||||
inline
|
||||
props={{
|
||||
objectId: node.getAttribute('data-id'),
|
||||
title: node.getAttribute('data-label'),
|
||||
_class: correctClass(node.getAttribute('data-objectclass')),
|
||||
inline: true
|
||||
}}
|
||||
/>
|
||||
{@const objectId = node.getAttribute('data-id')}
|
||||
{@const objectClass = node.getAttribute('data-objectclass')}
|
||||
|
||||
{#if objectClass !== undefined && objectId !== undefined}
|
||||
<ObjectNode _id={objectId} _class={correctClass(objectClass)} title={node.getAttribute('data-label')} />
|
||||
{:else}
|
||||
<svelte:self nodes={node.childNodes} />
|
||||
{/if}
|
||||
|
@ -0,0 +1,48 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { Component } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
import { createQuery } from '../../utils'
|
||||
|
||||
export let _id: Ref<Doc> | undefined = undefined
|
||||
export let _class: Ref<Class<Doc>> | undefined = undefined
|
||||
export let title: string = ''
|
||||
|
||||
const docQuery = createQuery()
|
||||
|
||||
let doc: Doc | undefined = undefined
|
||||
|
||||
$: if (_class != null && _id != null) {
|
||||
docQuery.query(_class, { _id }, (r) => {
|
||||
doc = r.shift()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !doc}
|
||||
<span class="antiMention">@{title}</span>
|
||||
{:else}
|
||||
<Component
|
||||
is={view.component.ObjectMention}
|
||||
showLoading={false}
|
||||
props={{
|
||||
object: doc,
|
||||
title
|
||||
}}
|
||||
/>
|
||||
{/if}
|
@ -17,7 +17,7 @@
|
||||
import { Organization } from '@hcengineering/contact'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
import Company from './icons/Company.svelte'
|
||||
|
||||
export let value: Organization
|
||||
@ -29,18 +29,18 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<DocNavLink {disabled} {inline} object={value} {accent} {noUnderline}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>
|
||||
@{value.name}
|
||||
</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
|
||||
{:else}
|
||||
<DocNavLink {disabled} object={value} {accent} {noUnderline}>
|
||||
<div class="flex-presenter" style:max-width={maxWidth} use:tooltip={{ label: getEmbeddedLabel(value.name) }}>
|
||||
<div class="icon circle"><Company size={'small'} /></div>
|
||||
<div class="icon circle">
|
||||
<Company size={'small'} />
|
||||
</div>
|
||||
<span class="overflow-label label" class:no-underline={noUnderline || disabled} class:fs-bold={accent}
|
||||
>{value.name}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Employee, Person } from '@hcengineering/contact'
|
||||
import { IconSize, LabelAndProps, tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
import Avatar from './Avatar.svelte'
|
||||
|
||||
export let value: Person | Employee | undefined | null
|
||||
@ -35,12 +35,10 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<DocNavLink object={value} onClick={onEdit} {disabled} {noUnderline} {inline} {colorInherit} {accent} noOverflow>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={disabled ? undefined : showTooltip}>
|
||||
@{name}
|
||||
</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {accent} {noUnderline} {colorInherit} onClick={onEdit} />
|
||||
{:else}
|
||||
<DocNavLink object={value} onClick={onEdit} {disabled} {noUnderline} {colorInherit} {accent} noOverflow>
|
||||
<span
|
||||
use:tooltip={disabled ? undefined : showTooltip}
|
||||
class="antiPresenter"
|
||||
@ -62,6 +60,6 @@
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { getName, Person } from '@hcengineering/contact'
|
||||
import { getEmbeddedLabel, IntlString } from '@hcengineering/platform'
|
||||
import type { LabelAndProps, IconSize } from '@hcengineering/ui'
|
||||
import { personByIdStore, PersonLabelTooltip } from '..'
|
||||
import { getPersonTooltip, personByIdStore, PersonLabelTooltip } from '..'
|
||||
import PersonContent from './PersonContent.svelte'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
@ -48,12 +48,9 @@
|
||||
value: Person | null | undefined
|
||||
): LabelAndProps | undefined {
|
||||
if (!tooltipLabels) {
|
||||
return !value
|
||||
? undefined
|
||||
: {
|
||||
label: getEmbeddedLabel(getName(client.getHierarchy(), value))
|
||||
}
|
||||
return getPersonTooltip(client, value)
|
||||
}
|
||||
|
||||
const direction = tooltipLabels?.direction
|
||||
const component = value ? tooltipLabels.component : undefined
|
||||
const label = value
|
||||
|
@ -119,6 +119,7 @@ import {
|
||||
getCurrentEmployeeEmail,
|
||||
getCurrentEmployeeName,
|
||||
getCurrentEmployeePosition,
|
||||
getPersonTooltip,
|
||||
resolveLocation
|
||||
} from './utils'
|
||||
|
||||
@ -373,7 +374,8 @@ export default async (): Promise<Resources> => ({
|
||||
GetContactFirstName: getContactFirstName,
|
||||
GetContactLastName: getContactLastName,
|
||||
GetContactLink: getContactLink,
|
||||
ContactTitleProvider: contactTitleProvider
|
||||
ContactTitleProvider: contactTitleProvider,
|
||||
PersonTooltipProvider: getPersonTooltip
|
||||
},
|
||||
resolver: {
|
||||
Location: resolveLocation
|
||||
|
@ -15,9 +15,9 @@
|
||||
//
|
||||
|
||||
import contact, { contactId } from '@hcengineering/contact'
|
||||
import { type Doc } from '@hcengineering/core'
|
||||
import { type Client, type Doc } from '@hcengineering/core'
|
||||
import { type IntlString, mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import { type Location } from '@hcengineering/ui'
|
||||
import { type LabelAndProps, type Location } from '@hcengineering/ui'
|
||||
import { type FilterFunction, type SortFunc } from '@hcengineering/view'
|
||||
|
||||
export default mergeIds(contactId, contact, {
|
||||
@ -87,6 +87,7 @@ export default mergeIds(contactId, contact, {
|
||||
FilterChannelInResult: '' as FilterFunction,
|
||||
FilterChannelNinResult: '' as FilterFunction,
|
||||
FilterChannelHasMessagesResult: '' as FilterFunction,
|
||||
FilterChannelHasNewMessagesResult: '' as FilterFunction
|
||||
FilterChannelHasNewMessagesResult: '' as FilterFunction,
|
||||
PersonTooltipProvider: '' as Resource<(client: Client, doc?: Doc | null) => Promise<LabelAndProps | undefined>>
|
||||
}
|
||||
})
|
||||
|
@ -40,7 +40,7 @@ import {
|
||||
toIdMap
|
||||
} from '@hcengineering/core'
|
||||
import notification, { type DocNotifyContext, type InboxNotification } from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { getEmbeddedLabel, getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { type TemplateDataProvider } from '@hcengineering/templates'
|
||||
import {
|
||||
@ -48,11 +48,13 @@ import {
|
||||
type ResolvedLocation,
|
||||
type TabItem,
|
||||
getCurrentResolvedLocation,
|
||||
getPanelURI
|
||||
getPanelURI,
|
||||
type LabelAndProps
|
||||
} from '@hcengineering/ui'
|
||||
import view, { type Filter } from '@hcengineering/view'
|
||||
import { FilterQuery } from '@hcengineering/view-resources'
|
||||
import { derived, get, writable } from 'svelte/store'
|
||||
|
||||
import contact from './plugin'
|
||||
|
||||
export function formatDate (dueDateMs: Timestamp): string {
|
||||
@ -377,3 +379,13 @@ export async function contactTitleProvider (client: Client, ref: Ref<Contact>, d
|
||||
if (object === undefined) return ''
|
||||
return getName(client.getHierarchy(), object)
|
||||
}
|
||||
|
||||
export function getPersonTooltip (client: Client, value: Person | null | undefined): LabelAndProps | undefined {
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
return value == null
|
||||
? undefined
|
||||
: {
|
||||
label: getEmbeddedLabel(getName(hierarchy, value))
|
||||
}
|
||||
}
|
||||
|
@ -15,22 +15,20 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Category } from '@hcengineering/inventory'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { tooltip } from '@hcengineering/ui'
|
||||
import inventory from '../plugin'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
|
||||
export let value: Category
|
||||
export let inline: boolean = false
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<DocNavLink object={value} {inline}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: inventory.string.Category }}>@{value.name}</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} />
|
||||
{:else}
|
||||
<DocNavLink object={value}>
|
||||
<div class="flex-presenter overflow-label sm-tool-icon">
|
||||
{value.name}
|
||||
</div>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -15,8 +15,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Product } from '@hcengineering/inventory'
|
||||
import { Icon, tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { Icon } from '@hcengineering/ui'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
|
||||
import inventory from '../plugin'
|
||||
|
||||
export let value: Product
|
||||
@ -24,14 +25,14 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<DocNavLink {inline} object={value}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: inventory.string.Product }}>@{value.name}</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} />
|
||||
{:else}
|
||||
<DocNavLink object={value}>
|
||||
<div class="flex-presenter">
|
||||
<div class="icon"><Icon icon={inventory.icon.Products} size={'small'} /></div>
|
||||
<span class="label">{value.name}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -14,9 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type Doc } from '@hcengineering/core'
|
||||
import { type Client, type Doc, type Ref } from '@hcengineering/core'
|
||||
import { type Resources } from '@hcengineering/platform'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import { type Category, type Product } from '@hcengineering/inventory'
|
||||
|
||||
import Categories from './components/Categories.svelte'
|
||||
import CategoryPresenter from './components/CategoryPresenter.svelte'
|
||||
import CategoryRefPresenter from './components/CategoryRefPresenter.svelte'
|
||||
@ -27,9 +29,22 @@ import VariantPresenter from './components/VariantPresenter.svelte'
|
||||
import Variants from './components/Variants.svelte'
|
||||
import CreateProduct from './components/CreateProduct.svelte'
|
||||
|
||||
import product from './plugin'
|
||||
|
||||
async function createSubcategory (object: Doc): Promise<void> {
|
||||
showPopup(CreateCategory, { attachedTo: object._id })
|
||||
}
|
||||
async function getProductId (client: Client, ref: Ref<Product>, doc?: Product): Promise<string> {
|
||||
const object = doc ?? (await client.findOne(product.class.Product, { _id: ref }))
|
||||
if (object === undefined) return ''
|
||||
return object.name
|
||||
}
|
||||
|
||||
async function getCategoryId (client: Client, ref: Ref<Category>, doc?: Category): Promise<string> {
|
||||
const object = doc ?? (await client.findOne(product.class.Category, { _id: ref }))
|
||||
if (object === undefined) return ''
|
||||
return object.name
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
actionImpl: {
|
||||
@ -44,5 +59,9 @@ export default async (): Promise<Resources> => ({
|
||||
Variants,
|
||||
VariantPresenter,
|
||||
CreateProduct
|
||||
},
|
||||
function: {
|
||||
ProductIdProvider: getProductId,
|
||||
CategoryIdProvider: getCategoryId
|
||||
}
|
||||
})
|
||||
|
@ -15,8 +15,8 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Lead } from '@hcengineering/lead'
|
||||
import { Icon, tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { Icon } from '@hcengineering/ui'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
import lead from '@hcengineering/lead'
|
||||
|
||||
export let value: Lead
|
||||
@ -24,15 +24,14 @@
|
||||
export let disabled: boolean = false
|
||||
export let accent: boolean = false
|
||||
export let noUnderline: boolean = false
|
||||
|
||||
export let shouldShowAvatar: boolean = true
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<DocNavLink object={value} {inline} {disabled} {noUnderline} {accent}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: lead.string.Lead }}>@{value.identifier}</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {noUnderline} {accent} />
|
||||
{:else}
|
||||
<DocNavLink object={value} {disabled} {noUnderline} {accent}>
|
||||
<div class="flex-presenter">
|
||||
{#if shouldShowAvatar}
|
||||
<div class="icon"><Icon icon={lead.icon.Lead} size={'small'} /></div>
|
||||
@ -41,6 +40,6 @@
|
||||
>{value.identifier}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -15,6 +15,7 @@
|
||||
//
|
||||
|
||||
import { type Resources } from '@hcengineering/platform'
|
||||
|
||||
import CreateFunnel from './components/CreateFunnel.svelte'
|
||||
import CreateLead from './components/CreateLead.svelte'
|
||||
import EditLead from './components/EditLead.svelte'
|
||||
@ -25,11 +26,12 @@ import LeadsPresenter from './components/LeadsPresenter.svelte'
|
||||
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import CreateCustomer from './components/CreateCustomer.svelte'
|
||||
import NewItemsHeader from './components/NewItemsHeader.svelte'
|
||||
import { getLeadTitle } from './utils'
|
||||
import EditFunnel from './components/EditFunnel.svelte'
|
||||
import MyLeads from './components/MyLeads.svelte'
|
||||
import TitlePresenter from './components/TitlePresenter.svelte'
|
||||
|
||||
import { getLeadId, getLeadTitle } from './utils'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
CreateFunnel,
|
||||
@ -47,6 +49,7 @@ export default async (): Promise<Resources> => ({
|
||||
TitlePresenter
|
||||
},
|
||||
function: {
|
||||
LeadTitleProvider: getLeadTitle
|
||||
LeadTitleProvider: getLeadTitle,
|
||||
LeadIdProvider: getLeadId
|
||||
}
|
||||
})
|
||||
|
@ -53,6 +53,7 @@ export default mergeIds(leadId, lead, {
|
||||
TitlePresenter: '' as AnyComponent
|
||||
},
|
||||
function: {
|
||||
LeadTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>
|
||||
LeadTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||
LeadIdProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>
|
||||
}
|
||||
})
|
||||
|
@ -7,3 +7,9 @@ export async function getLeadTitle (client: TxOperations, ref: Ref<Doc>, doc?: L
|
||||
if (object === undefined) throw new Error(`Lead not found, _id: ${ref}`)
|
||||
return `LEAD-${object.number}`
|
||||
}
|
||||
|
||||
export async function getLeadId (client: TxOperations, ref: Ref<Lead>, doc?: Lead): Promise<string> {
|
||||
const object = doc ?? (await client.findOne(lead.class.Lead, { _id: ref }))
|
||||
if (object === undefined) throw new Error(`Lead not found, _id: ${ref}`)
|
||||
return object.identifier
|
||||
}
|
||||
|
@ -17,9 +17,8 @@
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import type { Applicant } from '@hcengineering/recruit'
|
||||
import recruit from '@hcengineering/recruit'
|
||||
import recruitPlg from '../plugin'
|
||||
import { Icon, tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { Icon } from '@hcengineering/ui'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
|
||||
export let value: Applicant
|
||||
export let inline: boolean = false
|
||||
@ -33,12 +32,10 @@
|
||||
</script>
|
||||
|
||||
{#if value && shortLabel}
|
||||
<DocNavLink object={value} {inline} {disabled} {noUnderline} {accent}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: recruitPlg.string.Application }}>
|
||||
@{#if shortLabel}{shortLabel}-{/if}{value.number}
|
||||
</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {noUnderline} {accent} />
|
||||
{:else}
|
||||
<DocNavLink object={value} {disabled} {noUnderline} {accent}>
|
||||
<div class="flex-presenter">
|
||||
{#if shouldShowAvatar}
|
||||
<div class="icon">
|
||||
@ -49,6 +46,6 @@
|
||||
{#if shortLabel}{shortLabel}-{/if}{value.number}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -15,11 +15,11 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
|
||||
import { Vacancy } from '@hcengineering/recruit'
|
||||
import { Icon, getPlatformAvatarColorForTextDef, themeStore, tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
import recruit from '../plugin'
|
||||
|
||||
export let value: Vacancy
|
||||
@ -38,18 +38,16 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<DocNavLink {disabled} object={value} {inline} {accent} {noUnderline} component={recruit.component.EditVacancy}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: recruit.string.Vacancy }}>
|
||||
@{value.name}
|
||||
</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {accent} {noUnderline} component={recruit.component.EditVacancy} />
|
||||
{:else}
|
||||
<DocNavLink {disabled} object={value} {accent} {noUnderline} component={recruit.component.EditVacancy}>
|
||||
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>
|
||||
<div class="icon"><Icon icon={recruit.icon.Vacancy} size={'small'} /></div>
|
||||
<span class="label nowrap" class:no-underline={noUnderline || disabled} class:fs-bold={accent}>
|
||||
{value.name}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -17,9 +17,9 @@
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Icon, Component as UIComponent, themeStore, tooltip } from '@hcengineering/ui'
|
||||
import { Icon, Component as UIComponent, themeStore } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let value: WithLookup<Component> | undefined
|
||||
@ -53,10 +53,10 @@
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center">
|
||||
<DocNavLink object={value} {onClick} {disabled} {noUnderline} {inline} {accent} component={view.component.EditDoc}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: tracker.string.Component }}>@{label}</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {noUnderline} {accent} {onClick} />
|
||||
{:else}
|
||||
<DocNavLink object={value} {onClick} {disabled} {noUnderline} {accent} component={view.component.EditDoc}>
|
||||
<span class="flex-presenter flex-row-center" class:list={kind === 'list'}>
|
||||
<div class="flex-row-center">
|
||||
{#if shouldShowAvatar}
|
||||
@ -69,8 +69,8 @@
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
|
||||
{#if presenters.length > 0}
|
||||
<div class="flex-row-center">
|
||||
|
@ -21,7 +21,8 @@
|
||||
import type { Issue } from '@hcengineering/tracker'
|
||||
import { AnySvelteComponent, Component, Icon, tooltip } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let value: WithLookup<Issue> | undefined
|
||||
@ -41,19 +42,7 @@
|
||||
</script>
|
||||
|
||||
{#if inline && value}
|
||||
<DocNavLink
|
||||
object={value}
|
||||
{onClick}
|
||||
{disabled}
|
||||
{noUnderline}
|
||||
{inline}
|
||||
component={tracker.component.EditIssue}
|
||||
shrink={0}
|
||||
>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: tracker.string.Issue }}>@{value.identifier}</span>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
<ObjectMention object={value} {disabled} {noUnderline} {onClick} component={tracker.component.EditIssue} />
|
||||
{#if presenters.length > 0}
|
||||
<div class="flex-row-center">
|
||||
{#each presenters as mixinPresenter}
|
||||
@ -63,15 +52,7 @@
|
||||
{/if}
|
||||
{:else if value}
|
||||
<div class="flex-row-center">
|
||||
<DocNavLink
|
||||
object={value}
|
||||
{onClick}
|
||||
{disabled}
|
||||
{noUnderline}
|
||||
{inline}
|
||||
component={tracker.component.EditIssue}
|
||||
shrink={0}
|
||||
>
|
||||
<DocNavLink object={value} {onClick} {disabled} {noUnderline} component={tracker.component.EditIssue} shrink={0}>
|
||||
<span class="issuePresenterRoot" class:list={kind === 'list'} class:cursor-pointer={!disabled}>
|
||||
{#if shouldShowAvatar}
|
||||
<div class="icon" use:tooltip={{ label: tracker.string.Issue }}>
|
||||
|
@ -22,8 +22,9 @@
|
||||
themeStore,
|
||||
tooltip
|
||||
} from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let value: WithLookup<Milestone> | undefined
|
||||
@ -47,10 +48,10 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<DocNavLink object={value} {disabled} {inline} {accent} {noUnderline} {onClick}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: tracker.string.Milestone }}>@{value.label}</span>
|
||||
{:else}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {noUnderline} {accent} {onClick} />
|
||||
{:else}
|
||||
<DocNavLink object={value} {disabled} {accent} {noUnderline} {onClick}>
|
||||
<div class="flex-presenter" use:tooltip={{ label: tracker.string.Milestone }}>
|
||||
{#if shouldShowAvatar}
|
||||
<div class="icon">
|
||||
@ -66,6 +67,6 @@
|
||||
{value.label}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -14,11 +14,13 @@ export function isIssueId (shortLink: string): boolean {
|
||||
return /^\S+-\d+$/.test(shortLink)
|
||||
}
|
||||
|
||||
export async function issueIdentifierProvider (client: TxOperations, ref: Ref<Doc>): Promise<string> {
|
||||
const object = await client.findOne(tracker.class.Issue, { _id: ref as Ref<Issue> })
|
||||
export async function issueIdentifierProvider (client: TxOperations, ref: Ref<Issue>, doc?: Issue): Promise<string> {
|
||||
const object = doc ?? (await client.findOne(tracker.class.Issue, { _id: ref }))
|
||||
|
||||
if (object === undefined) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return object.identifier
|
||||
}
|
||||
|
||||
|
125
plugins/view-resources/src/components/ObjectMention.svelte
Normal file
125
plugins/view-resources/src/components/ObjectMention.svelte
Normal file
@ -0,0 +1,125 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { AnyComponent, LabelAndProps, themeStore, tooltip } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { getResource, translate } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
|
||||
import DocNavLink from './DocNavLink.svelte'
|
||||
import { getDocIdentifier } from '../utils'
|
||||
|
||||
export let _id: Ref<Doc> | undefined = undefined
|
||||
export let _class: Ref<Class<Doc>> | undefined = undefined
|
||||
export let object: Doc | undefined | null
|
||||
export let title: string = ''
|
||||
export let component: AnyComponent | undefined = undefined
|
||||
export let disabled: boolean = false
|
||||
export let accent: boolean = false
|
||||
export let noUnderline: boolean = false
|
||||
export let colorInherit: boolean = false
|
||||
export let onClick: ((event: MouseEvent) => void) | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docQuery = createQuery()
|
||||
|
||||
let doc: Doc | undefined = object ?? undefined
|
||||
|
||||
let docLabel: string = ''
|
||||
let docTitle: string | undefined = undefined
|
||||
let docTooltip: LabelAndProps = {}
|
||||
let docComponent: AnyComponent
|
||||
|
||||
let displayTitle = ''
|
||||
|
||||
$: displayTitle = docTitle || title || docLabel
|
||||
$: docComponent = getPanelComponent(doc, _class)
|
||||
|
||||
$: if (object == null && _class != null && _id != null) {
|
||||
docQuery.query(_class, { _id }, (r) => {
|
||||
doc = r.shift()
|
||||
})
|
||||
} else if (object != null) {
|
||||
docQuery.unsubscribe()
|
||||
doc = object
|
||||
}
|
||||
|
||||
$: void updateDocTitle(doc)
|
||||
$: void updateDocTooltip(doc)
|
||||
$: void updateDocLabel(doc, _class)
|
||||
|
||||
function getPanelComponent (doc?: Doc, _class?: Ref<Class<Doc>>): AnyComponent {
|
||||
if (component !== undefined) {
|
||||
return component
|
||||
}
|
||||
|
||||
const resultClass = doc?._class ?? _class
|
||||
|
||||
if (resultClass === undefined) {
|
||||
return view.component.EditDoc
|
||||
} else {
|
||||
const panelComponent = hierarchy.classHierarchyMixin(resultClass, view.mixin.ObjectPanel)
|
||||
|
||||
return panelComponent?.component ?? view.component.EditDoc
|
||||
}
|
||||
}
|
||||
|
||||
async function updateDocLabel (doc?: Doc, _class?: Ref<Class<Doc>>): Promise<void> {
|
||||
const resultClass = doc?._class ?? _class
|
||||
|
||||
docLabel = resultClass ? await translate(hierarchy.getClass(resultClass).label, {}, $themeStore.language) : ''
|
||||
}
|
||||
|
||||
async function updateDocTitle (doc: Doc | undefined): Promise<void> {
|
||||
docTitle = doc ? await getDocIdentifier(client, doc._id, doc._class, doc) : undefined
|
||||
}
|
||||
|
||||
async function updateDocTooltip (doc?: Doc): Promise<void> {
|
||||
if (doc === undefined) {
|
||||
docTooltip = {}
|
||||
return
|
||||
}
|
||||
|
||||
const mixin = hierarchy.classHierarchyMixin(doc._class, view.mixin.ObjectTooltip)
|
||||
|
||||
if (mixin?.provider !== undefined) {
|
||||
const providerFn = await getResource(mixin.provider)
|
||||
|
||||
docTooltip = (await providerFn(client, doc)) ?? { label: hierarchy.getClass(doc._class).label }
|
||||
} else {
|
||||
docTooltip = { label: hierarchy.getClass(doc._class).label }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if displayTitle}
|
||||
<DocNavLink
|
||||
object={doc}
|
||||
component={docComponent}
|
||||
{disabled}
|
||||
{accent}
|
||||
{colorInherit}
|
||||
{noUnderline}
|
||||
inline
|
||||
noOverflow
|
||||
{onClick}
|
||||
>
|
||||
<span class="antiMention" class:reference={!disabled} use:tooltip={disabled ? undefined : docTooltip}>
|
||||
@{displayTitle}
|
||||
</span>
|
||||
</DocNavLink>
|
||||
{/if}
|
@ -87,6 +87,7 @@ import ArrayFilter from './components/filter/ArrayFilter.svelte'
|
||||
import SpaceHeader from './components/SpaceHeader.svelte'
|
||||
import ViewletContentView from './components/ViewletContentView.svelte'
|
||||
import AttachedDocPanel from './components/AttachedDocPanel.svelte'
|
||||
import ObjectMention from './components/ObjectMention.svelte'
|
||||
|
||||
import {
|
||||
afterResult,
|
||||
@ -193,7 +194,8 @@ export {
|
||||
SpaceHeader,
|
||||
ViewletContentView,
|
||||
HyperlinkEditor,
|
||||
IconPicker
|
||||
IconPicker,
|
||||
ObjectMention
|
||||
}
|
||||
|
||||
function PositionElementAlignment (e?: Event): PopupAlignment | undefined {
|
||||
@ -256,7 +258,8 @@ export default async (): Promise<Resources> => ({
|
||||
StatusRefPresenter,
|
||||
DateFilterPresenter,
|
||||
StringFilterPresenter,
|
||||
AttachedDocPanel
|
||||
AttachedDocPanel,
|
||||
ObjectMention
|
||||
},
|
||||
popup: {
|
||||
PositionElementAlignment
|
||||
|
@ -50,6 +50,7 @@ import {
|
||||
ObjectPanel,
|
||||
ObjectPresenter,
|
||||
ObjectTitle,
|
||||
ObjectTooltip,
|
||||
ObjectValidator,
|
||||
PreviewPresenter,
|
||||
SpaceHeader,
|
||||
@ -91,6 +92,7 @@ const view = plugin(viewId, {
|
||||
ObjectFactory: '' as Ref<Mixin<ObjectFactory>>,
|
||||
ObjectTitle: '' as Ref<Mixin<ObjectTitle>>,
|
||||
ObjectIdentifier: '' as Ref<Mixin<ObjectIdentifier>>,
|
||||
ObjectTooltip: '' as Ref<Mixin<ObjectTooltip>>,
|
||||
SpaceHeader: '' as Ref<Mixin<SpaceHeader>>,
|
||||
SpaceName: '' as Ref<Mixin<SpaceName>>,
|
||||
IgnoreActions: '' as Ref<Mixin<IgnoreActions>>,
|
||||
@ -153,7 +155,8 @@ const view = plugin(viewId, {
|
||||
GrowPresenter: '' as AnyComponent,
|
||||
DividerPresenter: '' as AnyComponent,
|
||||
IconWithEmoji: '' as AnyComponent,
|
||||
AttachedDocPanel: '' as AnyComponent
|
||||
AttachedDocPanel: '' as AnyComponent,
|
||||
ObjectMention: '' as AnyComponent
|
||||
},
|
||||
ids: {
|
||||
IconWithEmoji: '' as Asset
|
||||
|
@ -40,7 +40,13 @@ import {
|
||||
UXObject,
|
||||
WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import { AnyComponent, AnySvelteComponent, Location, Location as PlatformLocation } from '@hcengineering/ui'
|
||||
import {
|
||||
AnyComponent,
|
||||
AnySvelteComponent,
|
||||
type LabelAndProps,
|
||||
Location,
|
||||
Location as PlatformLocation
|
||||
} from '@hcengineering/ui'
|
||||
import { Asset, IntlString, Resource, Status } from '@hcengineering/platform'
|
||||
import { Preference } from '@hcengineering/preference'
|
||||
|
||||
@ -279,6 +285,13 @@ export interface ObjectIdentifier extends Class<Doc> {
|
||||
provider: Resource<<T extends Doc>(client: Client, ref: Ref<T>, doc?: T) => Promise<string>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ObjectTooltip extends Class<Doc> {
|
||||
provider: Resource<(client: Client, doc?: Doc | null) => Promise<LabelAndProps | undefined>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user