Added filters in Vacancies (#1879)

This commit is contained in:
Alexander Platov 2022-05-27 12:21:02 +03:00 committed by GitHub
parent bbcbb7722a
commit 2993925842
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 110 additions and 28 deletions

View File

@ -281,7 +281,7 @@
}
</script>
<div class="flex-col kanban-container">
<div class="flex-col kanban-container top-divider">
<div class="scrollable">
<ScrollBox>
<div class="kanban-content">

View File

@ -670,5 +670,6 @@ a.no-line {
.border-primary-button { border-color: var(--primary-button-border); }
.border-button-enabled { border: 1px solid var(--theme-button-border-enabled); }
.top-divider { border-top: 1px solid var(--divider-color); }
.bottom-divider { border-bottom: 1px solid var(--divider-color); }
.bottom-highlight-select { border-bottom: 1px solid var(--highlight-select); }

View File

@ -23,12 +23,14 @@
import Lost from './icons/Lost.svelte'
import Won from './icons/Won.svelte'
import StatesBar from './state/StatesBar.svelte'
import type { Filter } from '@anticrm/view'
export let _class: Ref<Class<Task>>
export let space: Ref<SpaceWithStates>
export let options: FindOptions<Task> | undefined
export let config: string[]
export let search: string
export let filters: Filter[] = []
let doneStatusesView: boolean = false
let state: Ref<State> | undefined = undefined
@ -104,7 +106,7 @@
$: updateQuery(search, selectedDoneStates)
</script>
<div class="flex-between mb-4 header">
<div class="flex-between mb-2 header">
<div class="flex-row-center buttons">
<div
class="button flex-center"
@ -173,7 +175,7 @@
</div>
</div>
<div class="statustableview-container">
<TableBrowser {_class} bind:query config={resConfig} {options} filters={[]} showNotification />
<TableBrowser {_class} bind:query config={resConfig} {options} bind:filters showNotification />
</div>
<style lang="scss">

View File

@ -33,7 +33,7 @@
// If defined, will show a number of dummy items before real data will appear.
export let loadingProps: LoadingProps | undefined = undefined
export let filters: Filter[] | undefined = undefined
export let filters: Filter[] = []
let resultQuery = query
@ -56,7 +56,7 @@
}}
/>
{#if filters}
{#if filters !== []}
<FilterBar {_class} {query} bind:filters on:change={(e) => (resultQuery = e.detail)} />
{/if}
<Scroller tableFade>

View File

@ -13,18 +13,39 @@
// limitations under the License.
-->
<script lang="ts">
import { Class, Doc, Ref } from '@anticrm/core'
import { translate } from '@anticrm/platform'
import { Class, Doc, Ref, RefTo } from '@anticrm/core'
import { eventToHTMLElement, IconClose, showPopup, Icon, Label } from '@anticrm/ui'
import { Filter } from '@anticrm/view'
import { createEventDispatcher } from 'svelte'
import view from '../../plugin'
import { getClient } from '@anticrm/presentation'
import task from '@anticrm/task'
import type { State } from '@anticrm/task'
export let _class: Ref<Class<Doc>>
export let filter: Filter
let current = 0
let current = 0
const client = getClient()
const hierarchy = client.getHierarchy()
const targetClass = (hierarchy.getAttribute(_class, filter.key.key).type as RefTo<Doc>).to
$: isState = targetClass === task.class.State ?? false
const dispatch = createEventDispatcher()
async function getCountStates (ids: Ref<Doc>[]): Promise<number> {
const selectStates = await client.findAll(targetClass, { _id: { $in: Array.from(ids) } }, {})
const unique = new Set(selectStates.map((s) => (s as State).title))
return unique.size
}
let countLabel: string = ''
async function getLabel (): Promise<void> {
const count = isState ? await getCountStates(filter.value) : filter.value.length
countLabel = await translate(view.string.FilterStatesCount, { value: count })
}
$: if (filter) getLabel()
function toggle () {
const modes = filter.modes.filter((p) => p.isAvailable(filter.value))
current++
@ -64,9 +85,7 @@
)
}}
>
<span>
<Label label={view.string.FilterStatesCount} params={{ value: filter.value.length }} />
</span>
<span>{countLabel}</span>
</button>
<button
class="filter-button right-round"

View File

@ -122,14 +122,13 @@
}
</script>
<div class="antiPopup">
<div class="ap-space" />
<div class="ap-scroll">
<div class="ap-box">
<div class="selectPopup">
<div class="scroll">
<div class="box">
{#each getTypes(_class) as type, i}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<button
class="ap-menuItem flex-row-center withIcon"
class="menu-item withIcon"
on:keydown={(event) => keyDown(event, i)}
on:mouseover={(event) => {
event.currentTarget.focus()
@ -146,7 +145,6 @@
{/each}
</div>
</div>
<div class="ap-space" />
</div>
<style lang="scss">

View File

@ -22,6 +22,8 @@
import { buildConfigLookup, getPresenter } from '../../utils'
import view from '../../plugin'
import { createEventDispatcher } from 'svelte'
import task from '@anticrm/task'
import type { State } from '@anticrm/task'
export let _class: Ref<Class<Doc>>
export let filter: Filter
@ -62,6 +64,28 @@
const targetClass = (hierarchy.getAttribute(_class, filter.key.key).type as RefTo<Doc>).to
const clazz = hierarchy.getClass(targetClass)
const targets = new Map<any, number>()
$: isState = clazz._id === task.class.State ?? false
let statesCount: number[] = []
let states: State[]
const groupValues = (val: State[]): (Doc | undefined | null)[] => {
states = val
const result: Doc[] = []
statesCount = []
const unique = [...new Set(val.map((v) => v.title))]
unique.forEach((label, i) => {
let count = 0
states.forEach((state) => {
if (state.title === label) {
if (!count) result[i] = state
count += targets.get(state._id) ?? 0
}
})
statesCount[i] = count
})
return result
}
async function getValues (search: string): Promise<void> {
if (objectsPromise) {
await objectsPromise
@ -88,6 +112,7 @@
if (targets.has(undefined)) {
values.unshift(undefined)
}
if (isState) values = groupValues(values as State[])
objectsPromise = undefined
}
@ -103,10 +128,20 @@
function toggle (value: Doc | undefined | null): void {
if (isSelected(value, filter.value)) {
filter.value = filter.value.filter((p) => (value ? p !== value._id : p != null))
if (isState) {
const ids = states.filter((state) => state.title === (value as State).title).map((s) => s._id)
filter.value = filter.value.filter((p) => !ids.includes(p))
} else filter.value = filter.value.filter((p) => (value ? p !== value._id : p != null))
} else {
if (value) {
filter.value = [...filter.value, value._id]
if (isState) {
filter.value = [
...filter.value,
...states
.filter((state) => state.title === states.filter((s) => s._id === value._id)[0].title)
.map((state) => state._id)
]
} else filter.value = [...filter.value, value._id]
} else {
filter.value = [...filter.value, undefined]
}
@ -149,7 +184,7 @@
{#if objectsPromise}
<Loading />
{:else}
{#each values as value}
{#each values as value, i}
<button
class="menu-item"
on:click={() => {
@ -168,7 +203,7 @@
{/if}
</div>
<div class="content-trans-color ml-2">
{targets.get(value?._id)}
{#if isState}{statesCount[i]}{:else}{targets.get(value?._id)}{/if}
</div>
</div>
</button>
@ -178,7 +213,7 @@
</div>
</div>
<Button
kind={'no-border'}
shape={'round'}
label={view.string.Apply}
on:click={() => {
onChange(filter)

View File

@ -13,18 +13,26 @@
// limitations under the License.
-->
<script lang="ts">
import type { Ref, Class, Doc } from '@anticrm/core'
import type { Asset } from '@anticrm/platform'
import { Icon } from '@anticrm/ui'
import { FilterButton } from '@anticrm/view-resources'
import type { Filter } from '@anticrm/view'
export let icon: Asset | undefined
export let label: string
export let description: string | undefined
export let _class: Ref<Class<Doc>> | undefined = undefined
export let filters: Filter[] = []
</script>
<div class="ac-header__wrap-description">
<div class="ac-header__wrap-title" on:click>
{#if icon}<div class="ac-header__icon"><Icon {icon} size={'small'} /></div>{/if}
<span class="ac-header__title">{label}</span>
<div class="flex-row-center clear-mins">
<div class="ac-header__wrap-title" on:click>
{#if icon}<div class="ac-header__icon"><Icon {icon} size={'small'} /></div>{/if}
<span class="ac-header__title">{label}</span>
</div>
{#if _class}<div class="ml-4"><FilterButton {_class} bind:filters /></div>{/if}
</div>
{#if description}<span class="ac-header__description">{description}</span>{/if}
</div>

View File

@ -18,11 +18,13 @@
import { createQuery } from '@anticrm/presentation'
import { Component, Loading } from '@anticrm/ui'
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
import type { Filter } from '@anticrm/view'
export let _class: Ref<Class<Doc>>
export let space: Ref<Space>
export let search: string
export let viewlet: WithLookup<Viewlet> | undefined
export let filters: Filter[] = []
const preferenceQuery = createQuery()
let preference: ViewletPreference | undefined
@ -56,6 +58,7 @@
options: viewlet.options,
config: preference?.config ?? viewlet.config,
viewlet,
filters,
search
}}
/>

View File

@ -34,6 +34,7 @@
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
@ -41,6 +42,8 @@
export let search: string
export let viewlet: WithLookup<Viewlet> | undefined
export let viewlets: WithLookup<Viewlet>[] = []
export let _class: Ref<Class<Doc>> | undefined = undefined
export let filters: Filter[] = []
const client = getClient()
const hierarchy = client.getHierarchy()
@ -84,12 +87,14 @@
}
</script>
<div class="ac-header divide full">
<div class="ac-header full">
{#if space}
<Header
icon={classIcon(client, space._class)}
label={space.name}
description={space.description}
{_class}
bind:filters
on:click={onSpaceEdit}
/>
{#if viewlets.length > 1}

View File

@ -21,6 +21,7 @@
import type { ViewConfiguration } from '@anticrm/workbench'
import SpaceContent from './SpaceContent.svelte'
import SpaceHeader from './SpaceHeader.svelte'
import type { Filter } from '@anticrm/view'
export let currentSpace: Ref<Space> | undefined
export let currentView: ViewConfiguration | undefined
@ -32,6 +33,7 @@
let space: Space | undefined
let _class: Ref<Class<Doc>> | undefined = undefined
let header: AnyComponent | undefined
let filters: Filter[] = []
const client = getClient()
@ -87,7 +89,16 @@
on:change={setViewlet}
/>
{:else}
<SpaceHeader spaceId={space._id} {viewlets} {createItemDialog} {createItemLabel} bind:search bind:viewlet />
<SpaceHeader
spaceId={space._id}
{_class}
{viewlets}
{createItemDialog}
{createItemLabel}
bind:filters
bind:search
bind:viewlet
/>
{/if}
<SpaceContent space={space._id} {_class} bind:search {viewlet} />
<SpaceContent space={space._id} {_class} bind:filters bind:search {viewlet} />
{/if}

View File

@ -335,7 +335,7 @@
</svg>
<div class="workbench-container">
<div class="antiPanel-application">
<div class="flex-col mt-1">
<div class="flex-col flex-no-shrink mt-1">
<!-- <ActivityStatus status="active" /> -->
<AppItem
icon={TopMenu}