mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-27 01:13:27 +03:00
Filter fixes (#1832)
This commit is contained in:
parent
9fb00d4db2
commit
2591bd7781
@ -279,6 +279,10 @@ export function createModel (builder: Builder): void {
|
||||
actions: [view.action.Delete]
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Contact, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: ['city', 'modifiedOn']
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
presentation.class.ObjectSearchCategory,
|
||||
core.space.Model,
|
||||
|
@ -37,6 +37,23 @@ async function createSpace (tx: TxOperations): Promise<void> {
|
||||
contact.space.Employee
|
||||
)
|
||||
}
|
||||
const contacts = await tx.findOne(core.class.Space, {
|
||||
_id: contact.space.Contacts
|
||||
})
|
||||
if (contacts === undefined) {
|
||||
await tx.createDoc(
|
||||
core.class.Space,
|
||||
core.space.Space,
|
||||
{
|
||||
name: 'Contacts',
|
||||
description: 'Contacts',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
},
|
||||
contact.space.Contacts
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const contactOperation: MigrateOperation = {
|
||||
|
@ -18,10 +18,10 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Ref } from '@anticrm/core'
|
||||
import { Class, Doc, FindResult, Ref } from '@anticrm/core'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import presentation, { createQuery, getClient, LiveQuery } from '@anticrm/presentation'
|
||||
import { Button, CheckBox, getPlatformColor } from '@anticrm/ui'
|
||||
import { Button, CheckBox, getPlatformColor, Loading } from '@anticrm/ui'
|
||||
import { Filter } from '@anticrm/view'
|
||||
import view from '@anticrm/view-resources/src/plugin'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
@ -105,9 +105,12 @@
|
||||
categories = res
|
||||
})
|
||||
|
||||
$: getValues(search)
|
||||
let objectsPromise: Promise<FindResult<TagElement>> | undefined
|
||||
|
||||
async function getValues (search: string): Promise<void> {
|
||||
if (objectsPromise) {
|
||||
await objectsPromise
|
||||
}
|
||||
const resultQuery =
|
||||
search !== ''
|
||||
? {
|
||||
@ -115,7 +118,9 @@
|
||||
targetClass: _class
|
||||
}
|
||||
: { targetClass: _class }
|
||||
objects = await client.findAll(tags.class.TagElement, resultQuery)
|
||||
objectsPromise = client.findAll(tags.class.TagElement, resultQuery)
|
||||
objects = await objectsPromise
|
||||
objectsPromise = undefined
|
||||
}
|
||||
|
||||
function checkMode () {
|
||||
@ -163,57 +168,70 @@
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
getValues(search)
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
<div class="header">
|
||||
<input bind:this={searchInput} type="text" bind:value={search} placeholder={phTraslate} />
|
||||
<input
|
||||
bind:this={searchInput}
|
||||
type="text"
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
getValues(search)
|
||||
}}
|
||||
placeholder={phTraslate}
|
||||
/>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#each categories as cat}
|
||||
{#if objects.filter((el) => el.category === cat._id).length > 0}
|
||||
<div class="sticky-wrapper">
|
||||
<button class="menu-group__header" class:show={search !== '' || show} on:click={toggleGroup}>
|
||||
<div class="flex-row-center">
|
||||
<span class="mr-1-5">{cat.label}</span>
|
||||
<div class="icon">
|
||||
<svg fill="var(--content-color)" viewBox="0 0 6 6" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0,0L6,3L0,6Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row-center text-xs">
|
||||
<span class="content-color mr-1">({objects.filter((el) => el.category === cat._id).length})</span>
|
||||
<span class="counter">{getCount(cat)}</span>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-group">
|
||||
{#each objects.filter((el) => el.category === cat._id) as element}
|
||||
<button
|
||||
class="menu-item"
|
||||
on:click={() => {
|
||||
checkSelected(element)
|
||||
}}
|
||||
>
|
||||
<div class="flex-between w-full">
|
||||
<div class="flex">
|
||||
<div class="check pointer-events-none">
|
||||
<CheckBox checked={isSelected(element)} primary />
|
||||
</div>
|
||||
<div class="tag" style="background-color: {getPlatformColor(element.color)};" />
|
||||
{element.title}
|
||||
</div>
|
||||
<div class="content-trans-color ml-2">
|
||||
{element.refCount ?? 0}
|
||||
</div>
|
||||
{#if objectsPromise}
|
||||
<Loading />
|
||||
{:else}
|
||||
{#each categories as cat}
|
||||
{#if objects.filter((el) => el.category === cat._id).length > 0}
|
||||
<div class="sticky-wrapper">
|
||||
<button class="menu-group__header" class:show={search !== '' || show} on:click={toggleGroup}>
|
||||
<div class="flex-row-center">
|
||||
<span class="mr-1-5">{cat.label}</span>
|
||||
<div class="icon">
|
||||
<svg fill="var(--content-color)" viewBox="0 0 6 6" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0,0L6,3L0,6Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex-row-center text-xs">
|
||||
<span class="content-color mr-1">({objects.filter((el) => el.category === cat._id).length})</span>
|
||||
<span class="counter">{getCount(cat)}</span>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-group">
|
||||
{#each objects.filter((el) => el.category === cat._id) as element}
|
||||
<button
|
||||
class="menu-item"
|
||||
on:click={() => {
|
||||
checkSelected(element)
|
||||
}}
|
||||
>
|
||||
<div class="flex-between w-full">
|
||||
<div class="flex">
|
||||
<div class="check pointer-events-none">
|
||||
<CheckBox checked={isSelected(element)} primary />
|
||||
</div>
|
||||
<div class="tag" style="background-color: {getPlatformColor(element.color)};" />
|
||||
{element.title}
|
||||
</div>
|
||||
<div class="content-trans-color ml-2">
|
||||
{element.refCount ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
|
@ -13,10 +13,10 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, DocumentQuery, getObjectValue, Ref, RefTo } from '@anticrm/core'
|
||||
import { Class, Doc, DocumentQuery, FindResult, getObjectValue, Ref, RefTo, SortingOrder } from '@anticrm/core'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import presentation, { getClient } from '@anticrm/presentation'
|
||||
import ui, { Button, CheckBox, Label } from '@anticrm/ui'
|
||||
import ui, { Button, CheckBox, Label, Loading } from '@anticrm/ui'
|
||||
import { Filter } from '@anticrm/view'
|
||||
import { onMount } from 'svelte'
|
||||
import { buildConfigLookup, getPresenter } from '../../utils'
|
||||
@ -59,11 +59,13 @@
|
||||
]
|
||||
|
||||
let values: (Doc | undefined | null)[] = []
|
||||
|
||||
$: getValues(search)
|
||||
let objectsPromise: Promise<FindResult<Doc>> | undefined
|
||||
|
||||
const targets = new Map<any, number>()
|
||||
async function getValues (search: string): Promise<void> {
|
||||
if (objectsPromise) {
|
||||
await objectsPromise
|
||||
}
|
||||
targets.clear()
|
||||
const baseObjects = await client.findAll(_class, query, { projection: { [filter.key.key]: 1 } })
|
||||
for (const object of baseObjects) {
|
||||
@ -81,10 +83,13 @@
|
||||
: {
|
||||
_id: { $in: Array.from(targets.keys()) }
|
||||
}
|
||||
values = await client.findAll(targetClass, resultQuery)
|
||||
const options = clazz.sortingKey !== undefined ? { sort: { [clazz.sortingKey]: SortingOrder.Ascending } } : {}
|
||||
objectsPromise = client.findAll(targetClass, resultQuery, options)
|
||||
values = await objectsPromise
|
||||
if (targets.has(undefined)) {
|
||||
values.unshift(undefined)
|
||||
}
|
||||
objectsPromise = undefined
|
||||
}
|
||||
|
||||
function isSelected (value: Doc | undefined | null, values: any[]): boolean {
|
||||
@ -122,39 +127,52 @@
|
||||
})
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
getValues(search)
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
<div class="header">
|
||||
<input bind:this={searchInput} type="text" bind:value={search} placeholder={phTraslate} />
|
||||
<input
|
||||
bind:this={searchInput}
|
||||
type="text"
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
getValues(search)
|
||||
}}
|
||||
placeholder={phTraslate}
|
||||
/>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#await promise then attribute}
|
||||
{#each values as value}
|
||||
<button
|
||||
class="menu-item"
|
||||
on:click={() => {
|
||||
toggle(value)
|
||||
}}
|
||||
>
|
||||
<div class="flex-between w-full">
|
||||
<div class="flex">
|
||||
<div class="check pointer-events-none">
|
||||
<CheckBox checked={isSelected(value, filter.value)} primary />
|
||||
{#if objectsPromise}
|
||||
<Loading />
|
||||
{:else}
|
||||
{#each values as value}
|
||||
<button
|
||||
class="menu-item"
|
||||
on:click={() => {
|
||||
toggle(value)
|
||||
}}
|
||||
>
|
||||
<div class="flex-between w-full">
|
||||
<div class="flex">
|
||||
<div class="check pointer-events-none">
|
||||
<CheckBox checked={isSelected(value, filter.value)} primary />
|
||||
</div>
|
||||
{#if value}
|
||||
<svelte:component this={attribute.presenter} {value} {...attribute.props} />
|
||||
{:else}
|
||||
<Label label={ui.string.NotSelected} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="content-trans-color ml-2">
|
||||
{targets.get(value?._id)}
|
||||
</div>
|
||||
{#if value}
|
||||
<svelte:component this={attribute.presenter} {value} {...attribute.props} />
|
||||
{:else}
|
||||
<Label label={ui.string.NotSelected} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="content-trans-color ml-2">
|
||||
{targets.get(value?._id)}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -13,6 +13,8 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { closeTooltip } from '@anticrm/ui'
|
||||
|
||||
import { Filter } from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import view from '../../plugin'
|
||||
@ -42,6 +44,7 @@
|
||||
filter.mode = filter.mode === undefined ? filter.modes[0] : filter.mode
|
||||
|
||||
function click (value: number): void {
|
||||
closeTooltip()
|
||||
filter.value = [value]
|
||||
onChange(filter)
|
||||
dispatch('close')
|
||||
|
@ -13,10 +13,10 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, DocumentQuery, getObjectValue, Ref } from '@anticrm/core'
|
||||
import { Class, Doc, DocumentQuery, FindResult, getObjectValue, Ref, SortingOrder } from '@anticrm/core'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import presentation, { getClient } from '@anticrm/presentation'
|
||||
import ui, { Button, CheckBox, Label } from '@anticrm/ui'
|
||||
import ui, { Button, CheckBox, Label, Loading } from '@anticrm/ui'
|
||||
import { Filter } from '@anticrm/view'
|
||||
import { onMount } from 'svelte'
|
||||
import { getPresenter } from '../../utils'
|
||||
@ -60,9 +60,12 @@
|
||||
let selectedValues: Set<any> = new Set<any>(filter.value.map((p) => p[0]))
|
||||
const realValues = new Map<any, Set<any>>()
|
||||
|
||||
$: getValues(search)
|
||||
let objectsPromise: Promise<FindResult<Doc>> | undefined
|
||||
|
||||
async function getValues (search: string): Promise<void> {
|
||||
if (objectsPromise) {
|
||||
await objectsPromise
|
||||
}
|
||||
values.clear()
|
||||
realValues.clear()
|
||||
const resultQuery =
|
||||
@ -79,15 +82,29 @@
|
||||
prefix = attr.attributeOf + '.'
|
||||
console.log('prefix', prefix)
|
||||
}
|
||||
const res = await client.findAll(_class, resultQuery, { projection: { [prefix + filter.key.key]: 1 } })
|
||||
objectsPromise = client.findAll(_class, resultQuery, {
|
||||
sort: { [filter.key.key]: SortingOrder.Ascending },
|
||||
projection: { [prefix + filter.key.key]: 1 }
|
||||
})
|
||||
const res = await objectsPromise
|
||||
|
||||
for (const object of res) {
|
||||
const realValue = getObjectValue(filter.key.key, object)
|
||||
const value = typeof realValue === 'string' ? realValue.trim().toUpperCase() : realValue ?? undefined
|
||||
const value = getValue(realValue)
|
||||
values.set(value, (values.get(value) ?? 0) + 1)
|
||||
realValues.set(value, (realValues.get(value) ?? new Set()).add(realValue))
|
||||
}
|
||||
values = values
|
||||
objectsPromise = undefined
|
||||
}
|
||||
|
||||
function getValue (obj: any): any {
|
||||
if (typeof obj === 'string') {
|
||||
const trim = obj.trim()
|
||||
return trim.length > 0 ? trim.toUpperCase() : undefined
|
||||
} else {
|
||||
return obj ?? undefined
|
||||
}
|
||||
}
|
||||
|
||||
function isSelected (value: any, values: Set<any>): boolean {
|
||||
@ -121,44 +138,57 @@
|
||||
onMount(() => {
|
||||
if (searchInput) searchInput.focus()
|
||||
})
|
||||
getValues(search)
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
<div class="header">
|
||||
<input bind:this={searchInput} type="text" bind:value={search} placeholder={phTraslate} />
|
||||
<input
|
||||
bind:this={searchInput}
|
||||
type="text"
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
getValues(search)
|
||||
}}
|
||||
placeholder={phTraslate}
|
||||
/>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#await promise then attribute}
|
||||
{#each Array.from(values.keys()) as value}
|
||||
{@const realValue = [...(realValues.get(value) ?? [])][0]}
|
||||
<button
|
||||
class="menu-item"
|
||||
on:click={() => {
|
||||
toggle(value)
|
||||
}}
|
||||
>
|
||||
<div class="flex-between w-full">
|
||||
<div class="flex">
|
||||
<div class="check pointer-events-none">
|
||||
<CheckBox checked={isSelected(value, selectedValues)} primary />
|
||||
{#if objectsPromise}
|
||||
<Loading />
|
||||
{:else}
|
||||
{#each Array.from(values.keys()) as value}
|
||||
{@const realValue = [...(realValues.get(value) ?? [])][0]}
|
||||
<button
|
||||
class="menu-item"
|
||||
on:click={() => {
|
||||
toggle(value)
|
||||
}}
|
||||
>
|
||||
<div class="flex-between w-full">
|
||||
<div class="flex">
|
||||
<div class="check pointer-events-none">
|
||||
<CheckBox checked={isSelected(value, selectedValues)} primary />
|
||||
</div>
|
||||
{#if value}
|
||||
<svelte:component
|
||||
this={attribute.presenter}
|
||||
value={typeof value === 'string' ? realValue : value}
|
||||
{...attribute.props}
|
||||
/>
|
||||
{:else}
|
||||
<Label label={ui.string.NotSelected} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="content-trans-color ml-2">
|
||||
{values.get(value)}
|
||||
</div>
|
||||
{#if value}
|
||||
<svelte:component
|
||||
this={attribute.presenter}
|
||||
value={typeof value === 'string' ? realValue : value}
|
||||
{...attribute.props}
|
||||
/>
|
||||
{:else}
|
||||
<Label label={ui.string.NotSelected} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="content-trans-color ml-2">
|
||||
{values.get(value)}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user