mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 02:51:54 +03:00
Viewlet setting fixes (#1751)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
dec594d969
commit
ba17bed87e
@ -173,7 +173,8 @@ export function createModel (builder: Builder): void {
|
||||
'modifiedOn',
|
||||
{ key: '', presenter: view.component.RolePresenter, label: view.string.Role },
|
||||
'$lookup.channels'
|
||||
]
|
||||
],
|
||||
hiddenKeys: ['name']
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Person, core.class.Class, view.mixin.ObjectEditor, {
|
||||
|
@ -127,7 +127,8 @@ export function createModel (builder: Builder): void {
|
||||
{ key: 'leads', presenter: lead.component.LeadsPresenter, label: lead.string.Leads },
|
||||
'modifiedOn',
|
||||
'$lookup.channels'
|
||||
]
|
||||
],
|
||||
hiddenKeys: ['name']
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
|
@ -271,7 +271,8 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
'modifiedOn',
|
||||
'$lookup.channels'
|
||||
]
|
||||
],
|
||||
hiddenKeys: ['name']
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
|
@ -139,6 +139,7 @@ export class TViewlet extends TDoc implements Viewlet {
|
||||
descriptor!: Ref<ViewletDescriptor>
|
||||
open!: AnyComponent
|
||||
config!: (BuildModelKey | string)[]
|
||||
hiddenKeys?: string[]
|
||||
}
|
||||
|
||||
@Model(view.class.Action, core.class.Doc, DOMAIN_MODEL)
|
||||
|
@ -38,7 +38,7 @@ export function resultSort<T extends Doc> (result: T[], sortOptions: SortingQuer
|
||||
for (const key in sortOptions) {
|
||||
const aValue = getValue(key, a)
|
||||
const bValue = getValue(key, b)
|
||||
const result = getSortingResult(aValue, bValue, sortOptions[key] as SortingOrder)
|
||||
const result = getSortingResult(aValue, bValue, sortOptions[key])
|
||||
if (result !== 0) return result
|
||||
}
|
||||
return 0
|
||||
|
@ -121,6 +121,10 @@ export type FindOptions<T extends Doc> = {
|
||||
*/
|
||||
export type SortingQuery<T extends Doc> = {
|
||||
[P in keyof T]?: SortingOrder
|
||||
} & {
|
||||
// support nested queries e.g. 'user.friends.name'
|
||||
// this will mark all unrecognized properties as any (including nested queries)
|
||||
[key: string]: SortingOrder
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,17 +75,6 @@
|
||||
<span class="ac-header__title"><Label label={contact.string.Contacts} /></span>
|
||||
</div>
|
||||
|
||||
{#if viewlet}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
@ -98,6 +87,16 @@
|
||||
kind={'primary'}
|
||||
on:click={(ev) => showCreateDialog(ev)}
|
||||
/>
|
||||
{#if viewlet}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if viewlet}
|
||||
|
@ -22,11 +22,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={container} class="flex container">
|
||||
<div bind:this={container} class="flex-center container">
|
||||
<div class="pr-2 over-underline">
|
||||
<PersonPresenter {value} {onEdit} {shouldShowAvatar} />
|
||||
</div>
|
||||
<div class="status">
|
||||
<div class="status content-color">
|
||||
<EmployeeStatusPresenter employeeId={value._id} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Employee, formatName, Status } from '@anticrm/contact'
|
||||
import { Ref, Space, WithLookup } from '@anticrm/core'
|
||||
import { Employee, EmployeeAccount, formatName, Status } from '@anticrm/contact'
|
||||
import { getCurrentAccount, Ref, Space, WithLookup } from '@anticrm/core'
|
||||
import { Avatar, createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Button, Label, showPopup } from '@anticrm/ui'
|
||||
import EmployeeSetStatusPopup from './EmployeeSetStatusPopup.svelte'
|
||||
@ -13,6 +13,8 @@
|
||||
export let space: Ref<Space>
|
||||
|
||||
const client = getClient()
|
||||
const me = (getCurrentAccount() as EmployeeAccount).employee
|
||||
$: editable = employeeId === me
|
||||
|
||||
const stattusQuery = createQuery()
|
||||
let status: WithLookup<Status>
|
||||
@ -58,23 +60,25 @@
|
||||
<Avatar size="x-large" avatar={employee?.avatar} />
|
||||
</div>
|
||||
<div class="pb-2">{formatName(employee?.name ?? '')}</div>
|
||||
<div class="pb-2">
|
||||
<Label label={contact.string.Status} />
|
||||
{#if status}
|
||||
{#if status}
|
||||
<div class="pb-2">
|
||||
<Label label={contact.string.Status} />
|
||||
<div class="flex-row-stretch statusContainer">
|
||||
<div class="pr-2">
|
||||
<EmployeeStatusPresenter {employeeId} withTooltip={false} />
|
||||
</div>
|
||||
<div class="setStatusButton">
|
||||
<Button icon={Edit} title={contact.string.SetStatus} on:click={onEdit} />
|
||||
</div>
|
||||
{#if editable}
|
||||
<div class="setStatusButton">
|
||||
<Button icon={Edit} title={contact.string.SetStatus} on:click={onEdit} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex-row-stretch over-underline" on:click={onEdit}>
|
||||
<Label label={contact.string.SetStatus} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if editable}
|
||||
<div class="flex-row-stretch over-underline pb-2" on:click={onEdit}>
|
||||
<Label label={contact.string.SetStatus} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -75,17 +75,6 @@
|
||||
<span class="ac-header__title"><Label label={inventory.string.Products} /></span>
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet: descr })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<EditWithIcon
|
||||
icon={IconSearch}
|
||||
placeholder={ui.string.Search}
|
||||
@ -100,6 +89,16 @@
|
||||
kind={'primary'}
|
||||
on:click={(ev) => showCreateDialog(ev)}
|
||||
/>
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet: descr })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
|
@ -64,10 +64,15 @@
|
||||
<span class="ac-header__title"><Label label={lead.string.Customers} /></span>
|
||||
</div>
|
||||
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
updateResultQuery(search)
|
||||
}}
|
||||
/>
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
@ -75,12 +80,6 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
updateResultQuery(search)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
|
@ -73,17 +73,6 @@
|
||||
<span class="ac-header__title"><Label label={recruit.string.Applications} /></span>
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet: descr })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
@ -91,6 +80,16 @@
|
||||
}}
|
||||
/>
|
||||
<Button icon={IconAdd} label={recruit.string.ApplicationCreateLabel} kind={'primary'} on:click={showCreateDialog} />
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet: descr })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
|
@ -87,10 +87,15 @@
|
||||
<span class="ac-header__title"><Label label={recruit.string.Candidates} /></span>
|
||||
</div>
|
||||
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
updateResultQuery(search, documentIds)
|
||||
}}
|
||||
/>
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
@ -98,12 +103,6 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
updateResultQuery(search, documentIds)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Component
|
||||
|
@ -20,7 +20,7 @@
|
||||
"MoveRight": "Move right",
|
||||
"MoveUp": "Move up",
|
||||
"MoveDown": "Move down",
|
||||
|
||||
"RestoreDefaults": "Restore defaults",
|
||||
"SelectItem": "Select focused item",
|
||||
"SelectItemAll": "Select all items",
|
||||
"SelectItemNone": "Deselect all items",
|
||||
|
@ -34,6 +34,7 @@
|
||||
"SelectItemNone": "Снять все выделения",
|
||||
"ShowPreview": "Предпросмотр документа",
|
||||
"ShowActions": "Показать действия",
|
||||
"RestoreDefaults": "По умолчанию",
|
||||
"CustomizeView": "Настроить отображение"
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
import presentation, { Card, createQuery, getAttributePresenterClass, getClient } from '@anticrm/presentation'
|
||||
import { BuildModelKey, Viewlet, ViewletPreference } from '@anticrm/view'
|
||||
import core, { ArrOf, Class, Doc, Lookup, Ref, Type } from '@anticrm/core'
|
||||
import { Grid, MiniToggle } from '@anticrm/ui'
|
||||
import { Button, Grid, MiniToggle } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { buildConfigLookup, getLookupClass, getLookupLabel, getLookupProperty } from '../utils'
|
||||
@ -105,7 +105,7 @@
|
||||
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)
|
||||
@ -142,6 +142,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function restoreDefault (): Promise<void> {
|
||||
attributes = getConfig(viewlet, undefined)
|
||||
}
|
||||
|
||||
function getKeyLabel<T extends Doc> (_class: Ref<Class<T>>, key: string, lookup: Lookup<T> | undefined): IntlString {
|
||||
if (key.startsWith('$lookup') && lookup !== undefined) {
|
||||
const lookupClass = getLookupClass(key, lookup, _class)
|
||||
@ -176,4 +180,7 @@
|
||||
<MiniToggle label={attribute.label} bind:on={attribute.enabled} />
|
||||
{/each}
|
||||
</Grid>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button label={view.string.RestoreDefaults} on:click={() => restoreDefault()} />
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
@ -39,6 +39,7 @@ export default mergeIds(viewId, view, {
|
||||
Type: '' as IntlString,
|
||||
ActionPlaceholder: '' as IntlString,
|
||||
CreatingAttribute: '' as IntlString,
|
||||
RestoreDefaults: '' as IntlString,
|
||||
CreatingAttributeConfirm: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -105,6 +105,7 @@ export interface Viewlet extends Doc {
|
||||
descriptor: Ref<ViewletDescriptor>
|
||||
options?: FindOptions<Doc>
|
||||
config: (BuildModelKey | string)[]
|
||||
hiddenKeys?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,17 +111,6 @@
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if viewlet}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
@ -131,5 +120,15 @@
|
||||
{#if createItemDialog}
|
||||
<Button icon={IconAdd} label={createItemLabel} kind={'primary'} on:click={(ev) => showCreateDialog(ev)} />
|
||||
{/if}
|
||||
{#if viewlet}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -30,6 +30,7 @@ import core, {
|
||||
Ref,
|
||||
ReverseLookups,
|
||||
SortingOrder,
|
||||
SortingQuery,
|
||||
toFindResult,
|
||||
Tx,
|
||||
TxCreateDoc,
|
||||
@ -56,6 +57,14 @@ function isLookupQuery<T extends Doc> (query: DocumentQuery<T>): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
function isLookupSort<T extends Doc> (sort: SortingQuery<T> | undefined): boolean {
|
||||
if (sort === undefined) return false
|
||||
for (const key in sort) {
|
||||
if (key.includes('$lookup.')) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
interface LookupStep {
|
||||
from: string
|
||||
localField: string
|
||||
@ -212,7 +221,7 @@ abstract class MongoAdapterBase extends TxProcessor {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
targetObject.$lookup[key] = this.modelDb.getObject(targetObject[key])
|
||||
targetObject.$lookup[key] = (await this.modelDb.findAll(_class, { _id: targetObject[key] }))[0]
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,7 +290,7 @@ abstract class MongoAdapterBase extends TxProcessor {
|
||||
): Promise<FindResult<T>> {
|
||||
const pipeline = []
|
||||
const match = { $match: this.translateQuery(clazz, query) }
|
||||
const slowPipeline = isLookupQuery(query)
|
||||
const slowPipeline = isLookupQuery(query) || isLookupSort(options.sort)
|
||||
const steps = await this.getLookups(options.lookup)
|
||||
if (slowPipeline) {
|
||||
for (const step of steps) {
|
||||
@ -293,11 +302,28 @@ abstract class MongoAdapterBase extends TxProcessor {
|
||||
if (options.sort !== undefined) {
|
||||
const sort = {} as any
|
||||
for (const _key in options.sort) {
|
||||
// Check if key is belong to mixin class, we need to add prefix.
|
||||
const key = this.checkMixinKey<T>(_key, clazz)
|
||||
let key: string = _key
|
||||
const arr = key.split('.').filter((p) => p)
|
||||
key = ''
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const element = arr[i]
|
||||
if (element === '$lookup') {
|
||||
key += arr[++i] + '_lookup'
|
||||
} else {
|
||||
if (!key.endsWith('.') && i > 0) {
|
||||
key += '.'
|
||||
}
|
||||
key += arr[i]
|
||||
if (i !== arr.length - 1) {
|
||||
key += '.'
|
||||
}
|
||||
}
|
||||
// Check if key is belong to mixin class, we need to add prefix.
|
||||
key = this.checkMixinKey<T>(key, clazz)
|
||||
}
|
||||
sort[key] = options.sort[_key] === SortingOrder.Ascending ? 1 : -1
|
||||
}
|
||||
resultPipeline.push({ $sort: sort })
|
||||
pipeline.push({ $sort: sort })
|
||||
}
|
||||
if (options.limit !== undefined) {
|
||||
resultPipeline.push({ $limit: options.limit })
|
||||
|
Loading…
Reference in New Issue
Block a user