Filter fixes (#1832)

This commit is contained in:
Denis Bykhov 2022-05-23 07:42:07 +06:00 committed by GitHub
parent 9fb00d4db2
commit 2591bd7781
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 197 additions and 107 deletions

View File

@ -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,

View File

@ -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 = {

View File

@ -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

View File

@ -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>

View File

@ -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')

View File

@ -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>