Vacancy configure (#1962)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov 2022-06-01 09:25:13 +06:00 committed by GitHub
parent a4353b0fc7
commit c6ff5f19ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 282 additions and 206 deletions

View File

@ -10,6 +10,10 @@ Platform:
- Fix skills/labels selection and show real usage counter
- Fix skills/labels activity
HR:
- Allow to configure vacancy table
## 0.6.22
Platform:

View File

@ -201,7 +201,7 @@ export function createModel (builder: Builder): void {
editor: contact.component.PersonEditor
})
builder.mixin(contact.class.Channel, core.class.Class, view.mixin.AttributePresenter, {
builder.mixin(contact.class.Channel, core.class.Class, view.mixin.CollectionPresenter, {
presenter: contact.component.ChannelsPresenter
})

View File

@ -134,10 +134,6 @@ export function createModel (builder: Builder): void {
}
})
builder.mixin(core.class.Space, core.class.Class, view.mixin.AttributePresenter, {
presenter: recruit.component.VacancyItemPresenter
})
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.CollectionEditor, {
editor: recruit.component.Applications
})
@ -269,6 +265,26 @@ export function createModel (builder: Builder): void {
hiddenKeys: ['name']
})
builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: recruit.class.Vacancy,
descriptor: view.viewlet.Table,
config: [
'',
{
key: '@applications',
label: recruit.string.Applications
},
'$lookup.company',
'location',
'description',
{
key: '@applications.modifiedOn',
label: core.string.Modified
}
],
hiddenKeys: ['name', 'space', 'modifiedOn']
})
builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: recruit.class.Applicant,
descriptor: task.viewlet.StatusTable,

View File

@ -16,10 +16,10 @@
<script lang="ts">
import { Doc, DocumentQuery } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation'
import { Tooltip, Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@anticrm/ui'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import { Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@anticrm/ui'
import type { Filter } from '@anticrm/view'
import { ActionContext, TableBrowser, ViewletSetting, FilterButton } from '@anticrm/view-resources'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import { ActionContext, FilterButton, TableBrowser, ViewletSettingButton } from '@anticrm/view-resources'
import contact from '../plugin'
import CreateContact from './CreateContact.svelte'
@ -90,17 +90,7 @@
kind={'primary'}
on:click={(ev) => showCreateDialog(ev)}
/>
{#if viewlet}
<Tooltip label={view.string.CustomizeView}>
<Button
icon={view.icon.Setting}
kind={'transparent'}
on:click={() => {
showPopup(ViewletSetting, { viewlet })
}}
/>
</Tooltip>
{/if}
<ViewletSettingButton {viewlet} />
</div>
{#if viewlet}

View File

@ -14,23 +14,22 @@
// limitations under the License.
-->
<script lang="ts">
import { createQuery, getClient } from '@anticrm/presentation'
import ui, {
Button,
EditWithIcon,
eventToHTMLElement,
Icon,
IconAdd,
IconSearch,
Label,
showPopup,
IconAdd,
eventToHTMLElement,
Loading,
Tooltip
showPopup
} from '@anticrm/ui'
import CreateProduct from './CreateProduct.svelte'
import inventory from '../plugin'
import { TableBrowser, ViewletSetting } from '@anticrm/view-resources'
import { createQuery, getClient } from '@anticrm/presentation'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import { TableBrowser, ViewletSettingButton } from '@anticrm/view-resources'
import inventory from '../plugin'
import CreateProduct from './CreateProduct.svelte'
let search = ''
$: resultQuery = search === '' ? {} : { $search: search }
@ -89,17 +88,7 @@
kind={'primary'}
on:click={(ev) => showCreateDialog(ev)}
/>
{#if descr}
<Tooltip label={view.string.CustomizeView}>
<Button
icon={view.icon.Setting}
kind={'transparent'}
on:click={() => {
showPopup(ViewletSetting, { viewlet: descr })
}}
/>
</Tooltip>
{/if}
<ViewletSettingButton viewlet={descr} />
</div>
{#if descr}

View File

@ -16,9 +16,9 @@
<script lang="ts">
import { Doc, DocumentQuery } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation'
import { Tooltip, Button, Icon, Label, Loading, showPopup, SearchEdit } from '@anticrm/ui'
import { Icon, Label, Loading, SearchEdit } from '@anticrm/ui'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import { ViewletSetting, TableBrowser } from '@anticrm/view-resources'
import { TableBrowser, ViewletSettingButton } from '@anticrm/view-resources'
import lead from '../plugin'
let search = ''
@ -70,17 +70,7 @@
updateResultQuery(search)
}}
/>
{#if descr}
<Tooltip label={view.string.CustomizeView}>
<Button
icon={view.icon.Setting}
kind={'transparent'}
on:click={() => {
showPopup(ViewletSetting, { viewlet: descr })
}}
/>
</Tooltip>
{/if}
<ViewletSettingButton viewlet={descr} />
</div>
{#if descr}

View File

@ -17,10 +17,10 @@
import { createQuery, getClient } from '@anticrm/presentation'
import { Applicant } from '@anticrm/recruit'
import task from '@anticrm/task'
import { Tooltip, Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@anticrm/ui'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import { Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@anticrm/ui'
import type { Filter } from '@anticrm/view'
import { TableBrowser, ViewletSetting, FilterButton } from '@anticrm/view-resources'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import { FilterButton, TableBrowser, ViewletSettingButton } from '@anticrm/view-resources'
import recruit from '../plugin'
import CreateApplication from './CreateApplication.svelte'
@ -83,17 +83,7 @@
}}
/>
<Button icon={IconAdd} label={recruit.string.ApplicationCreateLabel} kind={'primary'} on:click={showCreateDialog} />
{#if descr}
<Tooltip label={view.string.CustomizeView}>
<Button
icon={view.icon.Setting}
kind={'transparent'}
on:click={() => {
showPopup(ViewletSetting, { viewlet: descr })
}}
/>
</Tooltip>
{/if}
<ViewletSettingButton viewlet={descr} />
</div>
{#if descr}

View File

@ -17,10 +17,10 @@
import contact from '@anticrm/contact'
import { Doc, DocumentQuery } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation'
import { Tooltip, showPopup, Icon, Label, Loading, SearchEdit, Button, IconAdd } from '@anticrm/ui'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import { Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@anticrm/ui'
import type { Filter } from '@anticrm/view'
import { ActionContext, TableBrowser, ViewletSetting, FilterButton } from '@anticrm/view-resources'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import { ActionContext, FilterButton, TableBrowser, ViewletSettingButton } from '@anticrm/view-resources'
import recruit from '../plugin'
import CreateCandidate from './CreateCandidate.svelte'
@ -83,17 +83,7 @@
}}
/>
<Button icon={IconAdd} label={recruit.string.CandidateCreateLabel} kind={'primary'} on:click={showCreateDialog} />
{#if descr}
<Tooltip label={view.string.CustomizeView}>
<Button
icon={view.icon.Setting}
kind={'transparent'}
on:click={() => {
showPopup(ViewletSetting, { viewlet: descr })
}}
/>
</Tooltip>
{/if}
<ViewletSettingButton viewlet={descr} />
</div>
<ActionContext

View File

@ -14,20 +14,14 @@
-->
<script lang="ts">
import core, { Doc, DocumentQuery, Ref } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation'
import { createQuery, getClient } from '@anticrm/presentation'
import { Vacancy } from '@anticrm/recruit'
import { Button, getCurrentLocation, Icon, IconAdd, Label, navigate, SearchEdit, showPopup } from '@anticrm/ui'
import { TableBrowser } from '@anticrm/view-resources'
import { Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@anticrm/ui'
import view, { BuildModelKey, Filter, Viewlet, ViewletPreference } from '@anticrm/view'
import { FilterButton, TableBrowser, ViewletSettingButton } from '@anticrm/view-resources'
import recruit from '../plugin'
import CreateVacancy from './CreateVacancy.svelte'
function action (vacancy: Ref<Vacancy>): void {
const loc = getCurrentLocation()
loc.path[2] = vacancy
loc.path.length = 3
navigate(loc)
}
let search: string = ''
let resultQuery: DocumentQuery<Doc> = {}
@ -69,12 +63,82 @@
const modifiedSorting = (a: Doc, b: Doc) =>
(applications?.get(b._id as Ref<Vacancy>)?.modifiedOn ?? 0) -
(applications?.get(a._id as Ref<Vacancy>)?.modifiedOn ?? 0) ?? 0
const replacedKeys: Map<string, BuildModelKey> = new Map<string, BuildModelKey>([
[
'@applications',
{
key: '@applications',
presenter: recruit.component.VacancyCountPresenter,
label: recruit.string.Applications,
props: { applications },
sortingKey: '@applications',
sortingFunction: applicationSorting
}
],
[
'@applications.modifiedOn',
{
key: '',
presenter: recruit.component.VacancyModifiedPresenter,
label: core.string.Modified,
props: { applications },
sortingKey: 'modifiedOn',
sortingFunction: modifiedSorting
}
]
])
const client = getClient()
let filters: Filter[] = []
let descr: Viewlet | undefined
let loading = true
const preferenceQuery = createQuery()
let preference: ViewletPreference | undefined
client
.findOne<Viewlet>(view.class.Viewlet, {
attachTo: recruit.class.Vacancy,
descriptor: view.viewlet.Table
})
.then((res) => {
descr = res
if (res !== undefined) {
preferenceQuery.query(
view.class.ViewletPreference,
{
attachedTo: res._id
},
(res) => {
preference = res[0]
loading = false
},
{ limit: 1 }
)
}
})
function createConfig (descr: Viewlet, preference: ViewletPreference | undefined): (string | BuildModelKey)[] {
const base = preference?.config ?? descr.config
const result: (string | BuildModelKey)[] = []
for (const key of base) {
if (typeof key === 'string') {
result.push(replacedKeys.get(key) ?? key)
} else {
result.push(replacedKeys.get(key.key) ?? key)
}
}
return result
}
</script>
<div class="ac-header full">
<div class="ac-header__wrap-title">
<div class="ac-header__icon"><Icon icon={recruit.icon.Vacancy} size={'small'} /></div>
<span class="ac-header__title"><Label label={recruit.string.Vacancies} /></span>
<div class="ml-4"><FilterButton _class={recruit.mixin.Candidate} bind:filters /></div>
</div>
<SearchEdit
bind:value={search}
@ -83,40 +147,22 @@
}}
/>
<Button icon={IconAdd} label={recruit.string.VacancyCreateLabel} kind={'primary'} on:click={showCreateDialog} />
<ViewletSettingButton viewlet={descr} />
</div>
<TableBrowser
_class={recruit.class.Vacancy}
config={[
{
key: '',
presenter: recruit.component.VacancyItemPresenter,
label: recruit.string.Vacancy,
sortingKey: 'name',
props: { action }
},
{
key: '',
presenter: recruit.component.VacancyCountPresenter,
label: recruit.string.Applications,
props: { applications },
sortingKey: '@applications',
sortingFunction: applicationSorting
},
'$lookup.company',
'location',
'description',
{
key: '',
presenter: recruit.component.VacancyModifiedPresenter,
label: core.string.Modified,
props: { applications },
sortingKey: 'modifiedOn',
sortingFunction: modifiedSorting
}
]}
query={{
...resultQuery,
archived: false
}}
showNotification
/>
{#if descr}
{#if loading}
<Loading />
{:else}
<TableBrowser
_class={recruit.class.Vacancy}
config={createConfig(descr, preference)}
options={descr.options}
query={{
...resultQuery,
archived: false
}}
bind:filters
showNotification
/>
{/if}
{/if}

View File

@ -15,23 +15,54 @@
-->
<script lang="ts">
import type { Vacancy } from '@anticrm/recruit'
import { Icon } from '@anticrm/ui'
import { getPanelURI } from '@anticrm/ui/src/panelup'
import {
ActionIcon,
getCurrentLocation,
Icon,
IconEdit,
Location,
locationToUrl,
navigate,
showPanel
} from '@anticrm/ui'
import recruit from '../plugin'
export let value: Vacancy
export let inline: boolean = false
function editVacancy (): void {
showPanel(recruit.component.EditVacancy, value._id, value._class, 'content')
}
function getLoc (): Location {
const loc = getCurrentLocation()
loc.path[2] = value._id
loc.path.length = 3
return loc
}
function getLink (): string {
const loc = getLoc()
return document.location.origin + locationToUrl(loc)
}
</script>
{#if value}
<a
class="flex-presenter"
class:inline-presenter={inline}
href="#{getPanelURI(recruit.component.EditVacancy, value._id, value._class, 'content')}"
>
<div class="flex-presenter" class:inline-presenter={inline}>
<div class="icon">
<Icon icon={recruit.icon.Vacancy} size={'small'} />
</div>
<span class="label">{value.name}</span>
</a>
<a
on:click|preventDefault={(e) => {
navigate(getLoc())
e.preventDefault()
}}
href={getLink()}
>
<span class="label">{value.name}</span>
</a>
<div class="action">
<ActionIcon label={recruit.string.Edit} size={'small'} icon={IconEdit} action={editVacancy} />
</div>
</div>
{/if}

View File

@ -43,7 +43,7 @@
const client = getClient()
const hierarchy = client.getHierarchy()
$: lookup = buildConfigLookup(hierarchy, _class, config)
$: lookup = options?.lookup ?? buildConfigLookup(hierarchy, _class, config)
let sortKey = 'modifiedOn'
let sortOrder = SortingOrder.Descending

View File

@ -102,27 +102,26 @@
const allAttributes = hierarchy.getAllAttributes(viewlet.attachTo)
for (const [, attribute] of allAttributes) {
if (attribute.hidden === true || attribute.label === undefined) continue
if (viewlet.hiddenKeys?.includes(attribute.name)) continue
if (hierarchy.isDerived(attribute.type._class, core.class.Collection)) continue
const value = getValue(attribute.name, attribute.type)
if (result.findIndex((p) => p.value === value) !== -1) continue
if (hierarchy.isDerived(attribute.type._class, core.class.Collection)) continue
if (viewlet.hiddenKeys?.includes(value)) continue
if (attribute.hidden !== true && attribute.label !== undefined) {
const typeClassId = getAttributePresenterClass(attribute)
const typeClass = hierarchy.getClass(typeClassId)
let presenter = hierarchy.as(typeClass, view.mixin.AttributePresenter).presenter
let parent = typeClass.extends
while (presenter === undefined && parent !== undefined) {
const pclazz = hierarchy.getClass(parent)
presenter = hierarchy.as(pclazz, view.mixin.AttributePresenter).presenter
parent = pclazz.extends
}
if (presenter === undefined) continue
result.push({
value,
label: attribute.label,
enabled: false
})
const typeClassId = getAttributePresenterClass(attribute)
const typeClass = hierarchy.getClass(typeClassId)
let presenter = hierarchy.as(typeClass, view.mixin.AttributePresenter).presenter
let parent = typeClass.extends
while (presenter === undefined && parent !== undefined) {
const pclazz = hierarchy.getClass(parent)
presenter = hierarchy.as(pclazz, view.mixin.AttributePresenter).presenter
parent = pclazz.extends
}
if (presenter === undefined) continue
result.push({
value,
label: attribute.label,
enabled: false
})
}
return preference === undefined ? result : setStatus(result, preference)

View File

@ -0,0 +1,34 @@
<!--
// Copyright © 2022 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 { Button, showPopup, Tooltip } from '@anticrm/ui'
import { Viewlet } from '@anticrm/view'
import ViewletSetting from './ViewletSetting.svelte'
import view from '../plugin'
export let viewlet: Viewlet | undefined
</script>
{#if viewlet}
<Tooltip label={view.string.CustomizeView}>
<Button
icon={view.icon.Setting}
kind={'transparent'}
on:click={() => {
showPopup(ViewletSetting, { viewlet })
}}
/>
</Tooltip>
{/if}

View File

@ -16,40 +16,40 @@
import { Resources } from '@anticrm/platform'
import { getEventPopupPositionElement, PopupAlignment } from '@anticrm/ui'
import { actionImpl } from './actionImpl'
import ActionsPopup from './components/ActionsPopup.svelte'
import BooleanEditor from './components/BooleanEditor.svelte'
import BooleanPresenter from './components/BooleanPresenter.svelte'
import BooleanTruePresenter from './components/BooleanTruePresenter.svelte'
import ClassAttributeBar from './components/ClassAttributeBar.svelte'
import ClassPresenter from './components/ClassPresenter.svelte'
import ColorsPopup from './components/ColorsPopup.svelte'
import DateEditor from './components/DateEditor.svelte'
import DatePresenter from './components/DatePresenter.svelte'
import SpacePresenter from './components/SpacePresenter.svelte'
import StringEditor from './components/StringEditor.svelte'
import StringPresenter from './components/StringPresenter.svelte'
import DocAttributeBar from './components/DocAttributeBar.svelte'
import EditBoxPopup from './components/EditBoxPopup.svelte'
import EditDoc from './components/EditDoc.svelte'
import EnumEditor from './components/EnumEditor.svelte'
import FilterBar from './components/filter/FilterBar.svelte'
import ObjectFilter from './components/filter/ObjectFilter.svelte'
import TimestampFilter from './components/filter/TimestampFilter.svelte'
import ValueFilter from './components/filter/ValueFilter.svelte'
import HTMLPresenter from './components/HTMLPresenter.svelte'
import IntlStringPresenter from './components/IntlStringPresenter.svelte'
import GithubPresenter from './components/linkPresenters/GithubPresenter.svelte'
import YoutubePresenter from './components/linkPresenters/YoutubePresenter.svelte'
import Menu from './components/Menu.svelte'
import NumberEditor from './components/NumberEditor.svelte'
import NumberPresenter from './components/NumberPresenter.svelte'
import ObjectPresenter from './components/ObjectPresenter.svelte'
import RolePresenter from './components/RolePresenter.svelte'
import SpacePresenter from './components/SpacePresenter.svelte'
import StringEditor from './components/StringEditor.svelte'
import StringPresenter from './components/StringPresenter.svelte'
import Table from './components/Table.svelte'
import TableBrowser from './components/TableBrowser.svelte'
import TimestampPresenter from './components/TimestampPresenter.svelte'
import UpDownNavigator from './components/UpDownNavigator.svelte'
import GithubPresenter from './components/linkPresenters/GithubPresenter.svelte'
import YoutubePresenter from './components/linkPresenters/YoutubePresenter.svelte'
import ActionsPopup from './components/ActionsPopup.svelte'
import DocAttributeBar from './components/DocAttributeBar.svelte'
import ViewletSetting from './components/ViewletSetting.svelte'
import TableBrowser from './components/TableBrowser.svelte'
import ValueFilter from './components/filter/ValueFilter.svelte'
import ObjectFilter from './components/filter/ObjectFilter.svelte'
import TimestampFilter from './components/filter/TimestampFilter.svelte'
import ClassPresenter from './components/ClassPresenter.svelte'
import FilterBar from './components/filter/FilterBar.svelte'
import EditBoxPopup from './components/EditBoxPopup.svelte'
import BooleanTruePresenter from './components/BooleanTruePresenter.svelte'
import EnumEditor from './components/EnumEditor.svelte'
import ClassAttributeBar from './components/ClassAttributeBar.svelte'
import ViewletSettingButton from './components/ViewletSettingButton.svelte'
function PositionElementAlignment (e?: Event): PopupAlignment | undefined {
return getEventPopupPositionElement(e)
@ -58,10 +58,10 @@ function PositionElementAlignment (e?: Event): PopupAlignment | undefined {
export { getActions, invokeAction } from './actions'
export { default as ActionContext } from './components/ActionContext.svelte'
export { default as ActionHandler } from './components/ActionHandler.svelte'
export { default as FilterButton } from './components/filter/FilterButton.svelte'
export { default as LinkPresenter } from './components/LinkPresenter.svelte'
export { default as ContextMenu } from './components/Menu.svelte'
export { default as TableBrowser } from './components/TableBrowser.svelte'
export { default as LinkPresenter } from './components/LinkPresenter.svelte'
export { default as FilterButton } from './components/filter/FilterButton.svelte'
export * from './context'
export * from './selection'
export { buildModel, getCollectionCounter, getObjectPresenter, LoadingProps } from './utils'
@ -75,7 +75,7 @@ export {
Menu,
SpacePresenter,
UpDownNavigator,
ViewletSetting,
ViewletSettingButton,
FilterBar,
ClassAttributeBar
}
@ -88,7 +88,6 @@ export default async (): Promise<Resources> => ({
ValueFilter,
TimestampFilter,
TableBrowser,
ViewletSetting,
SpacePresenter,
StringEditor,
StringPresenter,

View File

@ -48,16 +48,20 @@ export interface LoadingProps {
export async function getObjectPresenter (
client: Client,
_class: Ref<Class<Obj>>,
preserveKey: BuildModelKey
preserveKey: BuildModelKey,
isCollectionAttr: boolean = false
): Promise<AttributeModel> {
const clazz = client.getHierarchy().getClass(_class)
const presenterMixin = client.getHierarchy().as(clazz, view.mixin.AttributePresenter)
const hierarchy = client.getHierarchy()
const mixin = isCollectionAttr ? view.mixin.CollectionPresenter : view.mixin.AttributePresenter
const clazz = hierarchy.getClass(_class)
let mixinClazz = hierarchy.getClass(_class)
let presenterMixin = hierarchy.as(clazz, mixin)
while (presenterMixin.presenter === undefined && mixinClazz.extends !== undefined) {
presenterMixin = hierarchy.as(mixinClazz, mixin)
mixinClazz = hierarchy.getClass(mixinClazz.extends)
}
if (presenterMixin.presenter === undefined) {
if (clazz.extends !== undefined) {
return await getObjectPresenter(client, clazz.extends, preserveKey)
} else {
throw new Error('object presenter not found for ' + JSON.stringify(preserveKey))
}
throw new Error('object presenter not found for ' + JSON.stringify(preserveKey))
}
const presenter = await getResource(presenterMixin.presenter)
const key = preserveKey.sortingKey ?? preserveKey.key
@ -132,7 +136,8 @@ export async function getPresenter<T extends Doc> (
_class: Ref<Class<T>>,
key: BuildModelKey,
preserveKey: BuildModelKey,
lookup?: Lookup<T>
lookup?: Lookup<T>,
isCollectionAttr: boolean = false
): Promise<AttributeModel> {
if (key.presenter !== undefined) {
const { presenter, label, sortingKey } = key
@ -146,7 +151,7 @@ export async function getPresenter<T extends Doc> (
}
}
if (key.key.length === 0) {
return await getObjectPresenter(client, _class, preserveKey)
return await getObjectPresenter(client, _class, preserveKey, isCollectionAttr)
} else {
if (key.key.startsWith('$lookup')) {
if (lookup === undefined) {
@ -262,7 +267,7 @@ async function getLookupPresenter<T extends Doc> (
const lookupClass = getLookupClass(key.key, lookup, _class)
const lookupProperty = getLookupProperty(key.key)
const lookupKey = { ...key, key: lookupProperty[0] }
const model = await getPresenter(client, lookupClass[0], lookupKey, preserveKey)
const model = await getPresenter(client, lookupClass[0], lookupKey, preserveKey, undefined, lookupClass[2])
model.label = getLookupLabel(client, lookupClass[1], lookupClass[0], lookupKey, lookupProperty[1])
return model
}
@ -292,7 +297,7 @@ export function getLookupClass<T extends Doc> (
key: string,
lookup: Lookup<T>,
parent: Ref<Class<T>>
): [Ref<Class<Doc>>, Ref<Class<Doc>>] {
): [Ref<Class<Doc>>, Ref<Class<Doc>>, boolean] {
const _class = getLookup(key, lookup, parent)
if (_class === undefined) {
throw new Error('lookup class does not provided for ' + key)
@ -313,7 +318,7 @@ function getLookup (
key: string,
lookup: Lookup<any>,
parent: Ref<Class<Doc>>
): [Ref<Class<Doc>>, Ref<Class<Doc>>] | undefined {
): [Ref<Class<Doc>>, Ref<Class<Doc>>, boolean] | undefined {
const parts = key.split('$lookup.').filter((p) => p.length > 0)
const currentKey = parts[0].split('.').filter((p) => p.length > 0)[0]
const current = (lookup as any)[currentKey]
@ -325,13 +330,17 @@ function getLookup (
return getLookup(nestedKey, current[1], current[0])
}
if (Array.isArray(current)) {
return [current[0], parent]
return [current[0], parent, false]
}
if (current === undefined && lookup._id !== undefined) {
const reverse = (lookup._id as any)[currentKey]
return reverse !== undefined ? [reverse, parent] : undefined
return reverse !== undefined
? Array.isArray(reverse)
? [reverse[0], parent, true]
: [reverse, parent, true]
: undefined
}
return current !== undefined ? [current, parent] : undefined
return current !== undefined ? [current, parent, false] : undefined
}
export function getBooleanLabel (value: boolean | undefined): IntlString {

View File

@ -391,7 +391,6 @@ const view = plugin(viewId, {
component: {
ObjectPresenter: '' as AnyComponent,
EditDoc: '' as AnyComponent,
ViewletSetting: '' as AnyComponent,
SpacePresenter: '' as AnyComponent,
BooleanTruePresenter: '' as AnyComponent
},

View File

@ -17,14 +17,14 @@
import core, { WithLookup } from '@anticrm/core'
import { IntlString } from '@anticrm/platform'
import presentation, { createQuery, getClient } from '@anticrm/presentation'
import { AnyComponent, showPanel, Button, Icon, SearchEdit, showPopup, Tooltip, IconAdd } from '@anticrm/ui'
import { AnyComponent, Button, Icon, IconAdd, SearchEdit, showPanel, showPopup, Tooltip } from '@anticrm/ui'
import type { Filter } from '@anticrm/view'
import view, { Viewlet } from '@anticrm/view'
import { ViewletSetting } from '@anticrm/view-resources'
import { ViewletSettingButton } from '@anticrm/view-resources'
import { createEventDispatcher } from 'svelte'
import plugin from '../plugin'
import { classIcon } from '../utils'
import Header from './Header.svelte'
import type { Filter } from '@anticrm/view'
export let spaceId: Ref<Space> | undefined
export let createItemDialog: AnyComponent | undefined
@ -115,16 +115,6 @@
{#if createItemDialog}
<Button icon={IconAdd} label={createItemLabel} kind={'primary'} on:click={(ev) => showCreateDialog(ev)} />
{/if}
{#if viewlet}
<Tooltip label={view.string.CustomizeView}>
<Button
icon={view.icon.Setting}
kind={'transparent'}
on:click={() => {
showPopup(ViewletSetting, { viewlet })
}}
/>
</Tooltip>
{/if}
<ViewletSettingButton {viewlet} />
{/if}
</div>